sql >> Database teknologi >  >> RDS >> PostgreSQL

En oversigt over Just-in-Time Compilation (JIT) til PostgreSQL

Historisk har PostgreSQL leveret kompileringsfunktioner i form af forudgående kompilering til PL/pgSQL-funktioner og version 10 introduceret udtrykskompilering. Ingen af ​​dem genererer dog maskinkode.

JIT til SQL blev diskuteret for mange år siden, og for PostgreSQL er funktionen resultatet af en væsentlig kodeændring.

For at kontrollere, om PostgreSQL binær blev bygget med LLVM-understøttelse, brug kommandoen pg_configure til at vise kompileringsflag og se efter –with-llvm i outputtet. Eksempel på PGDG RPM distribution:

omiday ~ $ /usr/pgsql-11/bin/pg_config --configure
'--enable-rpath' '--prefix=/usr/pgsql-11' '--includedir=/usr/pgsql-11/include' '--mandir=/usr/pgsql-11/share/man' '--datadir=/usr/pgsql-11/share' '--enable-tap-tests' '--with-icu' '--with-llvm' '--with-perl' '--with-python' '--with-tcl' '--with-tclconfig=/usr/lib64' '--with-openssl' '--with-pam' '--with-gssapi' '--with-includes=/usr/include' '--with-libraries=/usr/lib64' '--enable-nls' '--enable-dtrace' '--with-uuid=e2fs' '--with-libxml' '--with-libxslt' '--with-ldap' '--with-selinux' '--with-systemd' '--with-system-tzdata=/usr/share/zoneinfo' '--sysconfdir=/etc/sysconfig/pgsql' '--docdir=/usr/pgsql-11/doc' '--htmldir=/usr/pgsql-11/doc/html' 'CFLAGS=-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' 'PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'

Hvorfor LLVM JIT?

Det hele startede for omkring to år siden som forklaret i Adres Freunds indlæg, da udtryksevaluering og tuple-deformering viste sig at være vejspærringerne for at fremskynde store forespørgsler. Efter tilføjelse af JIT-implementeringen "er selve udtryksevalueringen mere end ti gange hurtigere end før" med Andres' ord. Yderligere forklarer Q&A-sektionen, der afslutter hans indlæg, valget af LLVM frem for andre implementeringer.

Mens LLVM var den valgte udbyder, kan GUC-parameteren jit_provider bruges til at pege på en anden JIT-udbyder. Bemærk dog, at inlining-support kun er tilgængelig, når du bruger LLVM-udbyderen, på grund af den måde, byggeprocessen fungerer på.

Hvornår skal JIT?

Dokumentationen er klar:langvarige forespørgsler, der er CPU-bundne, vil drage fordel af JIT-kompilering. Derudover påpeger diskussionerne om mailingliste, der refereres til i denne blog, at JIT er for dyrt til forespørgsler, der kun udføres én gang.

Sammenlignet med programmeringssprog har PostgreSQL fordelen ved at "vide" hvornår man skal JIT, ved at stole på forespørgselsplanlæggeren. Til det formål blev en række GUC-parametre introduceret. For at beskytte brugere mod negative overraskelser, når de aktiverer JIT, er de omkostningsrelaterede parametre med vilje sat til rimeligt høje værdier. Bemærk, at indstilling af JIT-omkostningsparametrene til '0' vil tvinge alle forespørgsler til at blive JIT-kompileret og som et resultat bremse alle dine forespørgsler.

Selvom JIT generelt kan være gavnligt, er der tilfælde, hvor det kan være skadeligt at have det aktiveret, som diskuteret i commit b9f2d4d3.

Hvordan JIT?

Som nævnt ovenfor er de binære RPM-pakker LLVM-aktiverede. Men for at få JIT-kompileringen til at virke, kræves der et par ekstra trin:

Altså:

[email protected][local]:54311 test# show server_version;
server_version
----------------
11.1
(1 row)
[email protected][local]:54311 test# show port;
port
-------
54311
(1 row)
[email protected][local]:54311 test# create table t1 (id serial);
CREATE TABLE
[email protected][local]:54311 test# insert INTO t1 (id) select * from generate_series(1, 10000000);
INSERT 0 10000000
[email protected][local]:54311 test# set jit = 'on';
SET
[email protected][local]:54311 test# set jit_above_cost = 10; set jit_inline_above_cost = 10; set jit_optimize_above_cost = 10;
SET
SET
SET
[email protected][local]:54311 test# explain analyze select count(*) from t1;
                                                               QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate  (cost=97331.43..97331.44 rows=1 width=8) (actual time=647.585..647.585 rows=1 loops=1)
   ->  Gather  (cost=97331.21..97331.42 rows=2 width=8) (actual time=647.484..649.059 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=96331.21..96331.22 rows=1 width=8) (actual time=640.995..640.995 rows=1 loops=3)
               ->  Parallel Seq Scan on t1  (cost=0.00..85914.57 rows=4166657 width=0) (actual time=0.060..397.121 rows=3333333 loops=3)
Planning Time: 0.182 ms
Execution Time: 649.170 ms
(8 rows)

Bemærk, at jeg aktiverede JIT (som er deaktiveret som standard efter pgsql-hacker-diskussionen, der refereres til i commit b9f2d4d3). Jeg justerede også prisen på JIT-parametre som foreslået i dokumentationen.

Det første tip findes i filen src/backend/jit/README, der henvises til i JIT-dokumentationen:

Which shared library is loaded is determined by the jit_provider GUC, defaulting to "llvmjit".

Da RPM-pakken ikke trækker JIT-afhængigheden automatisk ind - som det blev besluttet efter omfattende diskussioner (se hele tråden) - er vi nødt til at installere den manuelt:

[[email protected] ~]# dnf install postgresql11-llvmjit

Når installationen er fuldført, kan vi teste med det samme:

[email protected][local]:54311 test# explain analyze select count(*) from t1;
                                                               QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate  (cost=97331.43..97331.44 rows=1 width=8) (actual time=794.998..794.998 rows=1 loops=1)
   ->  Gather  (cost=97331.21..97331.42 rows=2 width=8) (actual time=794.870..803.680 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=96331.21..96331.22 rows=1 width=8) (actual time=689.124..689.125 rows=1 loops=3)
               ->  Parallel Seq Scan on t1  (cost=0.00..85914.57 rows=4166657 width=0) (actual time=0.062..385.278 rows=3333333 loops=3)
Planning Time: 0.150 ms
JIT:
   Functions: 4
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 2.146 ms, Inlining 117.725 ms, Optimization 47.928 ms, Emission 69.454 ms, Total 237.252 ms
Execution Time: 803.789 ms
(12 rows)

Vi kan også vise JIT-detaljerne pr. medarbejder:

[email protected][local]:54311 test# explain (analyze, verbose, buffers) select count(*) from t1;
                                                                  QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate  (cost=97331.43..97331.44 rows=1 width=8) (actual time=974.352..974.352 rows=1 loops=1)
   Output: count(*)
   Buffers: shared hit=2592 read=41656
   ->  Gather  (cost=97331.21..97331.42 rows=2 width=8) (actual time=974.166..980.942 rows=3 loops=1)
         Output: (PARTIAL count(*))
         Workers Planned: 2
         Workers Launched: 2
         JIT for worker 0:
         Functions: 2
         Options: Inlining true, Optimization true, Expressions true, Deforming true
         Timing: Generation 0.378 ms, Inlining 74.033 ms, Optimization 11.979 ms, Emission 9.470 ms, Total 95.861 ms
         JIT for worker 1:
         Functions: 2
         Options: Inlining true, Optimization true, Expressions true, Deforming true
         Timing: Generation 0.319 ms, Inlining 68.198 ms, Optimization 8.827 ms, Emission 9.580 ms, Total 86.924 ms
         Buffers: shared hit=2592 read=41656
         ->  Partial Aggregate  (cost=96331.21..96331.22 rows=1 width=8) (actual time=924.936..924.936 rows=1 loops=3)
               Output: PARTIAL count(*)
               Buffers: shared hit=2592 read=41656
               Worker 0: actual time=900.612..900.613 rows=1 loops=1
               Buffers: shared hit=668 read=11419
               Worker 1: actual time=900.763..900.763 rows=1 loops=1
               Buffers: shared hit=679 read=11608
               ->  Parallel Seq Scan on public.t1  (cost=0.00..85914.57 rows=4166657 width=0) (actual time=0.311..558.192 rows=3333333 loops=3)
                     Output: id
                     Buffers: shared hit=2592 read=41656
                     Worker 0: actual time=0.389..539.796 rows=2731662 loops=1
                     Buffers: shared hit=668 read=11419
                     Worker 1: actual time=0.082..548.518 rows=2776862 loops=1
                     Buffers: shared hit=679 read=11608
Planning Time: 0.207 ms
JIT:
   Functions: 9
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 8.818 ms, Inlining 153.087 ms, Optimization 77.999 ms, Emission 64.884 ms, Total 304.787 ms
Execution Time: 989.360 ms
(36 rows)

JIT-implementeringen kan også drage fordel af den parallelle forespørgselsudførelsesfunktion. For at eksemplificere, lad os først deaktivere parallelisering:

[email protected][local]:54311 test# set max_parallel_workers_per_gather = 0;
SET
[email protected][local]:54311 test# explain analyze select count(*) from t1;
                                                      QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Aggregate  (cost=169247.71..169247.72 rows=1 width=8) (actual time=1447.315..1447.315 rows=1 loops=1)
   ->  Seq Scan on t1  (cost=0.00..144247.77 rows=9999977 width=0) (actual time=0.064..957.563 rows=10000000 loops=1)
Planning Time: 0.053 ms
JIT:
   Functions: 2
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 0.388 ms, Inlining 1.359 ms, Optimization 7.626 ms, Emission 7.963 ms, Total 17.335 ms
Execution Time: 1447.783 ms
(8 rows)

Den samme kommando med parallelle forespørgsler aktiveret udføres på halvdelen af ​​tiden:

[email protected][local]:54311 test# reset max_parallel_workers_per_gather ;
RESET
[email protected][local]:54311 test# explain analyze select count(*) from t1;
                                                               QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate  (cost=97331.43..97331.44 rows=1 width=8) (actual time=707.126..707.126 rows=1 loops=1)
   ->  Gather  (cost=97331.21..97331.42 rows=2 width=8) (actual time=706.971..712.199 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=96331.21..96331.22 rows=1 width=8) (actual time=656.102..656.103 rows=1 loops=3)
               ->  Parallel Seq Scan on t1  (cost=0.00..85914.57 rows=4166657 width=0) (actual time=0.067..384.207 rows=3333333 loops=3)
Planning Time: 0.158 ms
JIT:
   Functions: 9
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 3.709 ms, Inlining 142.150 ms, Optimization 50.983 ms, Emission 33.792 ms, Total 230.634 ms
Execution Time: 715.226 ms
(12 rows)

Jeg fandt det interessant at sammenligne resultaterne fra de test, der blev diskuteret i dette indlæg, under de indledende faser af JIT-implementering versus den endelige version. Sørg først for, at betingelserne i den originale test er opfyldt, dvs. databasen skal passe i hukommelsen:

[email protected][local]:54311 test# \l+
postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |                       | 8027 kB | pg_default | default administrative connection database
template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +| 7889 kB | pg_default | unmodifiable empty database
          |          |          |             |             | postgres=CTc/postgres |         |            |
template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +| 7889 kB | pg_default | default template for new databases
          |          |          |             |             | postgres=CTc/postgres |         |            |
test      | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |                       | 2763 MB | pg_default |


[email protected][local]:54311 test# show shared_buffers ;
3GB

Time: 0.485 ms
Download Whitepaper Today PostgreSQL Management &Automation med ClusterControlFå flere oplysninger om, hvad du skal vide for at implementere, overvåge, administrere og skalere PostgreSQLDownload Whitepaper

Kør testene med JIT deaktiveret:

[email protected][local]:54311 test# set jit = off;
SET
Time: 0.483 ms

[email protected][local]:54311 test# select sum(c8) from t1;
   0

Time: 1036.231 ms (00:01.036)

[email protected][local]:54311 test# select sum(c2), sum(c3), sum(c4), sum(c5),
   sum(c6), sum(c7), sum(c8) from t1;
   0 |   0 |   0 |   0 |   0 |   0 |   0

Time: 1793.502 ms (00:01.794)

Kør derefter testene med JIT aktiveret:

[email protected][local]:54311 test# set jit = on; set jit_above_cost = 10; set
jit_inline_above_cost = 10; set jit_optimize_above_cost = 10;
SET
Time: 0.473 ms
SET
Time: 0.267 ms
SET
Time: 0.204 ms
SET
Time: 0.162 ms
[email protected][local]:54311 test# select sum(c8) from t1;
   0

Time: 795.746 ms

[email protected][local]:54311 test# select sum(c2), sum(c3), sum(c4), sum(c5),
   sum(c6), sum(c7), sum(c8) from t1;
   0 |   0 |   0 |   0 |   0 |   0 |   0

Time: 1080.446 ms (00:01.080)

Det er en hastighedsstigning på omkring 25 % for den første testsag og 40 % for den anden!

Til sidst er det vigtigt at huske, at for udarbejdede udsagn udføres JIT-kompileringen, når funktionen udføres første gang.

Konklusion

Som standard er JIT-kompilering deaktiveret, og for RPM-baserede systemer vil installationsprogrammet ikke antyde behovet for at installere JIT-pakken, der leverer bitkoden til standardudbyderen LLVM.

Når du bygger fra kilder, skal du være opmærksom på kompileringsflag for at undgå ydeevneproblemer, for eksempel hvis LLVM-påstande er aktiveret.

Som diskuteret på pgsql-hackerlisten er JIT's indvirkning på omkostninger endnu ikke fuldt ud forstået, så omhyggelig planlægning er påkrævet, før funktionsklyngen aktiveres bredt, da forespørgsler, der ellers kunne drage fordel af kompilering, faktisk kan køre langsommere. JIT kan dog aktiveres på basis af forespørgsler.

For dybdegående information om implementeringen af ​​JIT-kompileringen, gennemgå projektets Git-logfiler, Commitfests og pgsql-hackers mailtråd.

Glad JITing!


  1. Brug af Aria Storage Engine med MariaDB Server

  2. Hvordan bruger man en PostgreSQL container med eksisterende data?

  3. Sådan overvåges HAProxy-metrics med ClusterControl

  4. Split funktion tilsvarende i T-SQL?