ILGenerator induction method

Given the following code:

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); } } } 

Method call. Built in. The loop ends in about 40 ms. If, for example, I make Do a virtual function, it will not be inserted, and the cycle will end in 240 ms. So far, so good. When I use ILGenerator to generate the Do (Echo) method and then generate DynamicMethod with the same loop as this main method, calling the Echo method never becomes inlined, and it takes about 240 ms to complete the loop. The MSIL code is correct, as it returns the same result as the C # code. I was sure that the inlining method is something that JIT does, so I see no reason to not include the Echo method.

Does anyone know why this simple method will not be built into JIT.

+6
source share
3 answers

After further investigation, I concluded the following:

  • Created by ILGenerator will never be inserted. It doesn't matter if you name them using a delegate from another DynamicMethod or from a method created using MethodBuilder.
  • Existing methods (those encoded in C # and compiled by VS) can only be embedded when calling a method created using MethodBuilder. If called from DynamicMethod, they will never be inserted.

I completed this after carefully testing many samples and examining the final assembler code.

+1
source

If I understand correctly, I assume that since the method is generated dynamically, the JIT compiler does not know to inline it for callers.

I wrote a lot of IL, but I did not consider nesting behavior (mainly because dynamic methods are usually fast enough for my purposes without further optimization).

I would like to welcome someone who is more knowledgeable on this issue to give feedback (please, not just down, I would like to know something here if I am wrong).

Non-dynamic

  • you write "normal" .NET code (for example, C #, VB.NET, any language that supports CLS).
  • IL is created at compile time
  • machine code is created at runtime; methods, as appropriate, are built-in

Dynamic

  • you are writing "normal" .NET code whose purpose is to create a dynamic method.
  • IL is created at compile time for this code, but a dynamic method is not created
  • machine code is generated at runtime for code that generates a dynamic method
  • when this code is called, a dynamic method is created as an IL in a special assembly
  • dynamic IL method compiled into machine code
  • however, the JIT compiler does not recompile other callers to embed a new dynamic method. Or perhaps other subscribers themselves are dynamic and not yet created.
0
source

You can try creating a dynamic assembly. I understand that most of the runtime does not know that it is dynamic. I think that inside it loads, like any other byte [] assembly. Therefore, JIT / inliner may also not know about it (or not care).

0
source

Source: https://habr.com/ru/post/904779/


All Articles