Live data from Hacker News

Building the fastest Lua interpreter automatically

sillycross.github.io

81–90 of 114 posts

Re: Building the fastest Lua interpreter automatically

#81
post #52

This sounds a lot like the approach taken by GraalVM. Can someone better versed in this area comment?

No, it's almost entirely unrelated to GraalVM's approach.

Not in the original scope, but truffle has a way to generate interpreter from a java annotated switch case. And it also generates the bytecode format/length, and if you do changes in the interpreter it can change the bytecode format again.

So I think it is related, if not even the same thing. Plus, GraalVM also does JIT out of box. And it has GC.

That said, it's not all rainbows and unicorns, GraalVM has higher memory usage and is only 10-15% faster in production usage. AFAIK, Twitter is using it in production.

Re: Building the fastest Lua interpreter automatically

#83
I work on a game, that mostly uses lua as logic code, saddling on a c/c++ engine. One of the engine developers implemented lua jit years ago and found that for the actual performance, the interpreter/jit was neglibable, the most expensive thing being the actual switch between luacode and c-code, which is with a large API a constant ugly background performance loss.

The lua code by itself did not ran long enough to actually profit much from optimizations much.

So, back then we did discuss possible solutions, and one idea was to basically notice a upcoming C-Call in bytecode ahead of execution, detect the stability of the arguments ahead of time. A background thread, extracts the values, perform the Call-processing of arguments and return pushes the values onto the stack, finally setting a "valid" bit to unblock the c-call (which by then actually is no longer a call). Both sides never have a complete cache eviction and live happily ever after. Unfortunatly i have a game dev addiction, so nothing ever came of it.

But similar minds might have pushed similar ideas.. so asking the hive for this jive. Anyone ever seen something similar in the wild?

Re: Building the fastest Lua interpreter automatically

#84

I work on a game, that mostly uses lua as logic code, saddling on a c/c++ engine. One of the engine developers implemented lua jit years ago and found that for the actual performance, the interpreter/jit was neglibable, the most expensive thing being the actual switch between luacode and c-code, which is with a large API a constant ugly background performance loss. The lua code by itself did not ran long enough to ac…

I actually measured it. It was around 65-85ns for Lua and 55ns for LuaJIT (on a 7950X). It adds up every time you make a call, despite looking like a small value. Of course, you can multiply it by 2-5x when you put it in production.A decent emulator these days should have Ultimately the script in a game engine either makes a bunch of API calls, so we want the "system call overhead" to be that of an opaque function call (4ns), and sometimes you share memory in order to read out properties and put abstractions on them (probably not relevant to Lua). So, it's important to get going fast, and to be able to make hundreds of engine API calls without unnecessary costs.

Benchmarks: https://gist.github.com/fwsGonzo/2f4518b66b147ee657d64496811...

Re: Building the fastest Lua interpreter automatically

#85

I work on a game, that mostly uses lua as logic code, saddling on a c/c++ engine. One of the engine developers implemented lua jit years ago and found that for the actual performance, the interpreter/jit was neglibable, the most expensive thing being the actual switch between luacode and c-code, which is with a large API a constant ugly background performance loss. The lua code by itself did not ran long enough to ac…

In CPUs they call this speculative execution. It's a good idea if you can detect side effect free code (and don't have the sandboxing issues that caused problems on CPUs).

Re: Building the fastest Lua interpreter automatically

#86
It would be interesting to see 1-1 comparison with LuaJIT interpreter _without corresponding runtime changes_. That would given a meaningful baseline for how much you potentially loose/gain using this approach.

Current measurement presents comparison of two wildly different implementation strategies: LuaJIT Remake uses IC and hidden classes while LuaJIT proper does not. It's a well known fact that ICs+hidden classes can lead to major performance improvements. That insight originated in Smalltalk world and was brought to dynamic language mainstream by V8† - but unfortunately it muddles the comparison here.

† V8 was open sourced on September 2, 2008... Coincidentally (though probably not :)) JSC landed their first IC prototype on September 1, 2008.

Re: Building the fastest Lua interpreter automatically

#87

Earlier quoted context omitted.

> More recently I have been experimenting with a new calling convention that uses no callee-saved registers to work around this, but the results so far are inconclusive. The new calling convention would use all registers for arguments, but allocate registers in the opposite order of normal functions, to reduce the chance of overlap. I have been calling this calling convention "reverse_cc". As explained in the article…

I tried exposing the "cc 10" and "cc 11" calling conventions to Clang, but was unsuccessful. With "cc 10" I got crashes at runtime I could not explain. With "cc 11" I got a compile-time error: > fatal error: error in backend: Can't generate HiPE prologue without runtime parameters If "cc 10" would work from Clang I'd be happy to use that. Though I think reverse_cc could potentially still offer benefits by ordering ar…

Adding calling conventions to clang is fairly straightforward (e.g. https://reviews.llvm.org/D125970) but there's a limited size structure in clang somewhere that makes doing so slightly contentious. Partly because they're shared across all architectures. At some point we'll run out of bits and have to do something invasive and/or slow to free up space.

Re: Building the fastest Lua interpreter automatically

#88
post #13

Earlier quoted context omitted.

Faster than the LuaJIT's interpreter . People who focus on JIT often focus on the JIT and not the interpreter. Which is a shame because if you make the uncommon paths cheaper then you can tune your code for the hot paths a bit more aggressively. You get fewer situations where you are sacrificing Best Case for Average Case performance.

As in, LuaJIT with the JIT disabled, ie with the `-j off` flag? That would be really good to clarify in the article. They start off talking about how they can generate an interpreter and even a JIT, LuaJIT with its JIT turned on is not benchmarked to provide the necessary perspective, the results summary says “28% faster than the LuaJIT interpreter” before the scope of the research has been clarified, and only if you…

That's my read at least. There are several points where they are careful to say LuaJIT interpreter, and I don't think that's an accident. Occams Razor says that's what they meant. The gist of the article is an incremental improvement on interpreter loops, not some counterintuitive amazing breakthrough that nobody has heard before. That's consistent with apples to apples comparisons.

Re: Building the fastest Lua interpreter automatically

#89

> With the tail-call approach, each bytecode now gets its own function, and the pathological case for the C/C++ compiler is gone. And as shown by the experience of the Google protobuf developers, the tail-call approach can indeed be used to build very good interpreters. But can it push to the limit of hand-written assembly interpreters? Unfortunately, the answer is still no, at least at its current state. > The main…

> More recently I have been experimenting with a new calling convention that uses no callee-saved registers to work around this, but the results so far are inconclusive. The new calling convention would use all registers for arguments, but allocate registers in the opposite order of normal functions, to reduce the chance of overlap. I have been calling this calling convention "reverse_cc". As explained in the article…

I think I remember one called preserve_none. That might have been in a downstream fork though.

The calling convention you want for a coroutine switch is caller saves everything live, callee saves nothing. As that means spilling is done on the live set, to the stack in use before switching. Return after stack switch then restores them.

So if a new convention is proposed that solves both fast stackful coroutines and bytecode interpreter overhead, that seems reasonable to me.

Re: Building the fastest Lua interpreter automatically

#90

Earlier quoted context omitted.

Not C, but could probably do it with Rust & Cranelift's IR instead of LLVM's. Not as heavy weight of a dependency as bringing in LLVM.

I just want to point out that LLVM is not a runtime dependency. It is only a build-time dependency if you want to build LJR from source. Once LJR is built, it is stand-alone and does not need LLVM at runtime.

Do other JIT developers who would want to use Deegen need to pull in LLVM as a dependency still?
Post reply on HN