Fasterflect vs HyperDescriptor vs FastMember vs Reflection
The other day I had a small task to inspect return values of methods and if the following property exists then set it to empty array.
public long[] Achievements { get; set; }
This needed to happen once on every web request, and I decided to implement it as a PostSharp attribute. WHY I needed to do this is another interesting discussion, but broadly speaking boils down to assumptions baked into base class’s control flows no longer holds true but is inflexible to change.
A major refactoring is in order, but given we have a deadline to meet, let’s take a technical debt and make sure it’s in the backlog so we know to come back to it.
So I went about finding a more efficient way of doing this than hand-writing some reflection code.
There’s Jon Skeet’s famous reflection post but to make it work across all types and incorporating IL emit is more complex than this task warrants.
sidebar : if you’re still interested in seeing how Jon’s technique can be applied, check out FSharpx.Reflection to see how it’s used to make reflection over F# types fast.
Then there’s Marc Gravell’s HyperDescriptor although the Nuget package itself was authored by someone else. Plus Marc’s follow-up to HyperDescriptor — FastMember.
I also came across a package called Fasterflect which I wasn’t aware of before, from its project page the API looks pretty clean.
Test 1–1M instances of MyTypeA
Suppose I have a type, with the Achievements property that I want to set to empty whenever I see it returned by a method.
To do this with reflection is pretty straight forward:
With Fasterflect, this becomes:
I do like this API, it’s very intuitive.
And here’s how it looks with HyperDescriptor and FastMember:
Now let’s run this across 1M instance of MyTypeA and see how they do. Both Fasterflect and FastMember did really well although HyperDescriptor was 3x slower than basic reflection!
Test 2–1M instances of MyTypeA and MyTypeB
Ok, but since this code has to work across many types, so we have to expect both
- types that has this property, and
- types that don’t
To simulate that, let’s introduce another type into the test.
Instead of working with an array of MyTypeA, now we have a mixed array of both MyTypeA and MyTypeB for our test.
and the result over 1M objects makes for interesting readings:
some observations from this set of results:
- we need to invoke the setter on only half the objects, so reflection is faster (almost halved) than before, makes sense;
- both FastMember and HyperDescriptor are faster due to the same reason as above;
- having less work to do had much smaller impact on FastMember suggests some caching around the call site (and indeed it does);
- WTF is going on with Fasterflect!
Conclusions
The morale of this story is that — always verify claims against your particular use case.
Another perfect example of this is Kyle Kingsbury’s work with Jepsen. Where he uses a generative testing approach to verify whether NoSQL databases actually provide the consistency model that they claims to offer. His findings are very interesting to read, and in many cases pretty worrying…
Oh, and stick with FastMember or reflection