Это немного сложно сделать, но это можно сделать с помощью DynamicMethod в пространстве имен System.Reflection.Emit.Это позволяет нам генерировать IL во время выполнения, которое вызывает эти методы без необходимости ссылаться на действительные, видимые идентификаторы в нашем коде.Один из приемов, которые может использовать этот класс, - пропустить различные проверки безопасности и видимости, которые мы устанавливаем с помощью параметров в конструкторе.Из примера нам нужно заменить класс Utils
.Вот его переписывание с использованием DynamicMethod для создания делегатов:
internal static class DelegateUtils
{
private static readonly Type UtilsType = Type.GetType("System.Security.Cryptography.Utils");
private static readonly Func<int, SafeHandle> CreateHashDel;
private static readonly Action<SafeHandle, byte[], int, int> HashDataDel;
private static readonly Func<SafeHandle, byte[]> EndHashDel;
static DelegateUtils()
{
CreateHashDel = CreateCreateHashDelegate();
HashDataDel = CreateHashDataDelegate();
EndHashDel = CreateEndHashDelegate();
}
internal static SafeHandle CreateHash(int algid)
{
return CreateHashDel(algid);
}
internal static void HashData(SafeHandle h, byte[] data, int ibStart, int cbSize)
{
HashDataDel(h, data, ibStart, cbSize);
}
internal static byte[] EndHash(SafeHandle h)
{
return EndHashDel(h);
}
private static Func<int, SafeHandle> CreateCreateHashDelegate()
{
var prop = UtilsType.GetProperty("StaticProvHandle", BindingFlags.NonPublic | BindingFlags.Static);
var createHashMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
.FirstOrDefault(mi => mi.Name == "CreateHash" && mi.GetParameters().Length == 2);
var createHashDyn = new DynamicMethod("CreateHashDyn", typeof(SafeHandle), new[] { typeof(int) }, typeof(object), true);
var ilGen = createHashDyn.GetILGenerator();
ilGen.Emit(OpCodes.Call, prop.GetGetMethod(true));
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, createHashMethod);
ilGen.Emit(OpCodes.Ret);
var del = (Func<int, SafeHandle>)createHashDyn.CreateDelegate(typeof(Func<int, SafeHandle>));
return del;
}
private static Action<SafeHandle, byte[], int, int> CreateHashDataDelegate()
{
var hashDataMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
.FirstOrDefault(mi => mi.Name == "HashData" && mi.GetParameters().Length == 4);
var hashDataDyn = new DynamicMethod("HashDataDyn", typeof(void), new[] { typeof(SafeHandle), typeof(byte[]), typeof(int), typeof(int) }, typeof(object), true);
var ilGen = hashDataDyn.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldarg_1);
ilGen.Emit(OpCodes.Ldarg_2);
ilGen.Emit(OpCodes.Ldarg_3);
ilGen.Emit(OpCodes.Call, hashDataMethod);
ilGen.Emit(OpCodes.Ret);
var del = (Action<SafeHandle, byte[], int, int>)hashDataDyn.CreateDelegate(typeof(Action<SafeHandle, byte[], int, int>));
return del;
}
private static Func<SafeHandle, byte[]> CreateEndHashDelegate()
{
var endHashMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
.FirstOrDefault(mi => mi.Name == "EndHash" && mi.GetParameters().Length == 1);
var endHashDyn = new DynamicMethod("EndHashDyn", typeof(byte[]), new[] { typeof(SafeHandle) }, typeof(object), true);
var ilGen = endHashDyn.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, endHashMethod);
ilGen.Emit(OpCodes.Ret);
var del = (Func<SafeHandle, byte[]>)endHashDyn.CreateDelegate(typeof(Func<SafeHandle, byte[]>));
return del;
}
}
Далее вопрос в том, насколько это дает преимущество в скорости.Это дает увеличение примерно в 2-4 раза в зависимости от размера хешируемых данных.Меньшее ускорение ускоряется, вероятно, потому, что мы тратили меньше времени на вычисления и больше времени между вызовами методов.Вот результаты быстрого теста:
BenchmarkDotNet = v0.11.1, ОС = Windows 10.0.17134.286 (1803 / April2018Update / Redstone4)
Процессор Intel Core i5-4200U 1,60 ГГц (Haswell), 1 ЦП, 4 логических и 2 физических ядра
Частота = 2240904 Гц, Разрешение = 446,2485 нс, Таймер = TSC
[Хост]: .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32-разрядная версия LegacyJIT-v4.7.3163.0
DefaultJob: .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32-битная версия LegacyJIT-v4.7.3163.0
Метод |N |Значит |Ошибка |StdDev |
----------- | ------ | ----------: | ----------: | ---------: |
Отражение |1000 |16,239 с нами |0,1252 нас |0.1046 us |
Делегат |1000 |4,329 нас |0.0245 us |0.0230 us |
Отражение |10000 |31,832 сша |0,1599 с нами |0,1335 us |
Делегат |10000 |19,703 us |0,1005 с нами |0.0940 us |
Обратите внимание, что N - это количество байтов, которые хэшируются.При этом используется весь код, предоставленный в ссылках OP, для создания реализации MD4, а затем вызывается ComputeHash для этого.
Код теста:
public class MD4DelegateVsReflection
{
private MD4 md4 = MD4.Create();
private byte[] data;
[Params(1000, 10000)]
public int N;
public void SetupData()
{
data = new byte[N];
new Random(42).NextBytes(data);
}
[GlobalSetup(Target = nameof(Reflection))]
public void ReflectionSetup()
{
MD4.SetReflectionUtils();
SetupData();
}
[GlobalSetup(Target = nameof(Delegate))]
public void DelegateSetup()
{
MD4.SetDelegateUtils();
SetupData();
}
[Benchmark]
public byte[] Reflection() => md4.ComputeHash(data);
[Benchmark]
public byte[] Delegate() => md4.ComputeHash(data);
}