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

问题描述:

为了说明这个问题,编译一个C#项目,引用Microsoft Scripting Runtime和下面的代码。运行生成的可执行文件的单个实例。在我的12核心机器上,读取循环持续约180ms。启动另一个可执行文件的实例会减慢速度,每增加一个可执行文件大约100毫秒。Scripting.Dictionary性能受多个进程影响

任何想法发生了什么?除了切换到不同的字典实现以外的任何解决方案?

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

在控制台上滚动一千行文本,而计时代码是一个非常糟糕的主意。 – 2013-05-09 17:47:28

+2

我在1997年写了'Scripting.Dictionary'。我不知道你的问题是什么原因,但我会很好奇。不管它是什么,我都可以向你保证它不太可能被修复。 – 2013-05-09 19:25:22

埃里克,你一定会很高兴听到这个问题是不特定的Scripting.Dictionary,而是与公寓线程模型。可以阅读关于混合线程模型对MSDN的性能影响,但是对我来说这是一个惊喜(out-of-process)的影响。

我能够用一种方法创建自己的COM对象,如下所述,替换上例中的Scripting.Dictionary调用。然后,我可以在Test.rgs文件中的'Apartment'和'Both'线程模型之间切换,重建,并确认在启用MTA的对象中解决了跨进程影响。另外,附加一个调试器并暂停进程,然后使用procexp(来自Microsoft SysInternals),可以看到调用堆栈进入内核,在那里我假设有一个队列被管理用于COM编组,造成跨越不同流程的瓶颈。

更糟糕的是,当调用对不同对象的调用时,也会发生这种放缓,这是通过运行一个调用Scripting.Dictionary的应用程序来调用的,其他几个调用DoNothing()并观察它们全部变慢。

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

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

创建测试COM对象

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

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

+1

感谢您的更新;很高兴你想出来了。顺便说一句,有一个版本的Scripting.Dictionary错误地标记为Both,而不是Apartment。我的错。你可以在这里阅读有趣的故事:http://blogs.msdn.com/b/ericlippert/archive/2003/09/19/53054.aspx – 2013-05-10 17:08:49