В настоящее время я занимаюсь последней оптимизацией, в основном для развлечения и обучения, и обнаружил кое-что, что оставило у меня пару вопросов.
Во-первых, вопросы:
- Когда я создаю метод в памяти с помощью DynamicMethod и используйте отладчик, есть ли способ войти в сгенерированный ассемблерный код при просмотре кода в представлении дизассемблера? Отладчик, кажется, просто перешагнул через весь метод для меня.
- Или, если это невозможно, могу ли я каким-то образом сохранить сгенерированный IL-код на диск в виде сборки, чтобы я мог проверить его с помощью Рефлектор?
- Почему версия
Expression<...>
моего простого метода сложения (Int32+Int32 => Int32) работает быстрее, чем минимальная версия DynamicMethod?
Вот короткая и полная программа, которая демонстрирует. В моей системе вывод:
DynamicMethod: 887 ms
Lambda: 1878 ms
Method: 1969 ms
Expression: 681 ms
Я ожидал, что лямбда-вызовы и вызовы методов будут иметь более высокие значения, но версия DynamicMethod постоянно примерно на 30-50% медленнее (вариации, вероятно, из-за Windows и других программ). Кто-нибудь знает причину?
Вот программа:
using System;
using System.Linq.Expressions;
using System.Reflection.Emit;
using System.Diagnostics;
namespace Sandbox
{
public class Program
{
public static void Main(String[] args)
{
DynamicMethod method = new DynamicMethod("TestMethod",
typeof(Int32), new Type[] { typeof(Int32), typeof(Int32) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
Func<Int32, Int32, Int32> f1 =
(Func<Int32, Int32, Int32>)method.CreateDelegate(
typeof(Func<Int32, Int32, Int32>));
Func<Int32, Int32, Int32> f2 = (Int32 a, Int32 b) => a + b;
Func<Int32, Int32, Int32> f3 = Sum;
Expression<Func<Int32, Int32, Int32>> f4x = (a, b) => a + b;
Func<Int32, Int32, Int32> f4 = f4x.Compile();
for (Int32 pass = 1; pass <= 2; pass++)
{
// Pass 1 just runs all the code without writing out anything
// to avoid JIT overhead influencing the results
Time(f1, "DynamicMethod", pass);
Time(f2, "Lambda", pass);
Time(f3, "Method", pass);
Time(f4, "Expression", pass);
}
}
private static void Time(Func<Int32, Int32, Int32> fn,
String name, Int32 pass)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (Int32 index = 0; index <= 100000000; index++)
{
Int32 result = fn(index, 1);
}
sw.Stop();
if (pass == 2)
Debug.WriteLine(name + ": " + sw.ElapsedMilliseconds + " ms");
}
private static Int32 Sum(Int32 a, Int32 b)
{
return a + b;
}
}
}
Debug.WriteLine
выглядит неуместно; но даже сConsole.WriteLine
моя статистика похожа: DynamicMethod: 630 мс Lambda: 561 мс Method: 553 мс Expression: 360 мс Я все еще ищу... - person Marc Gravell   schedule 19.08.2009restrictedSkipVisibility
DynamicMethod. Однако в зависимости от контекста (безопасности кода) он может быть недоступен. - person Barry Kelly   schedule 19.08.2009result
вInt32 result = fn(index, 1);
на результат? Оптимизация мертвого кода должна сработать, верно? Я также попытался распечатать результат и получил DynamicMethod: ~1000 ms Lambda: ~400 ms Method: ~1000 ms Expression: ~400 ms. Для меня результаты (DynamicMethod и Method) и (Lambda и Expression) всегда выравниваются, в отличие от результатов, которые здесь выравниваются (DynamicMethod и Expression) и (Lambda и Method). Странно... с использованием С# 6.0, .NET 4.6, любого процессора на 64-битной машине. - person nawfal   schedule 01.07.2016