10

パブリックプロパティをコピーする単純なオブジェクトコピー機を作成しました。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<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;
        }

私がデバッグできることから、2つのメソッドは同じ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

これを二度読んでくださったことをお詫び申し上げます。私はこれを最初に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.
4

2 に答える 2

14

これは少し遅れていますが、すべてのアセンブリで.NET 4にいくつかのセキュリティ属性を設定し、組み込みのデリゲートタイプ、または同じセキュリティ属性を持つデリゲートを使用すると、パフォーマンスが大幅に向上します。

必要な属性は次のとおりです。

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

これは実際には少しバグのようです。ただし、コードはセキュリティ権限を上げないと言っているので、部分的に信頼された呼び出し元をブロックすることはありません。したがってskipVisibility=true、完全に信頼して使用する場合、デリゲートを呼び出すと、Func<int,int>基本的にほとんどすべての権限チェックを回避できます。

もう1つ、これらはデリゲートであるため、インスタンスメソッドのように扱うと、そうではない場合でも、最高のパフォーマンスが得られます。つまりDelegate.CreateDelegate、パラメータを受け入れ、firstArgumentデリゲートに初期オブジェクト参照を追加する2つのメソッドのいずれかを常に使用します。

所有者を割り当てずに、 DynamicMethodwithを作成することを検討してください。skipVisibility=true所有者を割り当てると、検証できないコードを実行できます。あなたはこれで本当にめちゃくちゃなことをすることができるので、あなたが何をしているのかを知らない限り私はそれを避けます。

于 2013-01-12T19:27:36.173 に答える
11

この問題は、.NETFramework4.0で行われた変更によって発生しました。ユーザー「Alan-N」がCodeProjectに投稿した解決策を見つけました。

実行時間の大幅な低下は、コンストラクターを使用した場合に発生する「システム提供の、完全に信頼された、セキュリティ透過性のアセンブリ」DynamicMethodに関連付けられたときに発生します。.NET 4は以前のバージョンよりも多くのセキュリティチェックを行っているようですが、実際に何が起こっているのかについての洞察や説明はありません。DynamicMethod(string, Type, Type[], bool)

DynamicMethodをaに関連付けると(代わりTypeにコンストラクターを使用します。追加の-valuedパラメーター'owner'に注意してください)、速度のペナルティが完全に削除されます。DynamicMethod(string, Type, Type[], Type, bool)Type

関連する可能性のあるMSDNに関するメモがいくつかあります(私がそれらを理解できた場合のみ!):

于 2012-11-17T15:34:57.050 に答える