Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>It is most likely because the first invocation of the code was not jitted. I decided to look at the IL and they are virtually identical.</p> <pre><code>Func&lt;int, Foo&gt; func = x =&gt; new Foo(x * 2); Expression&lt;Func&lt;int, Foo&gt;&gt; exp = x =&gt; new Foo(x * 2); var func2 = exp.Compile(); Array.ForEach(func.Method.GetMethodBody().GetILAsByteArray(), b =&gt; Console.WriteLine(b)); var mtype = func2.Method.GetType(); var fiOwner = mtype.GetField("m_owner", BindingFlags.Instance | BindingFlags.NonPublic); var dynMethod = fiOwner.GetValue(func2.Method) as DynamicMethod; var ilgen = dynMethod.GetILGenerator(); byte[] il = ilgen.GetType().GetMethod("BakeByteArray", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(ilgen, null) as byte[]; Console.WriteLine("Expression version"); Array.ForEach(il, b =&gt; Console.WriteLine(b)); </code></pre> <p>This code gets us the byte arrays and prints them to the console. Here is the output on my machine::</p> <pre><code>2 24 90 115 13 0 0 6 42 Expression version 3 24 90 115 2 0 0 6 42 </code></pre> <p>And here is reflector's version of the first function::</p> <pre><code> L_0000: ldarg.0 L_0001: ldc.i4.2 L_0002: mul L_0003: newobj instance void ConsoleApplication7.Foo::.ctor(int32) L_0008: ret </code></pre> <p>There are only 2 bytes different in the entire method! They are the first opcode, which is for the first method, ldarg0 (load the first argument), but on the second method ldarg1 (load the second argument). The difference here is because an expression generated object actually has a target of a <code>Closure</code> object. This can also factor in. </p> <p>The next opcode for both is ldc.i4.2 (24) which means load 2 onto the stack, the next is the opcode for <code>mul</code> (90), the next opcode is the <code>newobj</code> opcode (115). The next 4 bytes are the metadata token for the <code>.ctor</code> object. They are different as the two methods are actually hosted in different assemblies. The anonymous method is in an anonymous assembly. Unfortunately, I haven't quite gotten to the point of figuring out how to resolve these tokens. The final opcode is 42 which is <code>ret</code>. Every CLI function must end with <code>ret</code> even functions that don't return anything.</p> <p>There are few possibilities, the closure object is somehow causing things to be slower, which might be true (but unlikely), the jitter didn't jit the method and since you were firing in rapid spinning succession it didn't have to time to jit that path, invoking a slower path. The C# compiler in vs may also be emitting different calling conventions, and <code>MethodAttributes</code> which may act as hints to the jitter to perform different optimizations.</p> <p>Ultimately, I would not even remotely worry about this difference. If you really are invoking your function 3 billion times in the course of your application, and the difference being incurred is 5 whole seconds, you're probably going to be ok.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload