3 ms·
This is what it is compiled to in .NET[0] today more or less[1]. What does OpenJDK compile these to? (edit: misread as could compile. Hmm, I wonder how much the
by neonsunset 2y ago
This is what it is compiled to in .NET[0] today more or less[1]. What does OpenJDK compile these to? (edit: misread as could compile. Hmm, I wonder how much the difference will there be in average FFI cost with newer APIs vs direct calls)
[0] Objects that need pinning are pinned(by toggling a bit in object header), byrefs are pinned by simply storing them on the stack, arguments that need marshalling involve calling corresponding marshalling code. That code can allocate intermediate data on heap, on stack or call NativeMemory.Alloc/.Free C-style.
[1] Overhead can be further reduced by 1. annotating FFI calls with [SuppressGCTransition] which saves on possible arguments stack spills and GC helper call, replacing the call with a single flag check and optional call into GC in epilog, 2. in NativeAOT, p/invokes can be "direct" which saves on initialization checks and indirections (though they are reduced in JIT as it can bake data directly into codegen after static init has finished on recompilation). This has a tradeoff as system's dynamic loader will be used at application startup instead of regular lazy initialization and 3. direct p/invokes can be upgraded to static linking, which transforms them into direct calls identical to regular C calls save for the same GC flag check in post-condition. This comes with compiling .NET executables and libraries into a single statically linked binary (well, statically linked for the native dependencies the user has opted into linking this way).