4 ms·
Interesting that d and nim get better performance than c itself?? I woudl expect d, rust, nim, c, and zig, to all have equivelant performance since they use th
by earenndil 8y ago
Interesting that d and nim get better performance than c itself??
I woudl expect d, rust, nim, c, and zig, to all have equivelant performance since they use the same calling convention as c...
- rurban 8y agoHigher-level languages know more about the context and the stack. You also need a good inliner. You can do fast calls when you know exactly what registers you need to save and which not. With pure generic C you need to save most, with a more high-level language only those really needed, or even none, when you use the stack instead. Common-Lisp e.g. usually leads the ffi benchmarks.
- deleted 8y ago[deleted]
- BeeOnRope 8y agoHmm? Not really. In any case C or higher level language the caller knows what registers are in used and need saving (to the extent they intersect with the ABIs clobbered register set). In any case the caller usually doesn't know what registers the callee will use and so has to rely on the ABI. Of course if things can be inlined its a different story, but this is about FFI, i.e., no inlining.
- rurban 8y agoYes. But several VM's/ABI's don't clobber the regs by themselves (i.e. all locals are volatile), thus the external C func can use all the regs they want. Hence the intro doesn't need to save much. E.g. this also helps with GC pauses to scan registers, not needed there. We had great success with such an ABI (potion, a jitted lua variant). Native compilers with a better ABI than C has also advantages: Fortran (no need to save the return ptr), Common Lisp (mult. return values, can tune the FFI much better than usual ffi's), Haskell. The biggest problem is still callback, calling functions from the FFI back to us. This is costly. The FFI decl. needs to know that. And closures (local upvalues on the stack) are only properly supported with Haible's avcall. And some newer VM's solved the expensive FFI or C extension problem by redoing the external C part, such as pypy or Graal. They convert the called C function to their own format, and as such can inline it or know the used registers.
- BeeOnRope 8y agoI'm not following you. Clearly in a pure JIT environment or with custom calling conventions you can do better than a typical "blind" C call restricted by the platform ABI - but we're talking about FFI calling into a "plain" shared object, right? Such functions obey the platform ABI, so the caller needs to save exactly every callee-clobbered register they are using. Also, if the external C function wants to use any callee-saved registers, it needs to save them. It doesn't know that it's being called by a VM that allows FFI functions to clobber everything (if that's what you're saying). So yeah, there is all kinds of great stuff you can do with respect to function calls within a VM, or when you are working outside the platform C ABI (e.g., if you invented your own ABI), but it isn't clear how that relates to making fast FFI calls from higher level languages.
- jorams 8y agoI believe you're misreading the numbers. They're the "elapsed time in millis", so lower is better. The only result better than c itself seems to be luajit, which is quite impressive. I wonder how it achieves that.
- earenndil 8y agoAhhh oops you're right. Point still stands, though -- I wonder why those languages should have different results?
- masklinn 8y agoThey're both GC'd languages so the FFI may have some additional work/bookkeeping to do around the native calls, which C or Rust does not.
- dom96 8y agoOP poses a good question and I don't think it's a simple case of GC vs. no GC. In Nim's case it might have something to do with not using the C int types (PR already open to fix this: https://github.com/dyu/ffi-overhead/pull/6 https://github.com/dyu/ffi-overhead/pull/6)