Optimizing reflection in C# via dynamic code generation
--
I have recently been spending a lot of time working on ComputeSharp, a .NET Standard 2.1 library written in C# 8.0 that lets you run code in parallel on the GPU through DX12 and dynamically generated HLSL compute shaders. That is a somewhat unintuitive description for a library that does something conceptually simple: it runs code in parallel in a manner similar to Parallel.For, but on the GPU instead of on the CPU.
The library works as follows: just like Parallel.For it takes an Action<T> instance representing the code you want to run on the GPU, it then decompiles it to generate the shader code to be compiled and executed on the GPU, and inspects it to identify all the variables that the code is accessing. Once these values have been discovered, they need to be properly loaded so that the GPU can access them and the shader can finally be executed.
This post is a case study into some of the technical challenges I faced when writing the code that handles the variables extraction and loading, and how I managed to get a >20x performance improvement when moving from reflection to dynamic code generation. It is not meant to be a tutorial to follow step by step, but more of a summary of how I approached this problem and eventually solved it. I have learnt a lot while working on this library, and here I just want to share some of that knowledge and hopefully give new ideas to other developers working on their personal projects. After all, I got the idea to use dynamic code generation while going through this Reddit post, and I would really like people reading this post to find the same inspiration I had to start learning about some new topic I had never really explored before.
I will start by introducing the issue at hand, and I will generalize it so that we will not need to care about the specifics of my library or its codebase. I will also be describing all the necessary details about how the C# compiler works and what it does behind the scenes, so that every C# developer will be able to understand what is going on at any time, even without previous experience in this particular area.
If you have never dealt with reflection or dynamic code generation before, welcome to the magical world of…