Secara historis PostgreSQL telah menyediakan fitur kompilasi dalam bentuk kompilasi sebelumnya untuk fungsi PL/pgSQL dan kompilasi ekspresi yang diperkenalkan versi 10. Tak satu pun dari mereka yang menghasilkan kode mesin.
JIT untuk SQL telah dibahas bertahun-tahun yang lalu, dan untuk PostgreSQL fitur tersebut merupakan hasil dari perubahan kode yang substansial.
Untuk memeriksa apakah biner PostgreSQL dibangun dengan dukungan LLVM, gunakan perintah pg_configure untuk menampilkan flag kompilasi dan cari –with-llvm di output. Contoh untuk distribusi 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'
Mengapa LLVM JIT?
Semuanya dimulai sekitar dua tahun yang lalu seperti yang dijelaskan dalam posting Adres Freund ketika evaluasi ekspresi dan deformasi tuple terbukti menjadi penghalang dalam mempercepat kueri besar. Setelah menambahkan implementasi JIT "evaluasi ekspresi itu sendiri lebih dari sepuluh kali lebih cepat dari sebelumnya" dalam kata-kata Andres. Selanjutnya, bagian T&J yang mengakhiri postingannya menjelaskan pilihan LLVM dibandingkan implementasi lainnya.
Sementara LLVM adalah penyedia yang dipilih, parameter GUC jit_provider dapat digunakan untuk menunjuk ke penyedia JIT lain. Perhatikan bahwa dukungan inlining hanya tersedia saat menggunakan penyedia LLVM, karena cara kerja proses build.
Kapan JIT?
Dokumentasinya jelas:kueri yang berjalan lama yang terikat CPU akan mendapat manfaat dari kompilasi JIT. Selain itu, diskusi milis yang dirujuk di seluruh blog ini menunjukkan bahwa JIT terlalu mahal untuk kueri yang dieksekusi hanya sekali.
Dibandingkan dengan bahasa pemrograman, PostgreSQL memiliki keunggulan "mengetahui" kapan harus JIT, dengan mengandalkan perencana kueri. Untuk itu sejumlah parameter GUC diperkenalkan. Untuk melindungi pengguna dari kejutan negatif saat mengaktifkan JIT, parameter terkait biaya sengaja disetel ke nilai yang cukup tinggi. Perhatikan bahwa menyetel parameter biaya JIT ke '0' akan memaksa semua kueri dikompilasi JIT dan akibatnya memperlambat semua kueri Anda.
Meskipun JIT secara umum dapat bermanfaat, ada kasus ketika mengaktifkannya dapat merugikan seperti yang dibahas dalam komit b9f2d4d3.
Bagaimana cara JIT?
Seperti yang disinggung di atas paket biner RPM diaktifkan LLVM. Namun, agar kompilasi JIT berfungsi, diperlukan beberapa langkah tambahan:
Yaitu:
[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)
Perhatikan bahwa saya memang mengaktifkan JIT (yang dinonaktifkan secara default mengikuti diskusi pgsql-hacker yang dirujuk dalam komit b9f2d4d3). Saya juga menyesuaikan biaya parameter JIT seperti yang disarankan dalam dokumentasi.
Petunjuk pertama ditemukan di file src/backend/jit/README yang dirujuk dalam dokumentasi JIT:
Which shared library is loaded is determined by the jit_provider GUC, defaulting to "llvmjit".
Karena paket RPM tidak menarik ketergantungan JIT secara otomatis — seperti yang diputuskan setelah diskusi intensif (lihat utas lengkapnya) — kita perlu menginstalnya secara manual:
[[email protected] ~]# dnf install postgresql11-llvmjit
Setelah penginstalan selesai, kami dapat langsung menguji:
[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)
Kami juga dapat menampilkan detail JIT per pekerja:
[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)
Implementasi JIT juga dapat memanfaatkan fitur eksekusi query paralel. Sebagai contoh, pertama-tama, nonaktifkan paralelisasi:
[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)
Perintah yang sama dengan kueri paralel yang diaktifkan selesai dalam separuh waktu:
[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)
Saya menemukan menarik untuk membandingkan hasil dari tes yang dibahas dalam posting ini, selama tahap awal implementasi JIT versus versi final. Pertama-tama pastikan kondisi dalam pengujian asli terpenuhi yaitu database harus sesuai dengan memori:
[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
Unduh Whitepaper Hari Ini Pengelolaan &Otomatisasi PostgreSQL dengan ClusterControlPelajari tentang apa yang perlu Anda ketahui untuk menerapkan, memantau, mengelola, dan menskalakan PostgreSQLUnduh Whitepaper Jalankan pengujian dengan JIT dinonaktifkan:
[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)
Selanjutnya jalankan pengujian dengan JIT diaktifkan:
[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)
Itu adalah peningkatan sekitar 25% untuk kasus uji pertama dan 40% untuk yang kedua!
Terakhir, penting untuk diingat bahwa untuk pernyataan yang disiapkan, kompilasi JIT dilakukan saat fungsi pertama kali dijalankan.
Kesimpulan
Secara default, kompilasi JIT dinonaktifkan, dan untuk sistem berbasis RPM, penginstal tidak akan memberi petunjuk tentang perlunya menginstal paket JIT yang menyediakan bitcode untuk LLVM penyedia default.
Saat membangun dari sumber, perhatikan tanda kompilasi untuk menghindari masalah kinerja, misalnya jika pernyataan LLVM diaktifkan.
Seperti yang dibahas pada daftar pgsql-hacker, dampak JIT pada penetapan biaya belum sepenuhnya dipahami sehingga perencanaan yang cermat diperlukan sebelum mengaktifkan kluster fitur secara luas, karena kueri yang dapat diuntungkan dari kompilasi sebenarnya dapat berjalan lebih lambat. Namun, JIT dapat diaktifkan pada basis per kueri.
Untuk informasi mendalam tentang implementasi kompilasi JIT, tinjau log Git proyek, Commitfests, dan utas email pgsql-hackers.
Selamat JIT!