❔ How can I provide a safe zero cost abstraction over passing managed function pointers over FFI
I have the following:
Benchmark:
25 Replies
How can I get the same performance as passing in a
delegate* managed<int, void>
without requiring consumers to be unsafe?Got redirected here from silk - "zero cost" basically comes down to JIT eliminating whatever extra code you've written - not super sure what exactly you're asking, also not super sure what you're asking. Not even super sure how those benchmarks relate to the code given tbh
I want to be able to pass a callback function from a safe managed context over FFI without these insane overheads, here is the benchmark I used:
Test.Callback(1234, &Callback);
is perfect other than the fact the user consuming this static method has to be an unsafe context to give me a delegate* managed<int, void>
is there a way to take a managed delegate and convert it to a function pointer without the huge overheads?i don't think it will be safe to send a
managed
function pointer away
unless you only intend to invoke it in a managed context later
but regardlesswith the
delegate* managed<int, void>
it only works against static methods which makes it safe to execute from native codeit can only be executed with the managed calling convention
I just wish there was an easy way to get the same benefit without the consumer having to dip into unsafe code
which you would have to match in native
it currently works with this rust code and executes fine:
because the runtime probably uses the same calling convention as your rust code
that's not always a given
that's why it's safer to force it to something like Cdecl
so I should pass it as
delegate* unmanaged[Cdecl]<int, void>
?yes
the problem is that then the Callback has to be annotated with
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
Marshal.GetFunctionPointerForDelegate
creates a stub for youyeah but there seems to be a pretty beefy overhead to using it
also I thought the default calling convention for unmanaged callers only was cdecl? is it not?
may be
ok, it is not:
If omitted, the runtime will use the default platform calling convention.
yeah just checked
so you either use Marshal to get a stub, or you use unsafe and function pointers
I guess I will have to use unsafe then as the overhead for the stub is too big for the use case (interacting with an ECS)
do you have to create the callback every time?
because your benchmark measures the cost of calling
Marshal.GetFunctionPointerForDelegate
, not just calling the function pointer it returnsyeah I was thinking if I went down that route I would just have some lookup to get the function pointer for the given function
some things to think about, thanks for the info guys
the most efficient way is to take an unmanaged function pointer and for the caller to use
UnmanagedCallersOnly
yeah I think this is the route I will go down
a shame the UnmanagedCallersAttribute is sealed, would have liked to just have a CdeclAttribute to default the calling convention
attribute and polymorphism are a bit wack though
especially when they probably work with source generators
these were added for AOT I think so I assume these are compile time
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.