pl: replace LLVM JIT with a tree-walking interpreter#883
Open
cao1629 wants to merge 2 commits into
Open
Conversation
Execute PL by walking the resolved ObPLStmt tree (ObPLInterpreter) instead of JIT-compiling routines with LLVM, and remove the ORC-JIT code generator and the objit module. The interpreter dispatches blocks, DECLARE ... DEFAULT, assignment (including SET @user_var / @@sys_var and obj-access targets such as a trigger's NEW.col), IF/ELSEIF, CASE, WHILE, LOOP, REPEAT, LEAVE, ITERATE, DO, embedded SQL, RETURN (with deep-copied results), cursors (DECLARE/OPEN/FETCH/CLOSE), exception handling (DECLARE HANDLER, SIGNAL, completion conditions), PRAGMA INTERFACE routines, and nested CALL with OUT/INOUT copy-back. Loops poll for KILL and query/transaction timeout at the same cadence the JIT used. It passes the full PL mysqltest suite.
d81791f to
037f733
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Performance comparison of the PL: tree-walking interpreter vs LLVM JIT.
Part 1 — realistic PL (executes SQL): interpreter ≈ JIT
Fixture: a 1000-row account table (+ a log table for the write paths).
Ten stored procedures representative of real production workloads.
1. p_get_balance
interp 349 µs · JIT 332 µs → JIT 5% faster.
2. p_sum_region
interp 983 µs · JIT 977 µs → ≈ parity.
3. p_count_active
interp 951 µs · JIT 954 µs → ≈ parity.
4. p_cursor_sum
interp 3.74 ms · JIT 3.96 ms → interp 6% faster.
5. p_apply_interest
interp 9.85 ms · JIT 9.50 ms → JIT 4% faster.
6. p_transfer
interp 1.25 ms · JIT 1.30 ms → interp 3% faster.
7. p_insert_log
interp 650 µs · JIT 634 µs → JIT 2% faster.
8. p_batch_log
interp 263 ms · JIT 265 ms → ≈ parity.
9. p_upsert
interp 999 µs · JIT 914 µs → JIT 9% faster.
10. p_classify
interp 331 ms · JIT 330 ms → ≈ parity.
Why interpreter ≈ JIT here: every one of these routines spends almost all of its running time in SQL execution.
Part 2 — three extreme pure-PL cases: JIT wins only here
No SQL here — the per-iteration AST walk is the whole runtime: the interpreter dispatches every node, while the JIT runs precompiled machine code. The more control flow each packs in, the wider the gap.
chain_if100, n = 45000
interp 25.72 µs/iter · JIT 17.50 µs/iter → JIT ~1.47×. The 100 IF/ELSEIF arms form a deeply nested AST that the interpreter walks down arm by arm; the JIT compiled the chain to a straight compare-and-jump run.
iter_block50, n = 370000
interp 3.28 µs/iter · JIT 1.32 µs/iter → JIT ~2.48×. Each pass descends 50 nested blocks and
ITERATEunwinds back out through all of them; the JIT flattened the nest to direct jumps.flat_block100, n = 540000
interp 2.75 µs/iter · JIT 1.33 µs/iter → JIT ~2.07×. The interpreter enters and exits all 100 empty blocks every iteration; the JIT compiled them away entirely.
Conclusion
In realistic production use, JIT-compiled and interpreted PL perform about the same. The JIT pulls ahead only in extreme cases — no SQL execution and deeply nested control flow — which rarely occur in practice.