Krumelur
Krumelur

Reputation: 32497

How can I inline my inner function?

TL;DR: Can I somehow create an algorithm that can use different functionality in the inner loop, and still get that "functionality" inlined, without resorting to copy/paste or if/else statements?

I am trying to create an algorithm that basically looks like this:

for(var i=0; i<big; i++) {
  for(var j=0; j<big2; j++) {
     // ... processing
     var x = SomeFunc(a, b, c);
     // ... more processing
  }
}

I want the algorithm to be run for a number of possible functions (SomeFunc above), each called a large number of times per run. Each SomeFunc function is very simple (usually an arithmetic expression).

Now, to get acceptable performance out of this algorithm, it is imperative that SomeFunc is inlined. However I fail to get the function inlined while still allowing for multiple functions.

I realize this means that the algorithm function has to be JITted multiple times, but I was hoping that a construct like this would work:

interface ISomeFunc {
   int SomeFunc(int a, int b, int c);
}

private sealed class SomeFunc1 : ISomeFunc {
   public int SomeFunc(int a, int b, int c) {
     return ....;
   }
}


private static void RunMyGenericAlgo<T>(T func) where T : ISomeFunc
{
    for ... for ..
       x = func.SomeFunc(a, b, c);
}

But it appears that the function call is not inlined since func above is called via the interface and not via the sealed class.

I also tried the obvious approach:

abstract class MyAlgo {
   protected abstract int SomeFunc(int a, int b, int c);
   public void Run() {
     // as RunMyGenericAlgo above
   }
}

sealed class MyAlgoSomeFunc1 : MyAlgo {
   protected override int SomeFunc(int a, int b, int c) {...}
}

and it did not inline either.

This program will however inline as desired (and runs about 50% faster):

class MyAlgo {
   int SomeFunc(int a, int b, int c) {...}
   public void Run() {
     // as above
   }
}

EDIT: To clarify, I also investigated using the MethodImpl attribute with AggressiveInlining, and it did not seem to help.

Is what I'm trying even possible in C# or do I have to copy/paste my algorithm for each implementation of my inner function?

Upvotes: 4

Views: 227

Answers (1)

Bas
Bas

Reputation: 27095

To allow a method to be inlined, the implementation must be constant (e.g. not dependant on variables). So any form of a virtual/abstract/interface/delegate call is by definition a call that can never be inlined. Therefore, the only way is to have a nonvirtual method call, or just paste the code in there.

There are some exceptions to this rule. For example, the JVM designers have the problem of all the Java methods being virtual by default, they have virtual call inlining. This will do something like:

//calling obj.MyVirtCall();
if (obj.Type == MyCommonType) {
    //inlined code for MyCommonType.MyVirtCall();
} else {
    obj.MyVirtCall();
}

Edit: You could used T4 templates to generate C# code for each override of Run in your algorithm example, not requiring duplicate code, however it could make the maintenance slightly more complex, having to also maintain a T4 template instead of just C# code.

Upvotes: 2

Related Questions