Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we get an identity of a delegate?

Tags:

c#

.net

delegates

Is it possible to generate an idenitity of a delegate to distinguish it from other delegate? Think of this code:

Func<int, int, int> delegate1 = a, b => a + b;
Func<int, int, int> delegate2 = a, b => a + b;
Func<int, int, int> delegate3 = a, b => a - b;
let id1 = id(delegate1);
let id2 = id(delegate2);
let id3 = id(delegate3);
Assert(id1 == id2);
Assert(id1 != id3);

The problem I want to solve is, I want to cache some JIT compiled GPU code in .NET. To make it easy to use, I want to let user send the delegate, and if the delegate is same, we try to find out the GPU code from a cache, rather than JIT compile it everytime:

Parallel(outputA, inputA1, inputA2, a, b => a + b); //should JIT compile GPU code, and cache it by its identity
Parallel(outputB, inputB1, inputB2, a, b => a + b); //should NOT JIT compile GPU code, and use the cached GPU code by its identity

One possible solution is to compare the expression string, but it still has problem to catch the clouser, such as:

int c = 0;
Expression<Func<int, int, int>> delegate1 = (a, b) => a + b + c;
c += 1;
Expression<Func<int, int, int>> delegate2 = (a, b) => a + b + c;
Expression<Func<int, int, int>> delegate3 = (a, b) => a - b - c;
Console.WriteLine(delegate1);
Console.WriteLine(delegate2);
Console.WriteLine(delegate1.ToString() == delegate2.ToString());
Console.ReadKey();

As pointed out thanks to @SWeko and @Luaan, in the above example, delegate1 and delegate2 are actually the same. But the purpose of caching the delegate is in the following usage:

int c = 1;
Parallel(outputA, inputA1, inputA2, (a,b) => a+b); //do JIT compile of GPU code
c += 1;
Parallel(outputB, inputB1, inputB2, (a,b) => a+b); //as the delegate is same then the previouse one, it will not do JIT GPU code compiling, but then, that is wrong!
like image 234
Xiang Zhang Avatar asked Oct 05 '15 07:10

Xiang Zhang


People also ask

What is a identifier delegate?

Type Identifier. Delegates are configured to apply to specific object types. A type identifier identifies a business object type supported by a repository type.

Can delegates be private?

By default, delegates do not have access to private items, but you can change this option when assigning Delegate permissions. Sharing your folders and assigning permissions must be done explicitly.


2 Answers

One (relatively naive) approach would be to use Expression<Func>s instead of Func's themselves, as they have much more detailed info, that allows you to analyze stuff. E.g.

Expression<Func<int, int, int>> delegate1 = (a, b) => a + b;
Expression<Func<int, int, int>> delegate2 = (a, b) => a + b;
Expression<Func<int, int, int>> delegate3 = (a, b) => a - b;
var id1 = id(delegate1);
var id2 = id(delegate2);
var id3 = id(delegate3);
Debug.Assert(id1 == id2);
Debug.Assert(id1 != id3);

where id is as trivial as:

public static string id(Expression<Func<int, int, int>> expression)
{
    return expression.ToString();
}

passes the tests.


Note that this is not a complete solution, and has lots and lots of issues. If you need a comprehensive comparison, you'd need to take into account the full nature of the expression, including (but not limited too) types going in and out of the expression, methods calls, closure access, etc, etc.. I don't think this is solvable at all in the general case, but usually it can be restricted to some more specialized case, that can be solved.

like image 145
SWeko Avatar answered Oct 09 '22 12:10

SWeko


You need to work at the Expression level here. The C# compiler does not guarantee that identical lambdas end up with the same delegate object. This optimization is not being performed right now but there is a GitHub issue. Even if it is performed it will work one assembly at a time which might not be enough for you. If the delegate captures closure values then this will not ever work.

I once did this in order to cache LINQ 2 SQL compiled queries automatically given a query. It is not trivial to compare expression trees. ToString is not full fidelity and it is slow. You will need to write your own comparer class. I think there's code for that on the web as a starting point.

As an example, when you compare constant expressions you can't just do ReferenceEquals(val1, val2). You have to actually special case many types such as boxed primitive types. They can have the same value but boxed with different object identities.

You also will need to write a hash code function because you probably want to lookup cache results using a hash table.

You also will get GC problems because expression trees can hold onto arbitrary objects. Basically, a closure can randomly hold onto local variables in unexpected ways. So what I did was to sanitize trees before caching them. I deleted all constants from them that were not safe.

Is it possible to generate an identity of a delegate to distinguish it from other delegate?

Yes, but it involves manually comparing and hashing the expression tree.

like image 44
usr Avatar answered Oct 09 '22 10:10

usr