jit

JIT compiles query plans to native code. Speeds up complex analytics; slows down OLTP by 135× when misconfigured with wrong threshold.

MEDIUM RISK
Safe first move

Keep defaults: jit=on, jit_above_cost=100000. Only lower threshold for analytics-only workloads.

Main risk

jit=on + jit_above_cost=0: 1-row OLTP lookup goes from 0.1ms to 13ms (135× overhead, lab-captured). Keep default jit_above_cost=100000.

Tier 2 — Production Guide — Operational guidance is available; evidence scope is stated where relevant.

What We Observed

box

(PG 17.10, Intel Core Ultra 7 268V, WSL2), jit_test 500k rows

oltp with jit overhead

setting

jit=on, jit_above_cost=0 (JIT fires on every query)

query

SELECT * FROM jit_test WHERE id=250000 (1-row PK lookup)

exec time ms reps
  • 15.08
  • 9.988
  • 14.238
median ms

13.2

jit fires

true

source file

oltp without jit

setting

jit=off

query

Same 1-row PK lookup

exec time ms reps
  • 0.102
  • 0.082
  • 0.098
median ms

0.098

jit fires

false

source file

oltp overhead ratio

135× overhead (13.2ms vs 0.098ms)

analytics with jit

setting

jit=on, jit_above_cost=0

query

SELECT sum(a*b+c*d) FROM jit_test WHERE e LIKE 'A%'

exec time ms reps
  • 49.205
  • 42.599
  • 53.891
median ms

49.2

source file

analytics without jit

setting

jit=off

exec time ms reps
  • 25.349
  • 23.477
  • 20.777
median ms

23.5

note

On this hardware (WSL2), JIT did NOT improve analytical query — 2× slower with JIT than without!

curve note

JIT adds LLVM compilation time upfront. Threshold behavior: below jit_above_cost, no JIT; above it, JIT compiles. On short OLTP, compilation always exceeds benefit.

What Was Expected

framing

jit=on with jit_above_cost=0 fires JIT compilation on EVERY query. A 1-row PK lookup that normally takes 0.1ms takes 13ms with JIT (135× overhead). On OLTP servers doing 1000s of tiny queries/s, this is catastrophic: tps collapses.

oltp evidence
setting

jit=on, jit_above_cost=0

query

1-row primary key lookup

with jit ms

13.2

without jit ms

0.098

overhead factor

135×

source file

analytics note

On this hardware even analytical queries were SLOWER with JIT (49ms vs 23ms). jit_above_cost=100000 (default) prevents JIT on short queries but may still add overhead.

production impact

If jit=on globally and workload is OLTP-heavy, throughput can drop 10-100× for short queries.

source file

Common Pitfalls

  • Treating jit=on as universally beneficial — on OLTP / short queries it is often net-negative, and on a fast query with a stale/over-estimated cost it can add hundreds of ms (measured 2.2x slower).
  • Diagnosing a JIT regression as a JIT-tuning problem when the real cause is a bad ROW ESTIMATE — fix stats (ANALYZE / autovacuum / CREATE STATISTICS) first; the lab's regression was reltuples=10M vs 5,000 actual rows.
  • Forgetting JIT compile cost is paid in EVERY parallel worker — high parallelism multiplies the tax on queries that do not benefit.
  • Assuming jit=on means JIT is actually happening — a binary built without LLVM reports jit=on but pg_jit_available=false and never JITs; conversely JIT only fires when estimated cost also exceeds jit_above_cost.
  • Lowering jit_above_cost to 'get more JIT' — this drags more small/fast queries across the gate and into the compile tax; the safer direction is usually to RAISE it (or the inline/optimize gates).
  • Not reading the EXPLAIN ANALYZE 'JIT: . Timing' block — it is the only place the compile cost (Generation/Inlining/Optimization/Emission/Total) is visible; without it a JIT regression looks like an unexplained slowdown.

Tuning Guidance

Unlock Incident Pro

The free sections tell you what to do. Pro is the measured proof — the exact failure signature, safe-ceiling formula, and per-workload defaults, each captured on real PG 14–18 runs.

  • Tuning Guidance
Unlock Incident Pro

jit is the on/off master switch for Just-In-Time compilation of query expressions. When jit is on (the default), and a query's TOTAL estimated plan cost exceeds jit_above_cost (default 100,000), PostgreSQL hands that query's expression evaluation and tuple deforming to the LLVM JIT: instead of walking a generic expression-interpreter tree per row, it generates and runs machine code specialised to that exact query. For long, expression-heavy, many-row queries (analytics: big aggregates, complex WHERE/CASE arithmetic over millions of rows) this can cut CPU time substantially. JIT has THREE escalating stages, each with its own cost gate: basic code generation (fires above jit_above_cost), then Inlining (above jit_inline_above_cost, default 500,000) which pulls function bodies inline, then Optimization (above jit_optimize_above_cost, default 500,000) which runs LLVM optimisation passes. Each stage costs WALL-CLOCK TIME to compile BEFORE the query runs — and that compile tax is decided purely on the planner's COST ESTIMATE, not on how many rows the query actually touches. That asymmetry (real, up-front, estimate-driven cost vs only-sometimes-realised benefit) is the whole story of jit.

name

jit

setting default

on

unit

(none — boolean)

vartype

bool

category

Query Tuning / Other Planner Options

context

user

min val

(n/a — bool)

max val

(n/a — bool)

enumvals

(n/a — bool; accepts on/off/true/false/1/0/yes/no)

boot val

on

reset val

on

pending restart

false

short desc

Allow JIT compilation.

ground truth note

Captured literally from pg_settings on PG 14-18 (-18) on 2026-06-20. setting=on, boot_val=on, reset_val=on on EVERY version (default_changed=false, boot_fallback_changed=false). context=user (any session/role/database can set it, no restart, no superuser). vartype=bool. category='Query Tuning / Other Planner Options', short_desc='Allow JIT compilation.' pg_jit_available=true on all five lab images, so JIT is actually compiled in (a source build WITHOUT --with-llvm reports jit=on but pg_jit_available=false and silently never JITs). Companion cost gates on all versions: jit_above_cost=100000, jit_inline_above_cost=500000, jit_optimize_above_cost=500000.

References

  • PostgreSQL docs: Runtime Config — Query Planning → Other Planner Options (jit) and JIT cost gates (jit_above_cost, jit_inline_above_cost, jit_optimize_above_cost).
  • PostgreSQL docs: Chapter 'Just-in-Time Compilation (JIT)' — what gets compiled (expressions, tuple deforming), the cost-based decision, pg_jit_available, jit_provider.
  • PostgreSQL docs: EXPLAIN — the 'JIT' / 'Timing' output block (Generation, Inlining, Optimization, Emission, Total).
  • PostgreSQL docs: Statistics Used by the Planner; ANALYZE; CREATE STATISTICS — accurate row estimates are what keep JIT from firing on fast queries.
  • PostgreSQL docs: Parallel Query — JIT compilation is performed per worker.
  • Lab capture 2026-06-20: — 10M-row jit_t EFFECT (5.60 s -> 4.05 s, compile 345.888 ms) and jit_skew stale-stats DANGER (reltuples 10,000,806 vs 5,000 actual rows; jit=on ~2.2x slower; compile Timing Total 257.376 ms); pg_jit_available=true and jit/jit_*_cost ground truth PG 14-18 (-18).

Technical Reference

Category
Query Tuning / Other Planner Options
Type
bool ((none; a boolean on/off switch that decides whether the executor may JIT-compile a query whose total estimated cost crosses jit_above_cost))
Blast radius
query
Severity
Moderate
Context
user
Versions
14, 15, 16, 17, 18
Reviewed
2026-06-20

Severity scale: 1 Minimal · 2 Low · 3 Moderate · 4 High · 5 Critical.

Keep going

Related & next steps