Do symulacji dynamicznego tłumaczenia binarnego potrzebuję generować kolekcjonowalne zestawy .NET z klasami, które uzyskują dostęp do pól statycznych. Jednak w przypadku korzystania z pól statycznych w zespołach kolekcjonerskich wydajność wykonywania jest niższa o 2-3 razy w porównaniu z nieciągliwymi złożeniami. Zjawisko to nie występuje w kolekcjonowalnych złożeniach , które nie używają pól statycznych.Dostęp do pola statycznego w kolekcjonerskich złożeniach dynamicznych jest niewystarczający.
W poniższym kodzie metoda MyMethod
abstrakcyjnej klasy AbstrTest
jest implementowana przez kolekcjonerskie i nie kolekcjonowalne zestawy dynamiczne. Przy użyciu CreateTypeConst
mnoży się wartość argumentu ulong o stałą wartość dwóch, podczas gdy przy użyciu CreateTypeField
drugi czynnik jest pobierany z zainicjowanego przez konstruktora pola statycznego MyField
. Aby uzyskać realistyczne wyniki, wyniki
są kumulowane w pętli for.
Oto wyniki pomiarów (.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
Oto mój kod reproducer:
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);
}
}
}
Więc moje pytanie brzmi: Dlaczego wolniej?
To właśnie zaproponowałem w ostatnim akapicie. Nie statyczne, pola instancji klasy, będziesz mieć tylko jedno wystąpienie obiektu klasy. Będziesz musiał użyć go w swoich wywołaniach ILGenerator.Emit(). I użyj GCHandle.Alloc(), aby upewnić się, że pozostanie przy życiu, dopóki kod nie zostanie zebrany. –
Myślę, że ograniczenie rdzenia jest takie, że jitter nie może bezpiecznie założyć, że zmienna statyczna zostanie zniszczona lub zresetowana po zebraniu zespołu kolekcjonerskiego. Pozostawienie odniesienia do obiektu klasy, którego kod zniknął, jest katastrofalne i możliwe do wykorzystania. Coś w tym stylu. "Dlaczego" nie pomoże, oczywiście, w rozwiązaniu twojego problemu. –
Dobra odpowiedź. Wielkie dzięki. – Paebbels