DynamicMethod jest znacznie wolniejszy niż skompilowana funkcja IL
Napisałem prostą kopiarkę obiektów, która kopiuje właściwości publiczne. Nie mogę zrozumieć, dlaczego metoda Dynamic jest dużo wolniejsza niż wersja c #.
Czas trwania
Metoda C #: 4,963 ms
Metoda dynamiczna: 19,924 ms
Zauważ, że - ponieważ uruchamiam metodę dynamiczną przed uruchomieniem stopera - czas trwania nie obejmuje fazy kompilacji. Uruchamiam to w trybie Debug and Release, w trybie x86 i x64 oraz z VS iz linii poleceń z mniej więcej tym samym wynikiem (metoda dynamiczna jest wolniejsza o 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;
}
Z tego, co mogę debugować, dwie metody mają ten sam kod 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
Aplikuję, jeśli przeczytasz to dwa razy. Wysłałem to pierwotnie w:http://www.codeproject.com/Answers/494714/Can-27tplusfigureplusoutpluswhyplusthisplusDynamic ale nie dostałem wszystkich odpowiedzi, na które miałem nadzieję.
edytowany 17 listopada 2012 15:11:
removed the nop
removed the extra ="" which came from I don't where.