Inlining is a simple process of, essentially replacing a method call with the contents of the call, for example in C++ we might have something like
class Math {
public:
inline int square(int x);
};
inline int Math::square(int x) {
return x * x;
}
Math m;
int y = m.square(5);
where the square method might be compiled down to
int y = 5 * 5;
This post isn’t about C++ inlining but there are similarities with C# in that a method may be implicitly inline, i.e. no need for the inline keyword and where we might request a method to be inlined – the compiler in this case may or may not fulfil the request.
In C# the same implicit inlining can be seen with code such as
public static int Square(int x) => x * x;
and again we can request or hint to the JIT to inline – this is the key thing to take away from this post, we can request inlining but essentially the compiler will make the decision on whether it should inline our code.
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Square(int x) => x * x;
What cannot be inlined?
- Methods which are too large, i.e. a method body which exceeds JITA internal size threshold.
- Virtual/interfaces calls cannot be inlined unless the JIT can de-virtualize them.
- Generic methods with unresolved types.
- try/catch/finally blocks are no eligible for inlining
- async/iterator methods i.e. async/await and yield return both compile to a state machine which cannot be inlined.
- Methods with lock keyword, this essentially compiles to Monitor.Enter/Monitor.Exit with try/finally and hence cannot be inlined.
- Methods with stackalloc cannot be inlined.
- Methods with unsafe and some other pointer operations cannot be inlined.
- Might seem obvious but marking your methods as MethodImplOptions.NoInlining will not allow this method to be inlined.
- P/Invoke and extern methods.
- Inlining may be disabled for debug builds, and other compiler optimizations