나는 Reflection.Emit에서 놀고 있었는데, 거의 사용되지 않은 EmitCalli
에 대해 발견했다. 호기심, 나는 그것이 일반 메소드 호출에서 어떤 다른 있는지 궁금 그래서 아래의 코드를 채찍질 :Calli가 대리모 통화보다 빠른 이유는 무엇입니까?
using System;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Security;
[SuppressUnmanagedCodeSecurity]
static class Program
{
const long COUNT = 1 << 22;
static readonly byte[] multiply = IntPtr.Size == sizeof(int) ?
new byte[] { 0x8B, 0x44, 0x24, 0x04, 0x0F, 0xAF, 0x44, 0x24, 0x08, 0xC3 }
: new byte[] { 0x0f, 0xaf, 0xca, 0x8b, 0xc1, 0xc3 };
static void Main()
{
var handle = GCHandle.Alloc(multiply, GCHandleType.Pinned);
try
{
//Make the native method executable
uint old;
VirtualProtect(handle.AddrOfPinnedObject(),
(IntPtr)multiply.Length, 0x40, out old);
var mulDelegate = (BinaryOp)Marshal.GetDelegateForFunctionPointer(
handle.AddrOfPinnedObject(), typeof(BinaryOp));
var T = typeof(uint); //To avoid redundant typing
//Generate the method
var method = new DynamicMethod("Mul", T,
new Type[] { T, T }, T.Module);
var gen = method.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ldc_I8, (long)handle.AddrOfPinnedObject());
gen.Emit(OpCodes.Conv_I);
gen.EmitCalli(OpCodes.Calli, CallingConvention.StdCall,
T, new Type[] { T, T });
gen.Emit(OpCodes.Ret);
var mulCalli = (BinaryOp)method.CreateDelegate(typeof(BinaryOp));
var sw = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++) { mulDelegate(2, 3); }
Console.WriteLine("Delegate: {0:N0}", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < COUNT; i++) { mulCalli(2, 3); }
Console.WriteLine("Calli: {0:N0}", sw.ElapsedMilliseconds);
}
finally { handle.Free(); }
}
delegate uint BinaryOp(uint a, uint b);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(
IntPtr address, IntPtr size, uint protect, out uint oldProtect);
}
내가 86 모드 및 x64 모드에서 코드를 실행했습니다. 결과는?
32 비트 :
- 위임 버전 : 994
- 캘러스 버전 : 46
64 비트 :
- 위임 버전 : 326
- Calli 버전 : 83
나는 지금 당장 의문의 여지가 ... 왜 그렇게 큰 속도 차이가 나는지?
업데이트 :
- 위임 버전 : 284
- 캘러스 판 77
내가 아니라 64 비트 P/호출 버전을 생성 P/Invoke 버전 : 31
명백히 P/Invoke가 더 빠릅니다 ... 내 벤치마킹에 문제가 있습니까? 아니면 이해가 안되는 부분이 있습니까? (나는 그런데 해제 모드에있다.)
+1, 좋은 질문 :) –
매우 흥미로운 질문입니다. 나도 기계에서 시도하고 큰 속도 차이가 있습니다. 나는 또한 그 뒤에 정확한 이유를 알고 싶다. –
실제로 벤치마킹이 잘못되었다고 의심하기 시작했습니다. 중간에 내가 눈치 채지 못하는 지시 사항이있을 수 있습니다. 결과가 엉망입니다. 지금은 많이 생각할 수는 없지만 ... – Mehrdad