8/23/2026 at 8:32:39 AM
Reminds me of the 2024 blog post Look ma, I wrote a new JIT compiler for PostgreSQL [0]. Both articles lament that Postgres's LLVM-based JIT [1] takes a while to generate code.> The rarity of JIT compilers makes me believe that implementing a JIT compiler historically was too difficult for it to be worthwhile.
That's only true of writing a JIT from scratch. There's no rarity of JITs, it's just that LLVM (and other frameworks) are often used. Every major interpreter has a JIT compiler. PCRE2 has a JIT compiler. There are JIT frameworks out there with much faster code-generation than LLVM: Cranelift, GNU Lightning, Mir. I doubt they could do code-generation faster than a custom copy-and-patch JIT, but they'd be much faster than LLVM.
[0] https://www.pinaraf.info/2024/03/look-ma-i-wrote-a-new-jit-c... , discussed: https://news.ycombinator.com/item?id=39742916
by MaxBarraclough
8/23/2026 at 3:56:54 PM
Not sure where the idea comes from that Cranelift is much faster than LLVM -O0, at least in our experiments in 2024 it wasn't, see [1] Fig. 6.Template-based code generators suffer from bad code quality due to missing register allocation.
Our TPDE-based compilers compile a bit slower than template-based code generation but the generated code is much smaller and faster ([2] Fig. 2). Also for database workloads ([2] Fig. 6).
All that said, Postgres' main limitation is that it (IIRC) only compiles single expressions from operators, not pipelines. This fundamentally limits the achievable performance improvement compared to databases that perform more extensive query compilation.
[1]: https://aengelke.net/pubs/2403-cgo.pdf [2]: https://aengelke.net/pubs/2602-cgo1.pdf
PS: sorry for the promotion of my own research here, just couldn't resist.
by aengelke
8/23/2026 at 7:35:48 PM
Always good to have proper researchers in the thread.> Not sure where the idea comes from that Cranelift is much faster than LLVM -O0
Cranelift describes itself as a fast, secure, relatively simple and innovative compiler backend. [0] Interesting that LLVM can compete there, with its optimisations dialed down.
> Postgres' main limitation is that it (IIRC) only compiles single expressions from operators, not pipelines. This fundamentally limits the achievable performance improvement compared to databases that perform more extensive query compilation.
That sounds pretty limiting. That's separate from query optimisation though, right? The query optimiser is presumably able to reason 'broadly' and not just at the level of individual expressions? High-level query-plan optimisation must be much more consequential than effective use of JIT compilation.
by MaxBarraclough
8/23/2026 at 8:23:30 PM
> That's separate from query optimisation though, right? The query optimiser is presumably able to reason 'broadly' and not just at the level of individual expressions? High-level query-plan optimisation must be much more consequential than effective use of JIT compilation.Yes, yes, and yes. For databases, query optimization (esp. join ordering for larger queries, which heavily depends on estimates) is fundamental. Query optimization happens at the level of the query plan, JIT compilation is only relevant afterwards. A bad query plan leads to asymptotically worse performance (e.g., bad join ordering with huge intermediate results).
On query plan execution: The "classical" model as used in e.g. Postgres is a pull-based iterator model, where operators implement a next() method yielding the next tuple and in there recursively call next() on their child operators (e.g., a next() of a select operator calls next() on its child operator, then applies the predicate [what Postgres JIT-compiles], and returns the tuple if the predicate was true). This can happen one tuple at a time (Postgres) or "vectorized" where multiple tuples are processed at once (e.g. DuckDB). A query-compiling database will split the tree into pipelines and compile each pipeline as one function (e.g., a pipeline will iterate over all the tuples from a source (e.g. tablescan) and a select operator then becomes an if statement inside that loop). This results in pretty tight loops, avoids per-tuple dispatch overhead, and enables more optimizations inside the JIT-ted code (e.g., tuple values don't need to be reloaded from memory all the time). (I find the original paper on query compilation [1] to be well readable.)
by aengelke
8/23/2026 at 2:11:27 PM
> There's no rarity of JITs, it's just that LLVM (and other frameworks) are often usedExcept that using LLVM has high latency limitting it's applicability. Postgres just disabled LLVM by default because of this[0].
[0] https://www.postgresql.org/message-id/E1w8GWU-002bSL-31%40ge...
by malisper
8/23/2026 at 2:54:37 PM
Interesting, thanks for the link. LLVM isn't the only game in town though, nobody using it (or libgccjit) for JIT should be surprised to see relatively long compile times. I wonder if the postgres project will try a different backend.There's a strong 'diminishing returns' effect in striking a balance between compile time and the performance of the generated code. I'd expect a more lightweight (less optimising) JIT engine to be able to produce code with pretty respectable performance while taking only a fraction of the time that LLVM takes. There's a follow-up to the blog post I linked above, which bears this out. [0] (I don't know if that JIT solution is production-ready or viable for merging into postgres, mind.)
The blog post [0] gives this performance comparison:
> So, on our stupid benchmark, doing 10 times a simple SELECT * FROM demo WHERE a = 42 on a 10 million rows table...
PostgreSQL No JIT LLVM JIT Copyjit
---------- ------ -------- -------
Average time (ms) 120 106 (-12%) 101 (-15%)
Compilation time (ms) 0 19 0.06
Instructions 13,350,766,209 10,643,820,667 (-21%) 12,769,013,536 (-5%)
Cycles 4,660,821,596 4,005,881,863 (-14%) 3,924,602,439 (-16%)
Branches 2,322,470,659 1,798,221,785 (-23%) 2,031,456,214 (-13%)
[0] https://www.pinaraf.info/2025/12/jit-episode-iii-warp-speed-...
by MaxBarraclough
8/23/2026 at 9:33:41 AM
The original Dartmouth BASIC had a JIT like approach, the REPL would compile to machine code before execution.It was the limits of 8 bit home computers hardware that made the interpreter version be more widely known.
Same to Lisp, Smalltalk, and many other languages.
Fully agree with you.
by pjmlp
8/23/2026 at 9:29:58 AM
A few other small and fast JITs: https://github.com/zherczeg/sljit (used by libpcre), https://github.com/asmjit/asmjit (RPCS3 and FBGEMM) and https://webkit.org/blog/5852/introducing-the-b3-jit-compiler... (only used by JSC in Webkit, I think)by BoingBoomTschak
8/23/2026 at 2:17:30 PM
Thanks, sljit looks somewhat similar to GNU lightning.On reflection I wonder if I overstated the widespread use of JIT and of JIT compiler frameworks. All the 'major' well-resourced high-profile JIT-based interpreters I can think of don't use an off-the-shelf JIT framework for their backend, which makes sense as they want to carefully tune the code-generation. OpenJDK, OpenJ9, .Net, V8, SpiderMonkey, JavaScriptCore. LuaJIT and Python's new JIT don't use one either, nor does the Linux kernel's BPF engine.
The Guile Scheme interpreter uses a fork of the GNU Lightning JIT library. [0] Julia and (as mentioned) Postgres use LLVM for their JITs. I'm trying to think of other projects that use a JIT framework/library.
Similarly, I can't think of many problem domains where it makes sense to use JIT. The ones that spring to mind are interpreters (of course), regex engines, and DBMSs. JIT can also help in high-performance computing, to tailor the code to the particular problem and the particular CPU. [1] I don't think there are many other contexts where it makes sense to use JIT though.
JIT compilation brings its own drawbacks in portability (both between hardware platforms and operating systems), complexity, and perhaps cybersecurity, which might also limit its adoption, even if a good JIT framework could help with all three.
[0] https://doc.guix.gnu.org/guile/latest/en/html_node/Just_002d...
[1] https://www.intel.com/content/www/us/en/developer/articles/t...
by MaxBarraclough
8/23/2026 at 4:39:27 PM
I think that the "manual JIT compilation" that Common Lisp provide is the most practical compromise here. Sure, you don't have the automated switching between bytecode execution and progressively optimized compilation and you need to manually track runtime typing information to feed to the compiler, but the machinery is so much simpler and builtin!See https://github.com/marcoheisig/Petalisp#why-is-petalisp-writ...
by BoingBoomTschak