A tip related to the throw inlining tip:
One way to get more consistent/effective inlining is to split the complex 'slow paths' out of your functions into helper functions. For example, let's say you have a cached operation with cache hit and cache miss paths:<p><pre><code> void GetValue (string key, out SomeBigType result) {
if (_cache.TryGetValue(key, out result))
return;
result = new SomeBigType(key, ...);
_cache[key] = result;
}
</code></pre>
In most scenarios this function might not get inlined, because the cache miss path makes the function bigger. If you use the aggressive inlining attribute you might be able to convince the JIT to inline it, but once the function gets bigger it doesn't inline anymore.<p>However, if you pull the cache miss out:<p><pre><code> void GetValue (string key, out SomeBigType result) {
if (_cache.TryGetValue(key, out result))
return;
GetValue_Slow(key, out result);
}
void GetValue_Slow (string key, out SomeBigType result) {
result = new SomeBigType(key, ...);
_cache[key] = result;
}
</code></pre>
You will find that in most cases, GetValue is inlined and only GetValue_Slow produces a function call. This is especially true in release builds and you can observe it in the built-in Visual Studio profiler or by looking at method disassembly.<p>(Keep in mind that many debuggers - including VS's - will disable JIT optimization if you start an application under the debugger or attach to it. You can disable this.)<p>This tip applies to both desktop .NET Framework and .NET Core, in my testing (netcore is generally better at inlining, though!) If you're writing any performance-sensitive paths in a library I highly recommend doing this. It can make the code easier to read in some cases anyway.