Tôi hiện đang thực hiện một số biện pháp tối ưu hóa cuối cùng, chủ yếu là để giải trí và học tập, và khám phá điều gì đó khiến tôi có một vài câu hỏi.Sự tò mò: Tại sao biểu thức <...> khi được biên dịch chạy nhanh hơn một DynamicMethod tối thiểu?
Thứ nhất, các câu hỏi:
- Khi tôi xây dựng một phương pháp trong bộ nhớ thông qua việc sử dụng các DynamicMethod, và sử dụng trình gỡ lỗi, có cách nào cho tôi để bước vào mã lắp ráp tạo ra, khi vieweing mã trong khung nhìn disassembler? Trình gỡ rối dường như chỉ cần bước qua toàn bộ phương thức cho tôi
- Hoặc nếu điều đó là không thể, tôi có thể tiết kiệm mã IL đã tạo thành đĩa như một assembly, để tôi có thể kiểm tra nó với Reflector?
- Tại sao phiên bản
Expression<...>
của phương pháp bổ sung đơn giản của tôi (Int32 + Int32 => Int32) chạy nhanh hơn phiên bản DynamicMethod tối thiểu?
Đây là chương trình ngắn và đầy đủ minh họa. Trên hệ thống của tôi, đầu ra là:
DynamicMethod: 887 ms
Lambda: 1878 ms
Method: 1969 ms
Expression: 681 ms
tôi mong đợi các lambda và phương pháp gọi là có giá trị cao hơn, nhưng phiên bản DynamicMethod chậm liên tục khoảng 30-50% (biến thể có thể là do Windows và các chương trình khác). Có ai biết lý do không?
Dưới đây là các chương trình:
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;
}
}
}
Câu hỏi thú vị. Những thứ này có thể được giải quyết bằng WinDebug và SOS. Tôi đăng một bước của một phân tích tương tự tôi đã làm nhiều vệ tinh trước đây trong blog của tôi, http://blog.barrkel.com/2006/05/clr-tailcall-optimization-or-lack.html –
Tôi nghĩ rằng tôi nên ping bạn - tôi phát hiện ra cách ép buộc JIT mà không phải gọi phương thức một lần. Sử dụng đối số constructor 'restrictedSkipVisibility' DynamicMethod. Tùy thuộc vào ngữ cảnh (bảo mật mã), nó có thể không có sẵn mặc dù. –
Câu hỏi thực sự hay. Đầu tiên, đối với loại hồ sơ này, tôi sẽ sử dụng bản phát hành/Bảng điều khiển - vì vậy, 'Debug.WriteLine' trông không đúng chỗ; nhưng ngay cả với 'Console.WriteLine' số liệu thống kê của tôi là tương tự: DynamicMethod: 630 ms Lambda: 561 ms Phương pháp: 553 ms Biểu hiện: 360 ms Tôi vẫn đang tìm kiếm ... –