Scripting.Dictionary性能在多个进程中受到影响

时间:2013-05-09 17:42:46

标签: c# dictionary

为了说明问题,编译一个C#项目,引用Microsoft Scripting Runtime和下面的代码。运行生成的可执行文件的单个实例。在我的12核机器上,读取循环始终需要大约180ms。启动另一个可执行文件实例会减慢此速度,每个附加可执行文件大约减少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);
            }

        }
    }
}

1 个答案:

答案 0 :(得分:0)

Eric,你会很高兴听到这个问题并不是针对Scripting.Dictionary,而是针对Apartment线程模型。人们可以阅读混合线程模型对MSDN的性能影响,但令我感到意外的是对进程外的影响。

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

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

更糟糕的是,当编组对不同对象的调用时也会发生这种减速,这可以通过运行一个调用Scripting.Dictionary的应用程序,其他几个调用DoNothing(),并观察所有这些调用减慢来观察。

出于我们的目的,我们将使用替代字典实现(虽然可以更改Scripting.Dictionary线程模型,但这似乎是不明智的。)

我认为关于COM编组的进程间影响还有一个突出的问题。建议必须尽量避免在多进程环境中使用Apartment线程,除非有人有更好的答案......?

创建测试COM对象

  • 使用C ++ ATL模板在Visual Studio中创建一个新项目(我正在使用VS 2008)。
  • 选择服务器类型DLL。
  • 右键单击项目,添加类... - &gt; ATL简单对象。
  • 给它一个短名称,例如“测试”,确保选择公寓线程。
  • 在“班级视图”中,右键单击界面(ITest)和“添加方法...”
  • 命名方法,例如“DoNothing”,并保留默认选项。
  • 检查方法实现是否为空,以及Build。

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