ltrzesniewski / InlineIL.Fody

Inject arbitrary IL code at compile time.
MIT License
240 stars 17 forks source link

Usage Suggestion #28

Closed Ephron-WL closed 2 years ago

Ephron-WL commented 2 years ago

This is a nice tool that I have used successfully. However, my usage required that I comment out the validation for the definition of locals and then removed the local index translation calls. I wanted to replace a single expression in a method that already had a lot of C#. My IL leveraged variables already existing. I used ILSpy to identify the index values. A proposed feature might be to allow a user to add a list of names of locals they already know to be present. Once the method is compiled we should be able to find the variable names in the compiled metadata and match up the names and identify the index from the locals metadata. I don't know enough about Fody or your assembly to know if this is possible, but I figure that Fody is an injector using Mono and works after the assembly has been compiled, but I could be mistaken and maybe this idea is too much work.

My usage is below. I thought that fewer instructions would be faster, it was about 15% slower, no doubt because of the types of opcodes I'm using. The compiler generated many more instructions, but most were arithmetic, which, I guess, is very fast compared with using the evaluation stack. I guess that is why it is the compiler we use, because it really does know how to optimize.

image

image

image

ltrzesniewski commented 2 years ago

Well, it looks like I need to clarify the documentation. See, instead of writing IL.Emit.Ldloc_S(10);, you should have written IL.Push(row);.

IL.Emit.Ldloc_S(10); (in your modified version of the lib) is very brittle, since any change to your code could change the index, and changing the compilation mode (debug or release) will change it as well.

A proposed feature might be to allow a user to add a list of names of locals they already know to be present. Once the method is compiled we should be able to find the variable names in the compiled metadata and match up the names and identify the index from the locals metadata.

This is not needed, as you're supposed to use IL.Push in the first place. But even though, C# variables don't map cleanly to IL locals. In release mode, the compiler tries to minimize the IL locals count. It will avoid declaring a local if it can use the evaluation stack instead, and I think it can also reuse a local slot for two separate C# variables if their lifetime doesn't intersect.

My usage is below. I thought that fewer instructions would be faster,

Not necessarily, as you noticed. The compiler generates IL, then the JIT generates the machine code from that. The thing is, the JIT implements pattern recognition over the IL, and it can better optimize the known/common IL patterns the compiler generates. The quality of the generated machine code is what matters in the end, not the IL instruction count.

I also noticed there are nop instructions in your screenshots, which means you're compiling in debug mode. You should never benchmark anything in debug mode, as optimizations are then disabled both in the compiler and in the JIT. Also, use BenchmarkDotNet to benchmark your code (it won't let you mistakenly benchmark in debug mode).

On a side note, you don't need to use the _S variants with InlineIL (or even _0 etc), as it will automatically emit the shortest instruction encoding for you.

Ephron-WL commented 2 years ago

Thank you for taking the time to address my experience and clarifying a few things.

I’ve implemented your suggestions.

When I benchmarked in release mode I found my manually optimized code to be faster, although a mere 3ms faster to 122ms; so not much faster. I do feel that my optimizations were better than the C# compiler, leading to the improved performance, but I don’t know if the effort was worth a mere 3ms improvement. Still, it was an excellent experience. I’ve always been fascinated by the compilation process and optimizations. Thank you for your tool and feedback.

ltrzesniewski commented 2 years ago

You're welcome, and thank you also for your feedback, it's valuable knowledge. 🙂