1

I'm in a situation where I have to work with linq expressions and I need to get the last bit of performance out of them. After much work, and implementing all the lower hanging fruit, I've reached a point where micro-optimizations are necessary.

Situation

interface IThing { void DoThing(); }

class A : IThing { ... } // implicitly implements IThing

Common use, interface field

// First put an instance into a IThing field
IThing x = new A();

// Then, in a different method:
x.DoThing(); 

This is commonly known to create an expensive interface call.

Optimized, explicit type

A x = new A();
// Then, in a different method:
x.DoThing();

This time DoThing can be called directly since the concrete type of x is always known (maybe A needs (or maybe it helps even more) to be sealed, not sure)

My Problem

Now, keeping the above in mind, I now want to do the same with System.Linq.Expressions.

I'm using Expression.Constant() right now to "capture" an IThing into my expression tree (which I later put into a Expression.Lambda and compile), and then call a method on it.

All works fine, but I'm not sure if there's a interface dispatch happening in the compiled code. If there is, I'd really like to get rid of it because I've had great success with this optimization in the past. That is, replacing interface calls in hot code paths with "normal" calls.

Questions:

  1. How can I ensure the call will not use an interface method lookup?
  2. How can I see what machine code gets compiled for the delegate? For some reason I have a hard time stepping into the delegate in the disassembly window.
  3. Is code generated by expression trees optimized in the same way by the JIT as normal code?
  4. Do I have to explicitly specify the type in the constant? Or is it automatically detected from the given object? Is there any difference?
  5. How does the "indirection" though the delegate affect these things? Does it maybe prevent some optimizations? Or can the jump maybe even be optimized out entirely in some situations (maybe if the delegate is in a readonly field or so?)
Riki
  • 1,776
  • 1
  • 16
  • 31
  • To second question: check [this](https://stackoverflow.com/a/4764705/4685428) answer. That approach allow you to inspect generated IL code – Aleks Andreev Jan 22 '19 at 16:06
  • Your interface requires `DoIt` your examples call `DoThing`? You might want to consider using LINQPad to write test code using explicit lambdas, it will show you how they translate both to `Expression` trees and to IL. For example, that can help you answer #4, and possibly #5. – NetMage Jan 22 '19 at 16:59
  • @NetMage that was a typo. sorry. i fixed it – Riki Jan 22 '19 at 17:15
  • Apparently calling through the interface does not change the IL instructions. Testing has shown that there is only a very small difference between an interface call and a non-interface call and it is in possible ML instruction differences - the IL code is identical. See [here](https://softwareengineering.stackexchange.com/questions/234473/how-are-virtual-methods-slower-in-c) – NetMage Jan 22 '19 at 19:49
  • 1
    For #4, `Expression.Constant(iThing)` and `Expression.Constant(A)` will result in the same type of constant. `Expression.Constant` uses the runtime type of its parameter. – NetMage Jan 22 '19 at 20:21

0 Answers0