PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Преглед на компилацията точно навреме (JIT) за PostgreSQL

Исторически PostgreSQL предоставя функции за компилация под формата на предварителна компилация за PL/pgSQL функции, а версия 10 въвежда компилация на изрази. Нито един от тях обаче не генерира машинен код.

JIT за SQL беше обсъждан преди много години, а за PostgreSQL функцията е резултат от значителна промяна на кода.

За да проверите дали двоичният файл PostgreSQL е изграден с поддръжка на LLVM, използвайте командата pg_configure, за да покажете флаговете за компилиране и потърсете –with-llvm в изхода. Пример за разпределението на 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'

Защо LLVM JIT?

Всичко започна преди около две години, както е обяснено в публикацията на Adres Freund, когато оценката на израза и деформирането на кортежи се оказаха пречките за ускоряване на големи заявки. След добавяне на JIT реализацията „самата оценка на израза е повече от десет пъти по-бърза от преди“ по думите на Андрес. Освен това секцията с въпроси и отговори, завършваща публикацията му, обяснява избора на LLVM пред други реализации.

Докато LLVM беше избраният доставчик, GUC параметърът jit_provider може да се използва за насочване към друг доставчик на JIT. Имайте предвид обаче, че поддръжката за вграждане е достъпна само при използване на доставчика на LLVM, поради начина, по който работи процесът на изграждане.

Кога да JIT?

Документацията е ясна:продължително изпълняваните заявки, които са свързани с процесора, ще се възползват от JIT компилацията. Освен това дискусиите в пощенския списък, споменати в този блог, сочат, че JIT е твърде скъп за заявки, които се изпълняват само веднъж.

В сравнение с езиците за програмиране, PostgreSQL има предимството да „знае“ кога да JIT, като разчита на планировчика на заявки. За тази цел бяха въведени редица параметри на GUC. За да се предпазят потребителите от негативни изненади при активиране на JIT, свързаните с разходите параметри са умишлено зададени на разумно високи стойности. Обърнете внимание, че задаването на параметрите на разходите за JIT на „0“ ще принуди всички заявки да бъдат JIT-компилирани и в резултат на това ще забави всичките ви заявки.

Въпреки че JIT като цяло може да бъде полезен, има случаи, когато активирането му може да бъде вредно, както е обсъдено в комит b9f2d4d3.

Как да JIT?

Както бе споменато по-горе, двоичните пакети RPM са активирани с LLVM. Въпреки това, за да може JIT компилацията да работи, са необходими няколко допълнителни стъпки:

А именно:

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

Обърнете внимание, че активирах JIT (който е деактивиран по подразбиране след обсъждането на pgsql-hackers, споменато в комит b9f2d4d3). Също така коригирах цената на параметрите на JIT, както е предложено в документацията.

Първият намек се намира във файла src/backend/jit/README, посочен в документацията на JIT:

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

Тъй като пакетът RPM не изтегля зависимостта на JIT автоматично – както беше решено след интензивни дискусии (вижте цялата тема) – трябва да го инсталираме ръчно:

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

След като инсталацията приключи, можем да тестваме веднага:

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

Можем също да покажем подробностите за JIT на работник:

[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 може също да се възползва от функцията за паралелно изпълнение на заявка. За пример, първо нека деактивираме паралелизирането:

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

Същата команда с активирани паралелни заявки завършва за половината от времето:

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

Намирах интересно да сравня резултатите от тестовете, обсъдени в тази публикация, по време на началните етапи на внедряването на JIT спрямо окончателната версия. Първо се уверете, че условията в оригиналния тест са изпълнени, т.е. базата данни трябва да се побере в паметта:

[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
Изтеглете Бялата книга днес Управление и автоматизация на PostgreSQL с ClusterControl Научете какво трябва да знаете, за да внедрите, наблюдавате, управлявате и мащабирате PostgreSQLD Изтеглете Бялата книга

Изпълнете тестовете с деактивиран 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)

След това стартирайте тестовете с активиран 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)

Това е ускорение от около 25% за първия тестов случай и 40% за втория!

И накрая, важно е да запомните, че за подготвените оператори JIT компилацията се извършва, когато функцията се изпълнява за първи път.

Заключение

По подразбиране JIT компилацията е деактивирана и за системи, базирани на RPM, инсталаторът няма да намекне за необходимостта от инсталиране на JIT пакета, предоставящ бит кода за LLVM на доставчика по подразбиране.

Когато създавате от източници, обърнете внимание на флаговете за компилиране, за да избегнете проблеми с производителността, например ако LLVM твърденията са активирани.

Както беше обсъдено в списъка на pgsql-хакерите, въздействието на JIT върху разходите все още не е напълно разбрано, така че е необходимо внимателно планиране, преди да се разреши широк клъстер от функции, тъй като заявките, които иначе биха могли да се възползват от компилацията, всъщност могат да работят по-бавно. JIT обаче може да бъде активиран за всяка заявка.

За задълбочена информация относно внедряването на JIT компилацията прегледайте регистрационните файлове на Git на проекта, Commitfests и пощенската нишка на pgsql-hackers.

Честит JIT!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Има ли значение редът на таблиците в обединяване, когато се използват LEFT (външни) присъединявания?

  2. Регистрирайте и стартирайте PostgreSQL 9.0 като Windows Service

  3. Търсете в JSON масив за обект, съдържащ стойност, съответстваща на шаблон

  4. Как да направя заявка, използвайки полета в новия PostgreSQL JSON тип данни?

  5. Промяна на типа колона на по-дълги низове в релси