sql >> Databasteknik >  >> RDS >> PostgreSQL

En översikt över Just-in-Time Compilation (JIT) för PostgreSQL

Historiskt har PostgreSQL tillhandahållit kompileringsfunktioner i form av kompilering i förväg för PL/pgSQL-funktioner och version 10 introducerade uttryckskompilering. Ingen av dessa genererar dock maskinkod.

JIT för SQL diskuterades för många år sedan, och för PostgreSQL är funktionen resultatet av en betydande kodändring.

För att kontrollera om PostgreSQL-binären byggdes med LLVM-stöd använd kommandot pg_configure för att visa kompileringsflaggorna och leta efter –with-llvm i utdata. Exempel för 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'

Varför LLVM JIT?

Det hela började för ungefär två år sedan som förklaras i Adres Freunds inlägg när uttrycksutvärdering och tuppeldeformering visade sig vara vägspärrarna för att påskynda stora frågor. Efter att ha lagt till JIT-implementeringen är "uttrycksutvärderingen i sig mer än tio gånger snabbare än tidigare" med Andres ord. Vidare förklarar avsnittet Frågor och svar som avslutar hans inlägg valet av LLVM framför andra implementeringar.

Medan LLVM var den valda leverantören kan GUC-parametern jit_provider användas för att peka på en annan JIT-leverantör. Observera dock att stöd för inlining endast är tillgängligt när du använder LLVM-leverantören, på grund av hur byggprocessen fungerar.

När ska jag JIT?

Dokumentationen är tydlig:långvariga frågor som är CPU-bundna kommer att dra nytta av JIT-kompilering. Dessutom påpekar diskussionerna om e-postlistor som hänvisas till i den här bloggen att JIT är för dyrt för frågor som bara körs en gång.

Jämfört med programmeringsspråk har PostgreSQL fördelen att "veta" när man ska JIT, genom att förlita sig på frågeplaneraren. För detta ändamål introducerades ett antal GUC-parametrar. För att skydda användare från negativa överraskningar när de aktiverar JIT är de kostnadsrelaterade parametrarna avsiktligt inställda på rimligt höga värden. Observera att om du ställer in JIT-kostnadsparametrarna till "0" kommer alla frågor att bli JIT-kompilerade och som ett resultat sakta ner alla dina frågor.

Även om JIT generellt kan vara fördelaktigt, finns det fall då det kan vara skadligt att ha det aktiverat, vilket diskuterades i commit b9f2d4d3.

Hur JIT?

Som nämnts ovan är de binära RPM-paketen LLVM-aktiverade. Men för att JIT-kompileringen ska fungera krävs ytterligare några steg:

Alltså:

[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)

Observera att jag aktiverade JIT (som är inaktiverat som standard efter diskussionen om pgsql-hackers som refereras till i commit b9f2d4d3). Jag justerade också kostnaden för JIT-parametrar som föreslås i dokumentationen.

Det första tipset finns i filen src/backend/jit/README som refereras till i JIT-dokumentationen:

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

Eftersom RPM-paketet inte drar in JIT-beroendet automatiskt – som det beslutades efter intensiva diskussioner (se hela tråden) – måste vi installera det manuellt:

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

När installationen är klar kan vi testa direkt:

[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 också visa JIT-detaljerna per arbetare:

[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 också dra fördel av funktionen för körning av parallella frågor. För att exemplifiera, låt oss först inaktivera parallellisering:

[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)

Samma kommando med parallella frågor aktiverade slutförs på halva 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)

Jag tyckte att det var intressant att jämföra resultaten från testerna som diskuteras i det här inlägget, under de inledande stadierna av JIT-implementering kontra den slutliga versionen. Se först till att villkoren i det ursprungliga testet är uppfyllda, dvs databasen måste passa i minnet:

[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
Ladda ner Whitepaper Today PostgreSQL Management &Automation med ClusterControlLäs om vad du behöver veta för att distribuera, övervaka, hantera och skala PostgreSQLDladda Whitepaper

Kör testerna med JIT inaktiverat:

[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 sedan testerna med JIT aktiverat:

[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 är en hastighetsökning på cirka 25 % för det första testfallet och 40 % för det andra!

Slutligen är det viktigt att komma ihåg att för förberedda uttalanden utförs JIT-kompileringen när funktionen först körs.

Slutsats

Som standard är JIT-kompilering inaktiverad, och för RPM-baserade system kommer installationsprogrammet inte att antyda behovet av att installera JIT-paketet som tillhandahåller bitkoden för standardleverantören LLVM.

När du bygger från källor, var uppmärksam på kompileringsflaggorna för att undvika prestandaproblem, till exempel om LLVM-påståenden är aktiverade.

Som diskuterats på pgsql-hackarlistan är JIT:s inverkan på kostnadsberäkning ännu inte helt klarlagd så noggrann planering krävs innan funktionsklustret aktiveras, eftersom frågor som annars skulle kunna dra nytta av kompilering faktiskt kan köras långsammare. JIT kan dock aktiveras för varje fråga.

För djupgående information om implementeringen av JIT-kompileringen, granska projektets Git-loggar, Commitfests och pgsql-hackers e-posttråd.

Glad JITing!


  1. Hur man undslipper enstaka citat, specialtecken i MySQL

  2. Hur man hanterar skottsekunder i Oracle

  3. Tips för att hantera PostgreSQL på distans

  4. använda nollor i ett mysqli-förberedt uttalande