Với đoạn mã sau:ILGenerator phương pháp nội tuyến
using System;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Reflection;
namespace ConsoleApplication1
{
class A
{
public int Do(int n)
{
return n;
}
}
public delegate int DoDelegate();
class Program
{
public static void Main(string[] args)
{
A a = new A();
Stopwatch stopwatch = Stopwatch.StartNew();
int s = 0;
for (int i = 0; i < 100000000; i++)
{
s += a.Do(i);
}
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine(s);
DynamicMethod dm = new DynamicMethod("Echo", typeof(int), new Type[] { typeof(int) }, true);
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);
DynamicMethod dm2 = new DynamicMethod("Test", typeof(int), new Type[0]);
il = dm2.GetILGenerator();
Label loopStart = il.DefineLabel();
Label loopCond = il.DefineLabel();
il.DeclareLocal(typeof(int)); // i
il.DeclareLocal(typeof(int)); // s
// s = 0;
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc_1);
// i = 0;
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Br_S, loopCond);
il.MarkLabel(loopStart);
// s += Echo(i);
il.Emit(OpCodes.Ldloc_1); // Load s
il.Emit(OpCodes.Ldloc_0); // Load i
il.Emit(OpCodes.Call, dm); // Call echo method
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stloc_1);
// i++
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stloc_0);
il.MarkLabel(loopCond);
// Check for loop condition
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4, 100000000);
il.Emit(OpCodes.Blt_S, loopStart);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ret);
DoDelegate doDel = (DoDelegate)dm2.CreateDelegate(typeof(DoDelegate));
s = doDel.Invoke(); // Dummy run to force JIT
stopwatch = Stopwatch.StartNew();
s = doDel.Invoke();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine(s);
}
}
}
Gọi phương pháp Đừng bị inlined. Vòng lặp kết thúc trong khoảng 40 ms. Nếu tôi, ví dụ, làm cho Do là chức năng ảo, nó không nhận được nội tuyến, và vòng lặp kết thúc trong 240 ms. Càng xa càng tốt. Khi tôi sử dụng ILGenerator để tạo ra phương thức Do (Echo), và sau đó tạo DynamicMethod với cùng vòng lặp như phương thức chính đã cho, gọi phương thức Echo không bao giờ được inlined và mất khoảng 240 ms cho vòng lặp kết thúc. Mã MSIL là chính xác vì nó trả về kết quả tương tự như mã C#. Tôi đã chắc chắn rằng phương pháp nội tuyến là một cái gì đó được thực hiện bởi JIT, vì vậy tôi thấy không có lý do cho nó không để nội tuyến phương pháp Echo.
Có ai biết tại sao phương pháp đơn giản này sẽ không được JIT giới thiệu.
Bạn cũng đang tạo mã gọi phương thức Do() được tạo động hay là mã đó được biết đến lúc biên dịch? –
Bạn có thể bao gồm mẫu mã đầy đủ sử dụng ILGenerator không? Và, chỉ để chắc chắn: bạn có đang thử nghiệm dưới bản phát hành ** không ** kèm theo trình sửa lỗi không? –
Tôi đã chỉnh sửa lại bài đăng, cung cấp mã đầy đủ cho ứng dụng thử nghiệm. Tôi sử dụng phát hành xây dựng và chạy nó mà không có trình gỡ rối. C# cho vòng lặp inline gọi phương thức và chạy nhanh hơn đáng kể so với vòng lặp IL. – user102808