0

为了说明这个问题,请参考 Microsoft Scripting Runtime 和下面的代码来编译一个 C# 项目。运行生成的可执行文件的单个实例。在我的 12 核机器上,读取循环始终需要大约 180 毫秒。启动另一个可执行文件实例会减慢速度,每个附加可执行文件大约 100 毫秒。

有什么想法吗?除了切换到不同的字典实现之外,还有什么解决方案?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Diagnostics.Stopwatch stp = new System.Diagnostics.Stopwatch();
            var dict = (Scripting.IDictionary)(new Scripting.Dictionary());
            stp.Start();
            for (int i = 1; i < 1000; ++i)
            {
                Object s = i.ToString();
                dict.Add(ref s, ref s);
            }
            Console.WriteLine("After Add {0}", stp.ElapsedMilliseconds);
            object q = null;
            for (int j = 0; j < 1000; ++j)
            {
                long old = stp.ElapsedMilliseconds;
                for (int i = 1; i < 10000; ++i)
                {
                    q = null;
                    object s = i.ToString() as object;
                    q = dict.get_Item(ref s);
                }
                long newval = stp.ElapsedMilliseconds;
                Console.WriteLine("After Retrieve {0}", newval - old);
            }

        }
    }
}
4

1 回答 1

0

Eric,您会很高兴听到该问题并非特定于 Scripting.Dictionary,而是与 Apartment 线程模型有关。可以阅读MSDN上混合线程模型对性能的影响,但(对我来说)令人惊讶的是进程外影响。

如下所述,我能够使用单个方法创建自己的 COM 对象,替换上面示例中的 Scripting.Dictionary 调用。然后,我可以在 Test.rgs 文件中的“Apartment”和“Both”线程模型之间切换、重建并确认跨进程影响已在启用 MTA 的对象中得到解决。

此外,附加调试器并暂停进程,然后使用 procexp(来自 Microsoft SysInternals),可以看到调用堆栈进入内核,我假设有一个队列正在管理用于 COM 编组,从而导致瓶-跨越不同的进程。

更糟糕的是,当编组对不同对象的调用时也会出现这种减速,这可以通过运行一个调用 Scripting.Dictionary 的应用程序、其他几个调用 DoNothing() 的应用程序并观察它们都减速来观察到。

出于我们的目的,我们将使用另一种字典实现(尽管可以更改 Scripting.Dictionary 线程模型,但这似乎不明智)。

我想关于 COM 编组的进程间影响还有一个悬而未决的问题。建议必须尽量避免在多进程环境中使用单元线程,除非有人有更好的答案......?

创建测试 COM 对象

  • 在 Visual Studio 中创建一个新项目,使用 C++ ATL 模板(我使用的是 VS 2008)。
  • 选择服务器类型 DLL。
  • 右键单击项目,添加类... -> ATL 简单对象。
  • 给它一个短名称,例如“测试”,确保选择了单元线程。
  • 在类视图中,右键单击接口 (ITest),然后添加方法...
  • 命名该方法,例如“DoNothing”,并保留默认选项。
  • 检查方法实现是否为空,然后 Build.

现在可以从测试应用程序中添加一个 COM 引用并调用 DoNothing() 方法。

于 2013-05-10T11:45:14.823 回答