// File RTCG3.cs --- calling generated methods that contain loops // sestoft@itu.dk * 2002-09-18 // The benchmark results are strange, but reproducible: // RTCG method MyMethod1 takes 3.9 sec for 1 bn iterations if // MyMethod3 is not called, and only 2.5 sec if MyMethod3 is called, // even if it is called only *after* MyMethod1 has run to completion. // More precisely, if the compiler can statically determine that // MyMethod3 *cannot* be called, then MyMethod1 is slow. If MyMethod3 // *might* be called (but even if it isn't) then MyMethod1 is fast. // This phenomenon is reproducible, and observed under .Net 1.0 both // with and without SP2. using System; using System.Reflection; using System.Reflection.Emit; class RTCG3 { public static void Main(String [] args) { int count = int.Parse(args[0]); AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = "myassembly"; // Build: run-only assembly AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); // Build: module mymodule ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("mymodule"); // Build: public class MyClass { ... } TypeBuilder typeBuilder = moduleBuilder.DefineType("MyClass", TypeAttributes.Class | TypeAttributes.Public, typeof(Object)); { // Build: public static void MyMethod1(int x) { ... } MethodBuilder methodBuilder = typeBuilder.DefineMethod("MyMethod1", MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(int) }); // Obtain an IL generator to build the method body ILGenerator ilg = methodBuilder.GetILGenerator(); ilg.EmitWriteLine("MyClass.MyMethod1() was called"); Label start = ilg.DefineLabel(); ilg.MarkLabel(start); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Sub); ilg.Emit(OpCodes.Starg_S, 0); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Brtrue, start); ilg.Emit(OpCodes.Ret); } { // Build: public static void MyMethod2(int x) { ... } MethodBuilder methodBuilder = typeBuilder.DefineMethod("MyMethod2", MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(int) }); // Obtain an IL generator to build the method body ILGenerator ilg = methodBuilder.GetILGenerator(); ilg.EmitWriteLine("MyClass.MyMethod2() was called"); Label start = ilg.DefineLabel(); ilg.Emit(OpCodes.Ldarg_0); ilg.MarkLabel(start); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Sub); ilg.Emit(OpCodes.Dup); ilg.Emit(OpCodes.Brtrue, start); ilg.Emit(OpCodes.Pop); ilg.Emit(OpCodes.Ret); } { // Build: public static void MyMethod3(int x) { ... } MethodBuilder methodBuilder = typeBuilder.DefineMethod("MyMethod3", MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(int) }); // Obtain an IL generator to build the method body ILGenerator ilg = methodBuilder.GetILGenerator(); ilg.EmitWriteLine("MyClass.MyMethod3() was called"); Label start = ilg.DefineLabel(); ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Stloc_0); ilg.MarkLabel(start); ilg.Emit(OpCodes.Ldloc_0); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Sub); ilg.Emit(OpCodes.Stloc_0); ilg.Emit(OpCodes.Ldloc_0); ilg.Emit(OpCodes.Brtrue, start); ilg.Emit(OpCodes.Ret); } Type ty = typeBuilder.CreateType(); Timer t1 = new Timer(); ty.GetMethod("MyMethod1").Invoke(null, new object[] { count }); Console.WriteLine("Generated IL method 1: " + t1.Check() + " sec"); Timer t2 = new Timer(); ty.GetMethod("MyMethod2").Invoke(null, new object[] { count }); Console.WriteLine("Generated IL method 2: " + t2.Check() + " sec"); Timer t3 = new Timer(); ty.GetMethod("MyMethod3").Invoke(null, new object[] { count }); Console.WriteLine("Generated IL method 3: " + t3.Check() + " sec"); Timer t4 = new Timer(); YourMethod(count); Console.WriteLine("Compiled C# method: " + t4.Check() + " sec"); } public static void YourMethod(int n) { do { n--; } while (n != 0); } /* CIL code for YourMethod: .method public hidebysig static void YourMethod(int32 n) cil managed { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: sub IL_0003: starg.s n IL_0005: ldarg.0 IL_0006: brtrue.s IL_0000 IL_0008: ret } // end of method Test::YourMethod */ } // Crude timing utility ---------------------------------------- public class Timer { private DateTime start; public Timer() { start = DateTime.Now; } public double Check() { TimeSpan dur = DateTime.Now - start; return dur.TotalSeconds; } }