C
C#

help

Speed up reflection-based comparison

LLPeter199710/30/2022
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? 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
Ccanton710/30/2022
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
LLPeter199710/30/2022
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 when )
Ccanton710/30/2022
Nit: if you do Expression.Lambda<Func<...>>(...), you don't then need to cast the result of lambda.Compile()
LLPeter199710/30/2022
Oh I didn't know that, thanks
Ccanton710/30/2022
What's the need for the Unsafe.As? Also, you call Unsafe.As once for each field, rather than once
LLPeter199710/30/2022
I 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
Ccanton710/30/2022
Ah, I see. I'd use Expression.Convert there
LLPeter199710/30/2022
So no need for the typecheck each time
Ccanton710/30/2022
But even so, you can store the result of Unsafe.As in a variable
LLPeter199710/30/2022
I 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 pepehands
Ccanton710/30/2022
Expression.Block.
var castParam1 = Expression.Variable(...);
...
var blockMembers = new Expression[]
{
Expression.Assign(castParam1, unsafeAs),
Expression.Assign(castParam2, unsafeAs),
comparisonsConjuncted,
};
var block = Expression.Block(new[] { castParam1, castParam2 }, blockMembers);
var lambda = Expression.Lambda<...>(block, new[] { param1, param2 });
var castParam1 = Expression.Variable(...);
...
var blockMembers = new Expression[]
{
Expression.Assign(castParam1, unsafeAs),
Expression.Assign(castParam2, unsafeAs),
comparisonsConjuncted,
};
var block = Expression.Block(new[] { castParam1, castParam2 }, blockMembers);
var lambda = Expression.Lambda<...>(block, new[] { param1, param2 });
(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 well
LLPeter199710/30/2022
Does it implement them (locals) with closures or something
Ccanton710/30/2022
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)
LLPeter199710/30/2022
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
private static AsmGetHashCodeDelegate CreateHashCodeFunc(Type asmType)
{
var getTypeMethod = asmType.GetMethod(nameof(GetType))!;
var toHashCodeMethod = typeof(HashCode).GetMethod(nameof(HashCode.ToHashCode))!;

var param = Expression.Parameter(typeof(IAsyncStateMachine));

var hashCode = Expression.Variable(typeof(HashCode));
var asmAsConcreteType = Expression.Variable(asmType);

var blockExprs = new List<Expression>();
blockExprs.Add(Expression.Assign(hashCode, Expression.Default(typeof(HashCode))));
blockExprs.Add(Expression.Assign(asmAsConcreteType, CastToConcreteType(param, asmType)));

foreach (var field in GetRelevantFields(asmType))
{
blockExprs.Add(Expression.Call(
instance: hashCode,
methodName: nameof(HashCode.Add),
typeArguments: new[] { field.FieldType },
arguments: Expression.MakeMemberAccess(asmAsConcreteType, field)));
}

blockExprs.Add(Expression.Call(
instance: hashCode,
method: toHashCodeMethod));

var block = Expression.Block(blockExprs);
var lambda = Expression.Lambda<Func<IAsyncStateMachine, int>>(block, param);

return new(lambda.Compile());
}
private static AsmGetHashCodeDelegate CreateHashCodeFunc(Type asmType)
{
var getTypeMethod = asmType.GetMethod(nameof(GetType))!;
var toHashCodeMethod = typeof(HashCode).GetMethod(nameof(HashCode.ToHashCode))!;

var param = Expression.Parameter(typeof(IAsyncStateMachine));

var hashCode = Expression.Variable(typeof(HashCode));
var asmAsConcreteType = Expression.Variable(asmType);

var blockExprs = new List<Expression>();
blockExprs.Add(Expression.Assign(hashCode, Expression.Default(typeof(HashCode))));
blockExprs.Add(Expression.Assign(asmAsConcreteType, CastToConcreteType(param, asmType)));

foreach (var field in GetRelevantFields(asmType))
{
blockExprs.Add(Expression.Call(
instance: hashCode,
methodName: nameof(HashCode.Add),
typeArguments: new[] { field.FieldType },
arguments: Expression.MakeMemberAccess(asmAsConcreteType, field)));
}

blockExprs.Add(Expression.Call(
instance: hashCode,
method: toHashCodeMethod));

var block = Expression.Block(blockExprs);
var lambda = Expression.Lambda<Func<IAsyncStateMachine, int>>(block, param);

return new(lambda.Compile());
}
System.InvalidOperationException: 'variable '' of type 'System.HashCode' referenced from scope '', but it is not defined'
Ccanton710/30/2022
You need to give it the list of variables used in the block as well
LLPeter199710/30/2022
Oh crap yes the block
Ccanton710/30/2022
So Expression.Block(new[] { hashCode, asmAsConcreteType }, ...)
LLPeter199710/30/2022
Yea yea Ok, this works, thanks Weird design, since we are working with symbols effectively already
Ccanton710/30/2022
It makes compilation easier I think?

Looking for more? Join the community!

Want results from more Discord servers?
Add your server
Recommended Posts
how to authenticate visual studio to Azure devops private nuget feed?I have tried adding it through visual studio and logging in with my Microsoft account but it does noI'm trying not to repeat the same symbol on the calculator [Answered]I am trying to write this so that the same symbol is not repeated in the calculatorDependency injection with multi projectHi I have asp.net project and a console project which is in the same solution. I would like to launcpublic static variable not changingif (choice == 'H' && coinFlip == 1 || choice == 'T' && coinFlip == 2) { AutoMapper - map object from source to destionation's list of objects.Any way I can map source object (e.g string) inside destination list of objects (e.g List<string>) uBest csharp and framework related books?Not really a question specific about programming but... please write your recommended c# books below[git] Push a directory as a new commitI am thinking of is it possible to create a new directory and push it as a commit to an existing repThings require preview features... despite having enabled preview features [Answered]I'm getting an error saying that a type requires opting into preview features because it's from an aWhat is meant by copy on build.I'm following a stackoverflow answer there it mentioned like set them to copy on build. How to do itMiddleware to modify urlI'm trying to modify my HttpClient so it modifies a url before sending. I think I see how to do it bMediatR and CQRS for basic logicSo started doing abit of MediatR CQRS pattern in abit of Clean Architecture. I've been doing the Quechoosing Update softwareWhat update software do you all use for console apps? Looking for a solution to pushing remote updatpassing license keys to a unit testI would like to use a 3rd party DLL which needs a license key passed in, however I cannot find a wayAutoMapper - Ignore 'SourceMember' for specific typeHey folks During the development of our API, I stumbled upon a roadblock I wish to overcome. We haScale not changing UNITYThe mesh localScale doesent change except if I remove all of the objects first and run it again whicTestcontainers must override configuration before it runs the Web API, but it does the opposite.I'm trying to create an integration test for a Web API whose communication is based on Redis. The any thoughts why this error ?Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array. ❔ ASP.NET Core MVC Put Value to Route on Form SubmitHi, I have this action on `Home` controller ```cs public async Task<IActionResult> Index(EventsFiltHow to call a static method form interface in a generic class.So I have a class as bellow. ```cs public interface ISetup<T, P> where T : class { public staticCounting Sort, display the arrayI need to display the array which counted how many each numbers there are like in the screen