4 ms·
Same use case as to why .NET has low/zero-cost FFI. This is similar, except more boilerplate and much, much slower.
by neonsunset 2y ago
Same use case as to why .NET has low/zero-cost FFI.
This is similar, except more boilerplate and much, much slower.
- pron 2y agoThe FFM downcalls in OpenJDK compile down to argument shuffling + a CALL instruction (in "critical" linker mode), i.e. the same machine code gcc/clang would generate for a call from a C program.
- neonsunset 2y agoThis 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).
- int_19h 2y agoWhat does it do if you need to pass a struct that contains another struct?
- pron 2y agoThe same, but you need to define the layout appropriately. The JEP covers the basics: https://openjdk.org/jeps/454 https://openjdk.org/jeps/454. As I explained in another comment [1], we didn't want to trade off performance or limit the runtime, so the API for describing native layouts is more elaborate. [1]: https://news.ycombinator.com/item?id=40303242 https://news.ycombinator.com/item?id=40303242
- pjmlp 2y agoI still have some hopes that it will evolve towards a P/Invoke like experience. While a step closer to Valhala, the whole dev experience is still quite lacking versus what .NET offers. Currently is too much like making direct use of InteropServices.
- pron 2y ago> I still have some hopes that it will evolve towards a P/Invoke like experience. Doubtful, given that this is something we worked hard to avoid. To be efficient, a P/Invoke-like model places restrictions on the runtime, which inhibits optimisation and flexibility and this cost is worth it only when native calls are relatively common. In Java they are rare and easily abstracted away, so we opted for a model that offers full control without giving up on abstraction, given that only a very small number of experts (<1%) would directly write native calls and then hide them as implementation details. I'm not saying this approach is the right one for all languages, but it's clearly the right one for Java given the frequency of native calls and who makes them. Of course, you can wrap FFM with a higher-level P/Invoke-like mechanism, but it won't give you as much control.
- pjmlp 2y agoWell, for developers like myself that feel at home with JNI, the current development experience, even with jpackage, is too much to ask for. I will rather keep writing C++ with JNI, instead of enduring the current boilerplate, specially if I already need to manually create header files to feed into jpackage, for basic stuff like struct definitions, which I don't feel like writing by hand. As for performance, this is something I agree with neonsunset, unless we see Techpowerbenchmarks level of Panama beating P/Invoke, it is pretty much theoretical stuff at the expense of developer convience.
- _old_dude_ 2y agos/jpackage/jextract/g
- pron 2y agoWe can't tailor every feature to the widely disparate preferences of so many developers nor do we try to convince every last developer of the merit of our approach -- this is both impractical and a losing strategy. Rather, we rely on our experience designing a highly successful language and platform, and consult with companies -- each employing thousands of Java developers -- and authors of some of the most popular relevant Java libraries to ensure that we meet their requirements. Of course, we also look at what other languages have done and the tradeoffs they've accepted (some of which may not be appropriate for Java [1]), but there are always many possible designs and we don't adopt one from a less successful language just because it, too, has its fans. I would encourage those who think that we're consistently making suboptimal choices for Java compared to choices made by significantly less successful languages to consider whether it is possible that their preferences are not aligned with those of the software market at large. Java is and aims to continue being the world's most popular language for serious server software, and that requires tailoring designs to a very large audience. I always notice a certain lack of respect on forums such as HN for the world's most consistently successful and popular languages -- JS, Java, and Python. Different programmers have different preferences and I'm all for rooting for the underdog now and again, but you simply cannot consistently make wrong decisions over a very long period of time and yet consistently win. What we do may not be everyone's cup of tea (no language is), but it is clearly that of a whole lot of people. We work to offer value to them. [1]: E.g. the design of native interop has significantly impacted that of user-mode threads (or lack thereof: https://github.com/dotnet/runtimelab/issues/2398 https://github.com/dotnet/runtimelab/issues/2398) in both .NET and Go, and we weren't willing to make such tradeoffs in either performance or programming model.
- trelane 2y ago[flagged]
- miffy900 2y ago> This is similar, except more boilerplate and much, much slower. That's JNI, which really was truly terrible. Java 22 introducing FFM is finally an admission that JNI was crap and a dead end.
- peterashford 2y agoJNI worked it just wasnt as ergonomic as it could have been - which was on purpose. I disagree that Java should have discouraged use of JNI in that way but it was hardly "crap and a dead end"
- miffy900 2y ago> JNI worked it just wasnt as ergonomic as it could have been - which was on purpose There's a reason they're calling it the 'FFM' API and not JNI v2. The API devs were correct in rethinking the approach to native interop. This just proves my point; being crappy ON PURPOSE is why it's a dead end; it's very difficult to improve something that's been deliberately designed badly. Besides that, no Java dev in their right mind is going to continue to use JNI once they upgrade to Java 22 and realise FFM exists.
- peterashford 2y agoThere's no reason to use it now, yes. It wasn't a dead end because people absolutely could and were using it just fine (if not happily)
- pjmlp 2y agoI will keep using JNI, first of all because I cannot stand its boilerplate instead of having something nice like on .NET side, even jextract can't make up for it. Secondly, our libraries also land on Android applications, and lets see if FFM ever lands on ART.