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<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
Я извиняюсь, если вы прочитаете это дважды. Первоначально я разместил это по адресу: http://www.codeproject.com/Answers/494714/Can-27tplusfigureplusoutpluswhyplusthisplusDynamic но не получил ответы, на которые я надеялся.
отредактировано 17 ноября 2012 15:11:
removed the nop
removed the extra ="" which came from I don't where.
2 ответа
Эта проблема была введена изменениями, внесенными в .NET
Framework 4.0. Я нашел решение, опубликованное пользователем " Alan-N" на CodeProject.
Большое замедление во времени выполнения вызывается, когда DynamicMethod
ассоциируется с "предоставленной системой, полностью доверенной, прозрачной для безопасности сборкой", которая происходит, если вы используете DynamicMethod(string, Type, Type[], bool)
конструктор. Похоже, что.NET 4 выполняет больше проверок безопасности, чем предыдущие версии, хотя у меня нет понимания или объяснения того, что на самом деле происходит.
Ассоциация DynamicMethod
с Type
(используя DynamicMethod(string, Type, Type[], Type, bool)
вместо конструктора; обратите внимание на дополнительные Type
-value-параметр 'owner') полностью снимает штраф за скорость.
На MSDN есть некоторые заметки, которые могут иметь отношение (если бы я только мог их понять!):
Это немного поздно, но если вы установите несколько атрибутов безопасности в .NET 4 на всех своих сборках и используете встроенные типы делегатов - или делегаты с одинаковыми атрибутами безопасности - вы увидите значительное повышение производительности.
Вот атрибуты, которые вы хотите:
[assembly: AllowPartiallyTrustedCallers]
[assembly: SecurityTransparent]
[assembly: SecurityRules(SecurityRuleSet.Level2,SkipVerificationInFullTrust=true)]
Это на самом деле кажется ошибкой. Но поскольку вы говорите, что ваш код не будет повышать разрешения безопасности, вы не заблокируете вызывающих с частичным доверием, поэтому, если вы используете skipVisibility=true
в полном доверии, ссылаясь на Func<int,int>
делегат должен в основном избегать почти всех проверок разрешений.
Еще одна вещь, поскольку это делегаты, вы получите лучшую производительность, если будете обращаться с ними как с методами экземпляра, даже если это не так. То есть всегда используйте один из двух Delegate.CreateDelegate
методы, которые принимают firstArgument
Параметр и добавьте исходную ссылку на объект вашему делегату.
Рассмотреть вопрос о создании DynamicMethod
с skipVisibility=true
, но без назначения владельца. Назначение владельца позволяет запускать непроверяемый код. Вы можете сделать некоторые действительно испорченные вещи с этим, поэтому я бы избегал этого, если вы не знаете, что делаете.