DynamicMethod比编译的IL函数慢得多

我写了一个复制公共属性的简单对象复制器。 我无法弄清楚为什么Dynamic方法比c#版本慢得多。

持续时间

C#方法:4,963毫秒

动态方法:19,924毫秒

请注意 – 当我在启动秒表之前运行动态方法时 – 持续时间不包括编译阶段。 我在调试和发布模式下,在x86和x64模式下运行,从VS和命令行运行,结果大致相同(动态方法慢400%)。

const int NBRECORDS = 100 * 1000 * 1000; public class Person { private int mSomeNumber; public string FirstName { get; set; } public string LastName { get; set; } public DateTime DateOfBirth { get; set; } public int SomeNumber { get { return mSomeNumber; } set { mSomeNumber = value; } } } public static Action CreateCopier() { var meth = new DynamicMethod("copy", null, new Type[] { typeof(T1), typeof(T2) }, restrictedSkipVisibility: true); ILGenerator il = meth.GetILGenerator(); int cpt = 0; var stopHere = typeof(Program).GetMethod("StopHere"); foreach (var mi1 in typeof(T1).GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var mi2 = typeof(T2).GetProperty(mi1.Name, BindingFlags.Public | BindingFlags.Instance); if (mi1 != null && mi2 != null) { cpt++; il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Callvirt, mi1.GetMethod); il.Emit(OpCodes.Callvirt, mi2.SetMethod); } } il.Emit(OpCodes.Ret); var dlg = meth.CreateDelegate(typeof(Action)); return (Action)dlg; } static void Main(string[] args) { var person1 = new Person() { FirstName = "Pascal", LastName = "Ganaye", DateOfBirth = new DateTime(1909, 5, 1), SomeNumber = 23456 }; var person2 = new Person(); var copyUsingAMethod = (Action)CopyPerson; var copyUsingADynamicMethod = CreateCopier(); copyUsingAMethod(person1, person2); // 4882 ms var sw = Stopwatch.StartNew(); for (int i = 0; i < NBRECORDS; i++) { copyUsingAMethod(person1, person2); } Console.WriteLine("{0} ms", sw.ElapsedMilliseconds); copyUsingADynamicMethod(person1, person2); // 19920 ms sw = Stopwatch.StartNew(); for (int i = 0; i < NBRECORDS; i++) { copyUsingADynamicMethod(person1, person2); } Console.WriteLine("{0} ms", sw.ElapsedMilliseconds); Console.ReadKey(intercept: true); } private static void CopyPerson(Person person1, Person person2) { person2.FirstName = person1.FirstName; person2.LastName = person1.LastName; person2.DateOfBirth = person1.DateOfBirth; person2.SomeNumber = person1.SomeNumber; } 

从我可以调试的两个方法具有相同的IL代码。

 IL_0000: nop IL_0001: ldarg.1 IL_0002: ldarg.0 IL_0003: callvirt System.String get_FirstName()/DuckCopy.SpeedTests.Program+Person IL_0008: callvirt Void set_FirstName(System.String)/DuckCopy.SpeedTests.Program+Person IL_000d: nop IL_000e: ldarg.1 IL_000f: ldarg.0 IL_0010: callvirt System.String get_LastName()/DuckCopy.SpeedTests.Program+Person IL_0015: callvirt Void set_LastName(System.String)/DuckCopy.SpeedTests.Program+Person IL_001a: nop IL_001b: ldarg.1 IL_001c: ldarg.0 IL_001d: callvirt System.DateTime get_DateOfBirth()/DuckCopy.SpeedTests.Program+Person IL_0022: callvirt Void set_DateOfBirth(System.DateTime)/DuckCopy.SpeedTests.Program+Person IL_0027: nop IL_0028: ldarg.1 IL_0029: ldarg.0 IL_002a: callvirt Int32 get_SomeNumber()/DuckCopy.SpeedTests.Program+Person IL_002f: callvirt Void set_SomeNumber(Int32)/DuckCopy.SpeedTests.Program+Person IL_0034: nop IL_0035: ret 

如果你读了两遍,我会applogize。 我最初发布在: http : //www.codeproject.com/Answers/494714/Can-27tplusfigureplusoutpluswhyplusthisplusDynamic但没有得到我希望的所有答案。

编辑17 nov 2012 15:11:

 removed the nop removed the extra ="" which came from I don't where. 

这有点晚了,但是如果在所有程序集中在.NET 4中设置一些安全属性,并且使用内置委托类型或具有相同安全属性的委托 – 您将看到相当大的性能提升。

以下是您需要的属性:

 [assembly: AllowPartiallyTrustedCallers] [assembly: SecurityTransparent] [assembly: SecurityRules(SecurityRuleSet.Level2,SkipVerificationInFullTrust=true)] 

这实际上似乎是一个小错误。 但是因为你说你的代码不会提高安全权限,所以你不会阻止部分信任的调用者,所以如果你完全信任使用skipVisibility=true ,调用一个Func委托应该基本上避免所有的许可检查。

还有一件事,因为这些是委托,如果你把它们视为实例方法,你将获得最佳性能,即使它们不是。 也就是说总是使用两个Delegate.CreateDelegate方法中的一个接受firstArgument参数并向您的委托添加初始对象引用。

考虑使用skipVisibility=true构建DynamicMethod ,但不指定所有者。 分配所有者允许您运行无法validation的代码 。 你可以做一些真正搞砸的东西,所以我会避免它,除非你知道你在做什么。

.NET Framework 4.0中所做的更改引入了此问题。 我在CodeProject上找到了用户“ Alan-N ”发布的解决方案 。

DynamicMethod与“系统提供的,完全受信任的,安全透明的程序集”相关联时会导致执行时间大幅减慢,如果使用DynamicMethod(string, Type, Type[], bool)构造函数DynamicMethod(string, Type, Type[], bool)则会发生这种情况。 似乎.NET 4正在进行比以前版本更多的安全检查,尽管我没有深入了解或解释实际发生的事情。

DynamicMethodType (通过使用DynamicMethod(string, Type, Type[], Type, bool)构造函数;注意附加的Type -valued参数, ‘owner’ )完全消除了速度惩罚。

MSDN上有一些注释可能是相关的(如果我能理解它们的话):

  • DynamicMethod构造函数(String,Type,Type [],Boolean)
  • 反思中的安全问题