Đối với trình mô phỏng dịch nhị phân động, tôi cần tạo các assembly thu thập được với các lớp truy cập các trường tĩnh. Tuy nhiên, khi sử dụng các trường tĩnh bên trong các cụm thu, hiệu suất thực hiện là thấp hơn 2-3 so với các cụm không thu được. Hiện tượng này không có trong các cụm thu được không sử dụng các trường tĩnh.Truy cập trường tĩnh trong các cụm động thu được thiếu hiệu suất
Trong mã bên dưới phương pháp MyMethod
của lớp trừu tượng AbstrTest
được triển khai bởi các cụm động có thể thu và không thu được. Sử dụng CreateTypeConst
các MyMethod
nhân giá trị đối số ulong bởi một giá trị không đổi của hai, trong khi sử dụng CreateTypeField
yếu tố thứ hai được lấy từ một trường khởi tạo hàm khởi tạo tĩnh MyField
.
Để có được kết quả thực tế, kết quả MyMethod
được tích lũy trong vòng lặp for.
Sau đây là các kết quả đo lường (.NET CLR 4.5/4.6):
Testing non-collectible const multiply:
Elapsed: 8721.2867 ms
Testing collectible const multiply:
Elapsed: 8696.8124 ms
Testing non-collectible field multiply:
Elapsed: 10151.6921 ms
Testing collectible field multiply:
Elapsed: 33404.4878 ms
Đây là mã người mô phỏng của tôi:
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
public abstract class AbstrTest {
public abstract ulong MyMethod(ulong x);
}
public class DerivedClassBuilder {
private static Type CreateTypeConst(string name, bool collect) {
// Create an assembly.
AssemblyName myAssemblyName = new AssemblyName();
myAssemblyName.Name = name;
AssemblyBuilder myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
myAssemblyName, collect ? AssemblyBuilderAccess.RunAndCollect : AssemblyBuilderAccess.Run);
// Create a dynamic module in Dynamic Assembly.
ModuleBuilder myModuleBuilder = myAssembly.DefineDynamicModule(name);
// Define a public class named "MyClass" in the assembly.
TypeBuilder myTypeBuilder = myModuleBuilder.DefineType("MyClass", TypeAttributes.Public, typeof(AbstrTest));
// Create the MyMethod method.
MethodBuilder myMethodBuilder = myTypeBuilder.DefineMethod("MyMethod",
MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig,
typeof(ulong), new Type [] { typeof(ulong) });
ILGenerator methodIL = myMethodBuilder.GetILGenerator();
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Ldc_I4_2);
methodIL.Emit(OpCodes.Conv_U8);
methodIL.Emit(OpCodes.Mul);
methodIL.Emit(OpCodes.Ret);
return myTypeBuilder.CreateType();
}
private static Type CreateTypeField(string name, bool collect) {
// Create an assembly.
AssemblyName myAssemblyName = new AssemblyName();
myAssemblyName.Name = name;
AssemblyBuilder myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
myAssemblyName, collect ? AssemblyBuilderAccess.RunAndCollect : AssemblyBuilderAccess.Run);
// Create a dynamic module in Dynamic Assembly.
ModuleBuilder myModuleBuilder = myAssembly.DefineDynamicModule(name);
// Define a public class named "MyClass" in the assembly.
TypeBuilder myTypeBuilder = myModuleBuilder.DefineType("MyClass", TypeAttributes.Public, typeof(AbstrTest));
// Define a private String field named "MyField" in the type.
FieldBuilder myFieldBuilder = myTypeBuilder.DefineField("MyField",
typeof(ulong), FieldAttributes.Private | FieldAttributes.Static);
// Create the constructor.
ConstructorBuilder constructor = myTypeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.HideBySig,
CallingConventions.Standard, Type.EmptyTypes);
ConstructorInfo superConstructor = typeof(AbstrTest).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, Type.EmptyTypes, null);
ILGenerator constructorIL = constructor.GetILGenerator();
constructorIL.Emit(OpCodes.Ldarg_0);
constructorIL.Emit(OpCodes.Call, superConstructor);
constructorIL.Emit(OpCodes.Ldc_I4_2);
constructorIL.Emit(OpCodes.Conv_U8);
constructorIL.Emit(OpCodes.Stsfld, myFieldBuilder);
constructorIL.Emit(OpCodes.Ret);
// Create the MyMethod method.
MethodBuilder myMethodBuilder = myTypeBuilder.DefineMethod("MyMethod",
MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig,
typeof(ulong), new Type [] { typeof(ulong) });
ILGenerator methodIL = myMethodBuilder.GetILGenerator();
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Ldsfld, myFieldBuilder);
methodIL.Emit(OpCodes.Mul);
methodIL.Emit(OpCodes.Ret);
return myTypeBuilder.CreateType();
}
public static void Main() {
ulong accu;
Stopwatch stopwatch;
try {
Console.WriteLine("Testing non-collectible const multiply:");
AbstrTest i0 = (AbstrTest)Activator.CreateInstance(
CreateTypeConst("MyClassModule0", false));
stopwatch = Stopwatch.StartNew();
accu = 0;
for (uint i = 0; i < 0xffffffff; i++)
accu += i0.MyMethod(i);
stopwatch.Stop();
Console.WriteLine("Elapsed: " + stopwatch.Elapsed.TotalMilliseconds + " ms");
Console.WriteLine("Testing collectible const multiply:");
AbstrTest i1 = (AbstrTest)Activator.CreateInstance(
CreateTypeConst("MyClassModule1", true));
stopwatch = Stopwatch.StartNew();
accu = 0;
for (uint i = 0; i < 0xffffffff; i++)
accu += i1.MyMethod(i);
stopwatch.Stop();
Console.WriteLine("Elapsed: " + stopwatch.Elapsed.TotalMilliseconds + " ms");
Console.WriteLine("Testing non-collectible field multiply:");
AbstrTest i2 = (AbstrTest)Activator.CreateInstance(
CreateTypeField("MyClassModule2", false));
stopwatch = Stopwatch.StartNew();
accu = 0;
for (uint i = 0; i < 0xffffffff; i++)
accu += i2.MyMethod(i);
stopwatch.Stop();
Console.WriteLine("Elapsed: " + stopwatch.Elapsed.TotalMilliseconds + " ms");
Console.WriteLine("Testing collectible field multiply:");
AbstrTest i3 = (AbstrTest)Activator.CreateInstance(
CreateTypeField("MyClassModule3", true));
stopwatch = Stopwatch.StartNew();
accu = 0;
for (uint i = 0; i < 0xffffffff; i++)
accu += i3.MyMethod(i);
stopwatch.Stop();
Console.WriteLine("Elapsed: " + stopwatch.Elapsed.TotalMilliseconds + " ms");
}
catch (Exception e) {
Console.WriteLine("Exception Caught " + e.Message);
}
}
}
Vì vậy, câu hỏi của tôi là: Tại sao nó chậm hơn?
Đó là những gì tôi đã đề xuất trong đoạn cuối. Không tĩnh, các instance fields của một class, bạn sẽ chỉ có một instance của đối tượng class. Bạn sẽ phải sử dụng nó trong các cuộc gọi ILGenerator.Emit() của bạn. Và sử dụng GCHandle.Alloc() để đảm bảo nó vẫn hoạt động cho đến khi mã được thu thập. –
Tôi nghĩ rằng hạn chế cốt lõi là jitter không thể giả định một cách an toàn rằng một biến tĩnh sẽ bị hủy hoặc thiết lập lại khi thu thập tập hợp được thu thập. Có nó giữ lại một tham chiếu đến một đối tượng lớp mà mã đã biến mất là thảm họa và có thể khai thác được. Một cái gì đó như thế. "Tại sao" sẽ không hữu ích để giải quyết vấn đề của bạn tất nhiên. –
Câu trả lời hay. Cảm ơn rất nhiều. – Paebbels