Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check usage of C#9 Function Pointers #279

Open
jogibear9988 opened this issue Nov 18, 2020 · 5 comments
Open

Check usage of C#9 Function Pointers #279

jogibear9988 opened this issue Nov 18, 2020 · 5 comments

Comments

@jogibear9988
Copy link
Contributor

I don't know, but could C#9 function pointers help to get more perfomance?

https://docs.microsoft.com/de-de/dotnet/csharp/language-reference/proposals/csharp-9.0/function-pointers

@ebfortin
Copy link

In theory yes. It use another IL opcode that calls the method instead of a virtual call to the Invoke() method behind. However in practice I wonder if the JIT can eliminate these virtual call to Invoke() and call the method directly.

@dadhi
Copy link
Owner

dadhi commented Dec 28, 2020

My main quuestion what should I call with the pointers or calli op-code.
My only idea is to use it for inviking the nested lambda or the constant delegate, e.g. Invoke(Lambda(...),...). It seems a very rare case to bother about.
Otherwise, for the method I am always trying to use Call if possible, which should be as fast.

@TYoungSL
Copy link
Contributor

TYoungSL commented Jan 16, 2023

You could replace all delegate Invoke method calls as well as lambda invokes if you wanted so long as the delegate was embedded as a constant. The JIT can devirtualize calli call sites and may even inline callis to managed functions in the future.

If the Delegate has a non-null Target (the MethodInfo member has IsStatic == false) the signature for calli is fn(<Target>, <arguments ...>) otherwise it's fn(<arguments ...>) ... the value to give to calli must be the address from MethodInfo.MethodHandle.GetFunctionPointer() but MethodHandle and/or GetFunctionPointer can throw due to platform implementation / limitations / various reasons, and you'd need to fall back to calling the Invoke member of Delegate.

It is an decent optimization, though you'd have to bench it to see the performance difference.

If you can eliminate the embedding and invocation of a delegate it is generally a big win compared to e.g. arithmetic and tail call optimizations. calli can benefit from tail call optimizations in some cases too.

@dadhi
Copy link
Owner

dadhi commented Jan 16, 2023

@TYoungSL Great.
Maybe you can provide a small example of the IL for the Expression.Invoke for the delegate constant expression?

If you can eliminate the embedding and invocation of a delegate it is generally a big win

Yes, but it is hard to do generally. So you need to have a fitting scenario for this. And I did not yet work with such.
Maybe you have an example in mind?

@TYoungSL
Copy link
Contributor

TYoungSL commented Jan 16, 2023

I'll give it a shot.

The use case I have we workaround by generating a dynamic static method with AggressiveInlining that does the calli; the JIT does a good job with that. The calli are to unmanaged code though.

The function pointer is constant but the arguments change, we have to create stubs for any new static delegates / function pointers we want a fast calli to.

We expand some delegate calls using Expression.Call on the MethodInfo and Target of the delegate before the FEC/Linq.Expressions even gets to see them. It wouldn't make sense to use calli there.

It might make more sense to translate some delegate Invoke call to a direct call or callvirt like that.

For managed calli to make sense the Delegate would have to be the type of an external ParameterExpression (not a local variable).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants