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正在进行比以前版本更多的安全检查,尽管我没有深入了解或解释实际发生的事情。
将DynamicMethod
与Type
(通过使用DynamicMethod(string, Type, Type[], Type, bool)
构造函数;注意附加的Type
-valued参数, ‘owner’ )完全消除了速度惩罚。
MSDN上有一些注释可能是相关的(如果我能理解它们的话):
- DynamicMethod构造函数(String,Type,Type [],Boolean)
- 反思中的安全问题