Почему Делегат создается с `Delegate.CreateDelegate` быстрее, чем лямбда и делегаты методов? - PullRequest
0 голосов
/ 16 мая 2018

Все это время я читал об отражении, все всегда говорят: «отражение медленное», «отражение медленное».

Теперь я решил проверить, насколько медленно, и к моему удивлению, делегат, созданный с помощью отражения, на самом деле примерно в два раза быстрее, чем делегат, созданный с помощью лямбды, и, что удивительно, примерно в четыре раза быстрее, чем делегаты, принимающие объявленныеметоды.

См. Код

Это пользовательский класс, метод get свойства которого будет использоваться в делегатах:

#class to test
class SomeClass
{
    public SomeClass A { get; set; } //property to be gotten
    public static SomeClass GetA(SomeClass c) { return c.A; } //declared getter method
}

Это триЯ протестировал делегатов:

PropertyInfo AProp = typeof(SomeClass).GetProperty("A");

//1 - Created with reflection
Func<SomeClass, SomeClass> Getter = (Func<SomeClass, SomeClass>)Delegate.CreateDelegate(typeof(Func<SomeClass, SomeClass>), null, AProp.GetGetMethod());

//2 - Created with a lambda expression
Func<SomeClass, SomeClass> Getter2 = c => c.A;

//3 - Created from a declared method
Func<SomeClass, SomeClass> Getter3 = SomeClass.GetA;

Это тесты:

SomeClass C = new SomeClass();
C.A = new SomeClass(); //test doesn't change whether A is set or null
Stopwatch w;

//reflection delegate
w = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++) { SomeClass b = Getter(C); }
w.Stop(); Console.WriteLine(w.Elapsed);

//lambda delegate
w = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++) { SomeClass b = Getter2(C); }
w.Stop(); Console.WriteLine(w.Elapsed);

//method delegate
w = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++) { SomeClass b = Getter3(C); }
w.Stop(); Console.WriteLine(w.Elapsed);

//no delegate
w = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++) { SomeClass b = C.A; }
w.Stop(); Console.WriteLine(w.Elapsed);

И результаты:

enter image description here

Я также пытался инвертировать порядок тестирования, чтобы посмотреть, было ли влияние, или часы меня как-то обманули, но нет, тесты последовательны.

РЕДАКТИРОВАТЬ: с учетом компиляции "релиз", как предлагалось:

enter image description here

Сейчас ... Iожидал, что лямбда будет медленнее

1 Ответ

0 голосов
/ 16 мая 2018

Вот декомпиляция этого:

    Func<SomeClass, SomeClass> Getter = (Func<SomeClass, SomeClass>)Delegate.CreateDelegate(typeof(Func<SomeClass, SomeClass>), null, AProp.GetGetMethod());
    Func<SomeClass, SomeClass> arg_51_0;
    if ((arg_51_0 = Program.<>c.<>9__12_0) == null)
    {
        arg_51_0 = (Program.<>c.<>9__12_0 = new Func<SomeClass, SomeClass>(Program.<>c.<>9.<Main>b__12_0));
    }
    Func<SomeClass, SomeClass> Getter2 = arg_51_0;
    Func<SomeClass, SomeClass> Getter3 = new Func<SomeClass, SomeClass>(SomeClass.GetA);

Обратите внимание, что первый выживает компилятор почти без изменений, в то время как второй и третий довольно сильно модифицированы.

Если бы мне пришлось рисковать, предположение:

Первый вызов опережает некоторые хитрые приемы C ++ / COM для управления памятью, используемые в библиотеке Delegate.

Второй - создание нового метода и добавление нулевой проверки перед вызовом.это новый метод.

В то время как третий делает что-то похожее на второй, но сохраняет его до времени выполнения, я догадываюсь, почему по-прежнему вызывается свойство в новом встроенном методе (чего я и ожидалполучить его в свой метод, созданный компилятором, аналогично второй версии, так что я предполагаю, что эта часть произойдет во время компиляции, что объясняет, почему это время так смешно выше, чем в первых двух).

Я думаю, что комментарии относительно медленного отражения больше ориентированы на большие библиотеки;и я предполагаю, что вы не видите это здесь, потому что отражено на уроке, очень мало, так что размышлять не о чем.

РЕДАКТИРОВАТЬ: Когда я набирал этот последний бит, я решил попытаться замедлить первый вызов, расширив объект SomeClass.Я добавил около 30 новых свойств и около 20 новых методов.Казалось, не имеет значения.Я также слышал все предупреждения об отражении, так что это немного удивительно. В этом посте указывается, что со всем этим связан кеш, который, вероятно, очень помогает.Если все метаданные метода кэшируются, то отражения должны быть быстрее, чем проходить через дополнительные методы и проверки, добавленные компилятором.Возможно, это происходит, когда вы размышляете над внешним классом, который еще не загружен / не кэширован.Это значительно более сложный эксперимент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...