I’ve been following some C development going on in the community, and discussion around memory management and optimisations lead me to start thinking about how they might be emulated in a C# application.
Slight Digression
Now I know that you have to prioritise Correctness, Performance, Readability, and that C# by design makes things readable and productive sometimes at the sacrifice of performance, but since I am writing helper extensions for readability, it is worth considering their performance losses/gains, and if they could be generally better in any way.
Anyway, back to what I was saying, I wondered if there was a way to avoid extra stack allocations from function calls in c#. Inlining as it is usually called. And it turns out there is, so this brings me to an explanation of an attribute name MethodImplAttribute
.
MethodImplAttribute is an Attribute for your methods or constructors that allows you to provide hints to the compiler about how it JITs your code. It takes a flags enum MethodImplOptions with the following values
- AggressiveInlining
- ForwardRef
- InternalCall
- NoInlining
- NoOptimization
- PreserveSig
- Synchronized
- Unmanaged
Lets take a look what they are, and how and why you would use them.
AggressiveInlining
The method should be inlined if possible.
AggresiveInlining came in with .net 45. It’s purpose appears to be a compiler hint that it should really inline this method. I need to do some performance testing on this one to see if It meets my needs.
ForwardRef
The method is declared, but its implementation is provided elsewhere.
I found some information on stack overflow but my best understanding of this is that you would use it when you are compiling between languages, or implementing a language on top of IL. Probably less useful to the general C# developer.
InternalCall
The call is internal, that is, it calls a method that is implemented within the common language runtime.
Similar to ForwardRef
, probably not so useful to the general developer. I guess this functions as a proxy, similar to the concept of DllImport, except instead of an external c or c++ dll, it would be something built into the CLR.
NoInlining
The method cannot be inlined. Inlining is an optimization by which a method call is replaced with the method body.
The companion to AggressiveInlining
. This explicitly tells the compiler not to inline. There are certain edge cases where the compiler might decide to inline a method, and cause some strange behaviour, race conditions or memory quirks. In these cases, by applying this attribute you can work around such problems.
The other big usage of this again performance. The overheads of the JIT inlining such methods every time may actually be worse than having it just explicitly call particular methods. Maybe not the JIT, but the stack allocations could have similar performance implications. Providing this attribute can ensure that your method is never inlined.
NoOptimization
The method is not optimized by the just-in-time (JIT) compiler or by native code generation (see Ngen.exe) when debugging possible code generation problems.
NoOptimatisation came in in 3.5. This sounds like it again works around some edge cases that can occur due to incorrect decisions around optimising your code for you.
I found this interesting use case answer on stackoverflow that basically says you may not want your code to optimise out, for security reasons, to ensure a constant duration of your algorithm, even if it could be faster for a specific usage.
PreserveSig
The method signature is exported exactly as declared.
I Couldn’t find anything interesting about this one. Must be some situations where signatures can be changed by the JIT? Keen to hear from anyone who knows more about this one.
Synchronized
The method can be executed by only one thread at a time. Static methods lock on the type, whereas instance methods lock on the instance. Only one thread can execute in any of the instance functions, and only one thread can execute in any of a class’s static functions.
I guess this passes on to the compiler to ensure the locking code is put in place for you. I did read a warning on this though, that It is not recommended on public types, since anyone else could lock on your instance, deadlocks may occur.
Unmanaged
The method is implemented in unmanaged code.
As opposed to managed code, this tells the compiler how the method is implemented.
Reflection
Unlike normal attributes, this one doesn’t show up under MemberInfo.GetCustomAttributes
, but instead is found in MethodInfo.GetMethodImplementationFlags
for methods, and ConstructorInfo.GetMethodImplementationFlags
for constructors, in case you were interested in inspecting these at runtime.
Portable Class Libraries
PCL Profile328 is the profile that I use for my projects. It is basically compatibility with minimum versions and up .Net4.0, Silverlight 5, Windows 8, Windows Phone 8(Silveright), Windows Phone 8.1, and the Xamarin Andoid and iOS framework versions. Under this profile, I only has access to the NoInlining
and NoOptimization
enum values.
By unselecting Silverlight (Silverlight5 + Windows Phone Silverlight) (Profile92) it gives me PreserveSig
, and by also increasing .Net to version 4.5 (Profile111) I get AggressiveInlining
as well.
Your profile may vary, but if I (and you) want to use AggressiveInlining
then we need to ditch Silverlight, and .Net 4. I will need to run some experiments to see if the improvement is worth adding this to my library, and If it is, I will have to decide if I drop support from my library, or start adding #IFDEF
statements throughout my application. Tough choice.