1. Esperimenti con il JIT compiler di Postgres
Chris Mair
pgTraining
http://www.pgtraining.com
12 Marzo 2021
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 1 / 15
2. La compilazione JIT
Postgres supporta la compilazione just in time (JIT) di:
valutazione di espressioni (nella SELECT list, WHERE o GROUP BY)
trasformazione dei record dalla rappresentazione su disco alla
rappresentazion in memoria
La compilazione JIT è presente in Postgres dalla versione 11 e abilitato di
default dalla versione 12.
Però non tutti i binary distribuiti l’hanno attivata (p.e. i pacchetti PGDG
la attivano su Debian 10, ma non su CentOS 7).
Si può controllare se JIT è abilitato nel binary verificando la presenza di
--with-llvm nell’output di pg config --configure.
Il parametro GUC jit attiva o disattiva il feature dinamicamente.
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 2 / 15
3. Primo esempio/1
CREATE TABLE big10 AS
SELECT x, random() AS r1, random() AS r2
FROM generate_series(1, 10E6) AS x;
EXPLAIN SELECT count(*) FROM big10 WHERE r1 > 0.9;
QUERY PLAN
----------------------------------------------------------------------------------
Finalize Aggregate (cost=117830.76..117830.77 rows=1 width=8)
-> Gather (cost=117830.55..117830.76 rows=2 width=8)
Workers Planned: 2
-> Partial Aggregate (cost=116830.55..116830.56 rows=1 width=8)
-> Parallel Seq Scan on big10 (cost=0.00..115778.93 rows=420646 width=0)
Filter: (r1 > ’0.9’::double precision)
JIT:
Functions: 6
Options: Inlining false, Optimization false, Expressions true, Deforming true
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 3 / 15
4. Primo esempio/2
Cosa è successo?
non essendoci indici, il planner esegui un sequential scan
essendo la tabella sufficientemente grande (0.5 GB) parallelizza la
query con 2 worker
stimando un costo elevato (117830) superiore alla soglia
jit above cost (default 100k), attiva il JIT
attiva il JIT sia per la valutazione delle espressioni che per la
decodifica dei record (“Expressions true, Deforming true”)
non attiva le parti più costose del JIT: l’inlining con
jit inline above cost (default 500k) e l’ottimizzazione con
jit optimize above cost (default 500k)
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 4 / 15
5. Le macchine di prova
Come incide il JIT sulla performance?
Ho due macchine su AWS.
instanza m5d.xlarge:
4 thread di un Xeon Platinum 8000’ series @ 2.5 GHz (x86 64)
instanza m6gd.xlarge:
4 core di un Graviton 2 @ 2.5 GHz (ARM64)
Entrambe con 16 GB di RAM, 100 GB di storage di tipo gp3.
OS Debian 10.8 e Postgres 12.6 dal repo PGDG.
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 5 / 15
6. Il benchmark
Eseguo la query di prima:
SELECT count(*) FROM big10 WHERE r1 > 0.9;
Prendo il tempo minimo di un paio di run. Le macchine non sono dedicate
(ma i thread/core sı̀), quindi il valore assoluto delle misure non è
super-accurato.
La tabella è interamente cacheata (shared buffers = 4 GB, tabella =
0.5 GB). Il tempo d’esecuzione è quindi limitato dalla CPU e la banda
verso la RAM.
Chiaramente per una query limitata dalla banda verso lo storage, il JIT
non inciderebbe...
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 6 / 15
7. JIT deludente?
I risultati non convingono:
jit = off jit = on rapporto
x86 64 371 ms 386 ms 1.040
ARM64 330 ms 342 ms 1.036
Conclusioni preliminari:
Il JIT peggiora la performance leggermente (3...4%).
JIT a parte, la macchina ARM è più performante e costa meno!
Scoperta casuale: ARM batte JIT ;)
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 7 / 15
8. Optimization? Inlining?
Provo a settare set jit optimize above cost = 0 oppure
jit inline above cost = 0 per forzare le ulteriori ottimizzazioni che per
la query data non vengono normalmente eseguite.
jit = off jit jit + opt jit + inl rapporto
x86 64 371 ms 386 ms 344 ms 431 ms 0.927
ARM64 330 ms 342 ms 301 ms 392 ms 0.912
Conclusioni:
L’ulteriore ottimizzazione migliora la performance in modo
significativo (8...9%). Il valore di default per
jit optimize above cost (500k) pare essere troppo alto.
L’inlining effettivamente in questo caso non serve.
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 8 / 15
9. Un’espressione più complessa
Sospetto che un’espressione più complessa mostri meglio l’effetto del JIT.
Visto che si avvicina il π-day:
SELECT count(*) / (0.5 * 0.5) FROM big10
WHERE sqrt( (r1-0.5) * (r1-0.5) + (r2-0.5) * (r2-0.5) ) < 0.5;
jit = off jit jit + opt jit + inl rapporto
x86 64 660 ms 664 ms 559 ms 844 ms 0.847
ARM64 583 ms 663 ms 495 ms 783 ms 0.847
Conclusioni:
Anche qui bisogna forzare l’ottimizzazione per avere il risultato
migliore che in questo caso supera di circa 15% la base line con jit
= off per entrambe le macchine.
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 9 / 15
10. L’ottimizzazione costa
L’EXPLAIN ANALYZE sulla precedente query con
jit optimize above cost = 0 mi conferma il costo dell’ottimizzazione:
JIT:
Functions: 14
Options: Inlining false, Optimization true, Expressions true, Deforming true
Timing: Generation 2.368 ms, Inlining 0.000 ms, Optimization 48.847 ms,
Emission 45.975 ms, Total 97.190 ms
Questo spiega il motivo per cui jit optimize above cost è piuttosto
elevato di default. Qui l’ottimizzazione consuma 50 ms del processo JIT di
100 ms, per una query di 600 ms.
Questo tempo però non dipende dal numero di record, quindi mi aspetto
più guadagno per una tabella più corposa.
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 10 / 15
11. Size matters
Provo con una tabella da 3.7 GB:
CREATE TABLE big75 AS
SELECT x, random() AS r1, random() AS r2
FROM generate_series(1, 75E6) AS x;
E la query in onore del π-day:
SELECT count(*) / (0.5 * 0.5) / 7.5 FROM big75
WHERE sqrt( (r1-0.5) * (r1-0.5) + (r2-0.5) * (r2-0.5) ) < 0.5;
In questo caso tutte le soglie sono superate, quindi il JIT fa
automaticamente inlining e ottimizzazione:
JIT:
Functions: 14
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 2.359 ms, Inlining 118.053 ms, Optimization 105.903 ms,
Emission 85.359 ms, Total 311.674 ms
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 11 / 15
12. Risultati
In questo caso “ideale” della tabella grande, ma ancora cacheabile,
otteniamo un bel guadagno, superiore al 20%, per ARM addirittura del
25%:
jit = off jit (all) rapporto
x86 64 5149 ms 4086 ms 0.794
ARM64 4399 ms 3261 ms 0.741
In questo caso, inoltre non era necessario aggiustare le soglie per
l’ottimizzazione e l’inlining.
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 12 / 15
13. Conclusioni
Conclusioni:
La compilazione JIT velocizza query di tipo analitico che applicano
espressioni a molti record in situazioni limitati da CPU o banda di
memoria.
È attivata di default in Postgres a partire dalla versione 12, ma non
tutti i pacchetti binari l’hanno attivata.
Può succedere che sia controproducente e viene comunemente
disabilitato, p.e. AWS lo disabilita di default in RDS.
I miei test e anche altri test che si trovano sul web indicano che ci si
può aspettare un guadagno dell’ordine dei 20% nel caso migliore.
ARM spacca ;)
Chris Mair PgTraining online session 2021-03 12 Marzo 2021 13 / 15