Разница между LambdaExpression.Compile и Delegate.CreateDelegate

В процессе оптимизации вызовов отражения я экспериментирую с использованием Delegate.CreateDelegate и LambdaExpression.Compile чтобы превратить мои MethodInfo в более быстрых делегатов.

Основываясь на этом вопросе, я ожидал, что LambdaExpression.Compile будет работать значительно лучше, потому что он будет генерировать фактический MSIL для вызова метода — что-то близкое к прямому вызову метода.

Однако после бенчмаркинга я заметил, что оба метода имеют примерно одинаковую производительность.

Моя гипотеза заключается в том, что накладные расходы, которые я получаю при вызове делегата, созданного с использованием LambdaExpression.Compile, связаны с использованием самих делегатов. Чтобы подтвердить, я создал еще один тест, в котором я вручную создал делегата, напрямую вызывающего метод, и он имел аналогичную производительность.

Поэтому мой вопрос: есть ли разница в объекте делегата, сгенерированном LambdaExpression.Compile и Delegate.CreateDelegate?

Я ищу теоретический ответ о разнице между двумя подходами, поэтому я не буду вдаваться в подробности реализации, но вот суть реализации:


person Lazlo    schedule 06.04.2017    source источник
comment
тому, кто проголосовал за закрытие; хотите прокомментировать?   -  person Lazlo    schedule 06.04.2017
comment
Я не понимаю, почему делегат LambdaExpression должен работать быстрее. Да, он генерирует msil, но Delegate.CreateDelegate создает делегата для уже существующего метода с уже сгенерированным msil, так в чем же разница?   -  person Evk    schedule 06.04.2017
comment
Это именно мой вопрос. Есть ли разница на уровне IL между вызовом делегата, созданного с помощью Delegate.CreateDelegate, и вызовом делегата, созданного с помощью LambdaExpression.Compile? (даже если эта разница невелика).   -  person Lazlo    schedule 07.04.2017
comment
Ну, один делегат (ссылка на функцию), а другой делегат. Между ними нет никакой разницы. Вся разница в функциях, на которые указывают эти делегаты.   -  person Evk    schedule 07.04.2017


Ответы (1)


Существует разница между Delegate.CreateDelegate и LambdaExpression.Compile. Действительно, второй делает что-то большее и может быть более эффективным в определенных условиях.

Когда вы вызываете Delegate.CreateDelegate вручную, вы можете вызвать его с некоторой перегрузкой с целевым значением, но в некоторых случаях, например со статическим методом, у вас нет целевого значения.

В фоновом режиме, когда у вас нет целевого значения, делегат создается с помощью сгенерированного метода для извлечения бесполезного значения из стека перед переходом к реальному методу. Вы можете наблюдать это с помощью наблюдателя: у делегата есть InPtr и InPtrAux.

Когда IntPtrAux назначается действительному указателю, это означает, что InPtr вызывается до того, как выталкивает дескриптор и переходит к IntPtrAux. Вот почему делегат для делегата, сделанный для метода экземпляра (дескриптор «это»), более эффективен по сравнению с тем же для статического метода.

При компиляции лямбда-выражения для создания делегата всегда используется Target (для закрытия) и избегается вызов/переход через IntPtrAux, что часто бывает эффективным.

person Tony THONG    schedule 07.04.2017