DynamicMethod的是不是编译IL功能更慢(DynamicMethod is much sl

2019-07-03 21:53发布

我写了一个简单的对象复印机那份公共属性。 我想不通,为什么动态方法比C#版本慢了许多。

持续时间

C#方法:4963毫秒

动态方法:19924毫秒

需要注意的是 - 正如我开始秒表之前运行动态方法 - 持续时间不包括编译阶段。 我运行在调试和发布模式,在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<T1, T2> CreateCopier<T1, T2>()
        {
            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<T1, T2>));
            return (Action<T1, T2>)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<Person, Person>)CopyPerson;
            var copyUsingADynamicMethod = CreateCopier<Person, Person>();

            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却没有得到我所希望的答案。

编辑2012年11月17日15:11:

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

Answer 1:

这是一个有点晚了,但如果你设置在.NET 4的所有组件的几个安全属性,并具有相同的安全使用内置的委托类型,或委托属性,你会看到一个相当的性能增益。

这里是你将要的属性:

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

这实际上似乎是有点错误的。 但是,因为你是在说你的代码不会提高安全权限,您将不会阻止部分信任调用方,因此,如果您使用skipVisibility=true的完全信任,调用一个Func<int,int>委托基本上应该差不多避免所有的权限检查。

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

考虑构建DynamicMethodskipVisibility=true ,但没有指定的所有者。 分配所有者允许您运行无法验证的代码 。 你可以做一些真正搞砸了事情这一点,所以我会避免它,除非你知道自己在做什么。



Answer 2:

这个问题是由所做的更改引入.NET框架4.0。 我发现了一个解决方案由用户发布“ 艾伦-N ”在CodeProject上。

在执行时间的大幅减速的时候造成DynamicMethod获取与相关的“系统提供的,完全可信,安全透明组件,”如果使用这恰好DynamicMethod(string, Type, Type[], bool)构造函数。 看来,.NET 4做得比以前的版本更安全检查,虽然我没有洞察到,或解释,什么是真正回事。

关联所述DynamicMethodType (通过使用DynamicMethod(string, Type, Type[], Type, bool)构造代替;注意附加Type -valued参数,“所有者”)完全消除速度损失。

有MSDN上这可能是相关的一些注意事项(只要我能理解他们!):

  • DynamicMethod的构造函数(String,类型,类型[]布尔值)
  • 在反射发出安全性问题


文章来源: DynamicMethod is much slower than compiled IL function