sql >> Databáze >  >> RDS >> PostgreSQL

Přehled kompilace Just-in-Time (JIT) pro PostgreSQL

Historicky PostgreSQL poskytoval kompilační funkce ve formě předčasné kompilace pro funkce PL/pgSQL a verze 10 zavedla kompilaci výrazů. Žádný z nich však negeneruje strojový kód.

JIT pro SQL byl diskutován před mnoha lety a pro PostgreSQL je tato funkce výsledkem podstatné změny kódu.

Chcete-li zkontrolovat, zda byl binární soubor PostgreSQL sestaven s podporou LLVM, použijte příkaz pg_configure k zobrazení příznaků kompilace a vyhledejte –with-llvm ve výstupu. Příklad distribuce PGDG RPM:

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'

Proč LLVM JIT?

Všechno to začalo asi před dvěma lety, jak je vysvětleno v příspěvku Adres Freund, kdy se vyhodnocování výrazů a deformace n-tic ukázaly jako překážky při urychlování velkých dotazů. Po přidání implementace JIT „je samotné vyhodnocování výrazů více než desetkrát rychlejší než dříve“ slovy Andrese. Dále sekce Q&A, která končí jeho příspěvek, vysvětluje volbu LLVM oproti jiným implementacím.

Zatímco LLVM byl vybraný poskytovatel, parametr GUC jit_provider lze použít k nasměrování na jiného poskytovatele JIT. Všimněte si však, že podpora inlining je k dispozici pouze při použití poskytovatele LLVM kvůli způsobu, jakým proces sestavení funguje.

Kdy JIT?

Dokumentace je jasná:dlouhotrvající dotazy, které jsou vázány na CPU, budou těžit z kompilace JIT. Kromě toho diskuse o mailing listu, na které se odkazuje v tomto blogu, poukazují na to, že JIT je příliš drahý na dotazy, které se provádějí pouze jednou.

Ve srovnání s programovacími jazyky má PostgreSQL tu výhodu, že „ví“, kdy je třeba JIT, a to tím, že se spoléhá na plánovač dotazů. Za tímto účelem byla zavedena řada parametrů GUC. Pro ochranu uživatelů před negativními překvapeními při aktivaci JIT jsou parametry související s náklady záměrně nastaveny na přiměřeně vysoké hodnoty. Všimněte si, že nastavení parametrů nákladů JIT na „0“ vynutí kompilaci všech dotazů JIT a v důsledku toho zpomalí všechny vaše dotazy.

Zatímco JIT může být obecně prospěšný, existují případy, kdy jeho aktivace může být škodlivá, jak je popsáno v commitu b9f2d4d3.

Jak provést JIT?

Jak bylo zmíněno výše, binární balíčky RPM podporují LLVM. Aby však kompilace JIT fungovala, je vyžadováno několik dalších kroků:

Pro vtip:

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

Všimněte si, že jsem povolil JIT (který je ve výchozím nastavení zakázán po diskuzi pgsql-hackers odkazované v commit b9f2d4d3). Také jsem upravil cenu parametrů JIT, jak je navrženo v dokumentaci.

První nápovědu naleznete v souboru src/backend/jit/README, na který se odkazuje v dokumentaci JIT:

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

Vzhledem k tomu, že balíček RPM nestahuje závislost JIT automaticky – jak bylo rozhodnuto po intenzivních diskuzích (viz celé vlákno), musíme jej nainstalovat ručně:

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

Po dokončení instalace můžeme ihned testovat:

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

Můžeme také zobrazit podrobnosti o JIT na pracovníka:

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

Implementace JIT může také využít funkce paralelního provádění dotazů. Pro příklad nejprve deaktivujme paralelizaci:

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

Stejný příkaz s povolenými paralelními dotazy se dokončí za polovinu času:

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

Bylo pro mě zajímavé porovnat výsledky testů diskutovaných v tomto příspěvku během počátečních fází implementace JIT s konečnou verzí. Nejprve se ujistěte, že jsou splněny podmínky v původním testu, tj. databáze se musí vejít do paměti:

[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
Stáhněte si Whitepaper Today Správa a automatizace PostgreSQL s ClusterControlZjistěte, co potřebujete vědět k nasazení, monitorování, správě a škálování PostgreSQLStáhněte si Whitepaper

Spusťte testy se zakázaným JIT:

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

Dále spusťte testy s povoleným JIT:

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

To je zrychlení asi o 25 % pro první testovací případ a 40 % pro druhý!

Nakonec je důležité si uvědomit, že u připravených příkazů se kompilace JIT provádí při prvním spuštění funkce.

Závěr

Ve výchozím nastavení je kompilace JIT zakázána a pro systémy založené na RPM instalační program nenaznačí, že je třeba nainstalovat balíček JIT poskytující bitový kód pro výchozího poskytovatele LLVM.

Při sestavování ze zdrojů věnujte pozornost příznakům kompilace, abyste se vyhnuli problémům s výkonem, například pokud jsou povoleny aserce LLVM.

Jak je uvedeno na seznamu pgsql-hackers, dopad JIT na kalkulaci ještě není plně pochopen, takže před povolením celého clusteru funkcí je nutné pečlivé plánování, protože dotazy, které by jinak mohly těžit z kompilace, mohou ve skutečnosti běžet pomaleji. JIT však lze povolit na základě dotazu.

Podrobné informace o implementaci kompilace JIT naleznete v protokolech projektu Git, Commitfests a e-mailovém vláknu pgsql-hackers.

Šťastný JITing!


  1. Rozdíl časových razítek v hodinách pro PostgreSQL

  2. 7 tipů osvědčených postupů pro hromadné načítání dat PostgreSQL

  3. DevOps:DBA nebo vývojář – Jak dosáhnout správné rovnováhy

  4. Zaujměte lidský přístup ke správě dat