5 ms·
For those wondering why luajit is faster than C/C++/Rust, that's probably because it does direct calls to the function, while C/C++/Rust go through the PLT (pro
by glandium 8y ago
For those wondering why luajit is faster than C/C++/Rust, that's probably because it does direct calls to the function, while C/C++/Rust go through the PLT (procedure linkage table).
I don't have luajit to validate this, but changing the C test to dlsym() the symbol first and use that in the loop instead makes the test take 727ms instead of 896ms when going through the PLT on my machine.
Edit: confirmed after installing luajit, got the same time as the modified C test.
- fpgaminer 8y agoThank you. I ran both through lldb to see why luajit was faster and was confused how luajit was faster despite doing quite a bit more work: luajit loop: 0x12a0effe0: movl %eax, %ebp 0x12a0effe2: movl %ebp, %edi 0x12a0effe4: callq *%rbx 0x12a0effe6: movsd 0x8(%rsp), %xmm0 ; xmm0 = mem[0],zero 0x12a0effec: xorps %xmm7, %xmm7 0x12a0effef: cvtsi2sdl %eax, %xmm7 0x12a0efff3: ucomisd %xmm7, %xmm0 0x12a0efff7: ja 0x12a0effe0 c_hello loop: 0x100000ea0 <+80>: movl %ebx, %edi 0x100000ea2 <+82>: callq 0x100000ee6 ; symbol stub for: plusone 0x100000ea7 <+87>: movl %eax, %ebx 0x100000ea9 <+89>: cmpl %r14d, %ebx 0x100000eac <+92>: jl 0x100000ea0 ; <+80> 0x100000ee6 <+0>: jmpq *0x134(%rip) I'm still a bit surprised that that extra jumpq is more expensive than all those extra instructions luajit is executing. EDIT: For those curious, Lua handles all numbers as floating-point; there are no ints in Lua. That's what all the extra instructions are for. Interestingly it looks like LuaJIT is storing the loop variable x as int (in the eax register) and just converting it to floating-point for comparison against "count" each loop. Interesting bending of the rules there.
- glandium 8y agoSo, running under perf stat tells me the luajit version runs very much more instructions (5M vs. 3M with my patched C version), but they run in the same number of cycles (2.5M), with the luajit version using more instructions per cycle. The unpatched C version uses 3.5M instructions, and 3.5M cycles, which is half the number of instructions per cycle compared to the luajit version.
- viraptor 8y agoHow did you patch it? Just compile with "-fno-plt" or something more?
- glandium 8y agodiff --git a/hello.c b/hello.c index 725d5ec..b7b1745 100644 --- a/hello.c +++ b/hello.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE +#include <dlfcn.h> #include <stdio.h> #include "newplus/plus.h" @@ -7,8 +9,9 @@ void run(int count) long long end; int x = 0; + int (*_plusone)(int) = dlsym(RTLD_DEFAULT, "plusone"); while (x < count) - x = plusone(x); + x = _plusone(x); printf("%lld\n", current_timestamp() - start); } And you need to add -ldl to the link flags. Edit: but yeah, -fno-plt works too.
- ufo 8y agoLua 5.3 introduced actual 64 bit integers but those probably will never get in LuaJIT because its deeply married to the NaN tagging trick, which limits unboxed values to aprox 48 bits.
- TeMPOraL 8y ago> For those curious, Lua handles all numbers as floating-point; there are no ints in Lua. Same as in JavaScript. I'm surprised by that. On the surface, having just floats and no ints sounds like a pretty dumb idea. What am I missing? Is there a reasonable rationale for such choice?
- justincormack 8y agoThere are ints in Lua 5.3, and ffi ints in luajit. But generally, its simpler, why should people care about representations. Lua also let you compike as int only I think.
- TeMPOraL 8y ago> why should people care about representations Because ints are exact, while floats introduce errors with nearly every operation. Representations are important in programming - different ones have different consequences.
- mort96 8y agoWell, doubles can represent all the numbers a 52 bit int can, so there's not a huge reason to not use doubles for whole numbers. It also means that new programmers don't get confused when they do 1/3 and get 1, and the language can be significantly simpler when there's only one number type. Also, when you try to store numbers bigger than 2^52, doubles degrade relatively gracefully, while ints wrap around. There are definitely downsides too, but it's a trade-off, not an exclusively bad decision.
- mehrdadn 8y agoThis is what Visual C++'s __declspec(dllimport) is for. :-) On Windows, GCC also seems to handle it correctly, along with its own synonym __attribute__((dllimport)).
- AndyKelley 8y agoI believe this is equivalent to adding the `nonlazybind` attribute to an extern declaration - see http://llvm.org/docs/LangRef.html#function-attributes http://llvm.org/docs/LangRef.html#function-attributes > This attribute suppresses lazy symbol binding for the function. This may make calls to the function faster, at the cost of extra program startup time if the function is not called during program startup. I didn't find the zig issue for this feature so I created it: https://github.com/ziglang/zig/issues/1024 https://github.com/ziglang/zig/issues/1024 If we did the "Static control flow analysis" idea then zig would bench the same as the luajit/modified c test.
- jcelerier 8y ago> I don't have luajit to validate this, but changing the C test to dlsym() the symbol first and use that in the loop instead makes the test take 727ms instead of 896ms when going through the PLT on my machine. Isn't this what every C or C++ library that wraps another with dlopen / dlsym does ?