Speed up reflection-based comparison
We have a type that we have no access to. Not now, never. And we can't replace it. We have a couple of private fields in it that we need to compare inside two instances to determine if they are equivalent for our purposes. Now the thing is, the place where the comparisons happen is super performance-critical. Is there a way to build up and "compile" a comparison function like this at runtime purely from reflection info to speed this up?
20 Replies
It could be that what I need is expression trees?
For reasons it would be nice not to use 3rd party here
@Diddy didn't you send replies here
Are you caching the FieldInfo etc? That's the first big win
Compiled expressions will give you a little performance boost on top of that
I guess the best course of action is to set up a test with BDN and see for myself
Diddy acting... weird again
No bueno
Impossible
Changes all the time and there are many-many different types
(They are the async state machines the functions generate)
Anyone interested, this is what I ended up with, performs ok: https://gist.github.com/LPeter1997/47886848e145fb61eb49c32344c03abf
(Note: the code since has been updated to accommodate release mode, but I don't think anyone else would need this ever )
Nit: if you do
Expression.Lambda<Func<...>>(...)
, you don't then need to cast the result of lambda.Compile()
Oh I didn't know that, thanks
What's the need for the
Unsafe.As
?
Also, you call Unsafe.As
once for each field, rather than onceI don't want to cast the member each time to the concrete state machine type, because I know it's that type anyway, and this is running in critical code
Ah, I see. I'd use
Expression.Convert
thereSo no need for the typecheck each time
But even so, you can store the result of
Unsafe.As
in a variableI know but I also don't know how to factor it out to only have to cast it once. I haven't used the expr tree in years and IIRC they were sort of for single expressions. Like I don't recall being able to declare locals
My knowledge is (likely) seriously outdated on this tho
Sadly in practice, my code will likely write a convert anyway, because in release mode, Roslyn generates struct state machines, which means
Unsafe.As
is illegal anyway Expression.Block
.
(and use castParam1
etc in comparisons
of course)
The last expression in a block is the return value. You need to give it the list of variables used in the block as wellDoes it implement them (locals) with closures or something
No. It translates to a BasicBlock of instructions
Just a set of things executed one after another
System.Linq.Expressions aren't always pure expressions 😛
(IIRC this stuff was added on in C# 4 for dynamic support)
I'll hammer my hash-code function into this form to test it first, that's simpler
Yeah this doesn't work, I'm getting the exception I kind of expected
I might be screwing something up tho
System.InvalidOperationException: 'variable '' of type 'System.HashCode' referenced from scope '', but it is not defined'
You need to give it the list of variables used in the block as well
Oh crap yes the block
So
Expression.Block(new[] { hashCode, asmAsConcreteType }, ...)
Yea yea
Ok, this works, thanks
Weird design, since we are working with symbols effectively already
It makes compilation easier I think?