Trong lịch sử PostgreSQL đã cung cấp các tính năng biên dịch dưới dạng biên dịch trước thời hạn cho các hàm PL / pgSQL và biên dịch biểu thức phiên bản 10 đã được giới thiệu. Tuy nhiên, không ai trong số đó tạo ra mã máy.
JIT cho SQL đã được thảo luận nhiều năm trước và đối với PostgreSQL, tính năng này là kết quả của một sự thay đổi mã đáng kể.
Để kiểm tra xem tệp nhị phân PostgreSQL có được xây dựng với hỗ trợ LLVM hay không, hãy sử dụng lệnh pg_configure để hiển thị các cờ biên dịch và tìm –with-llvm trong đầu ra. Ví dụ về phân phối 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'
Tại sao sử dụng LLVM JIT?
Tất cả bắt đầu khoảng hai năm trước như đã giải thích trong bài đăng của Adres Freund khi đánh giá biểu thức và biến dạng tuple được chứng minh là rào cản trong việc tăng tốc các truy vấn lớn. Sau khi thêm triển khai JIT, "đánh giá biểu thức tự nó nhanh hơn mười lần so với trước đây" theo lời của Andres. Hơn nữa, phần Hỏi và Đáp ở cuối bài đăng của anh ấy giải thích sự lựa chọn của LLVM so với các cách triển khai khác.
Trong khi LLVM là nhà cung cấp đã chọn, tham số GUC jit_provider có thể được sử dụng để trỏ đến nhà cung cấp JIT khác. Mặc dù vậy, lưu ý rằng hỗ trợ nội tuyến chỉ khả dụng khi sử dụng nhà cung cấp LLVM, do cách thức hoạt động của quy trình xây dựng.
Khi nào đến JIT?
Tài liệu rõ ràng:các truy vấn chạy dài bị ràng buộc bởi CPU sẽ được hưởng lợi từ việc biên dịch JIT. Ngoài ra, các cuộc thảo luận về danh sách gửi thư được tham khảo trong blog này chỉ ra rằng JIT quá đắt đối với các truy vấn chỉ được thực thi một lần.
So với các ngôn ngữ lập trình, PostgreSQL có lợi thế là “biết” khi nào thì sử dụng JIT, bằng cách dựa vào công cụ lập kế hoạch truy vấn. Do đó, một số tham số GUC đã được đưa vào. Để bảo vệ người dùng khỏi những bất ngờ tiêu cực khi bật JIT, các thông số liên quan đến chi phí được cố ý đặt thành các giá trị cao hợp lý. Lưu ý rằng việc đặt thông số chi phí JIT thành ‘0’ sẽ buộc tất cả các truy vấn phải được biên dịch JIT và do đó làm chậm tất cả các truy vấn của bạn.
Mặc dù JIT nói chung có thể có lợi, nhưng có những trường hợp khi kích hoạt nó có thể gây bất lợi như đã thảo luận trong cam kết b9f2d4d3.
Làm thế nào để JIT?
Như đã đề cập ở trên, các gói nhị phân RPM được kích hoạt LLVM. Tuy nhiên, để quá trình biên dịch JIT hoạt động, cần thực hiện một số bước bổ sung:
Nói một cách thông minh:
[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)
Lưu ý rằng tôi đã bật JIT (được tắt theo mặc định sau cuộc thảo luận pgsql-hacker được tham chiếu trong commit b9f2d4d3). Tôi cũng đã điều chỉnh chi phí của các thông số JIT như được đề xuất trong tài liệu.
Gợi ý đầu tiên được tìm thấy trong tệp src / backend / jit / README được tham chiếu trong tài liệu JIT:
Which shared library is loaded is determined by the jit_provider GUC, defaulting to "llvmjit".
Vì gói RPM không tự động kéo phần phụ thuộc vào JIT - vì nó đã được quyết định sau các cuộc thảo luận kỹ lưỡng (xem toàn bộ chuỗi) - nên chúng tôi cần cài đặt nó theo cách thủ công:
[[email protected] ~]# dnf install postgresql11-llvmjit
Sau khi cài đặt hoàn tất, chúng tôi có thể kiểm tra ngay lập tức:
[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)
Chúng tôi cũng có thể hiển thị chi tiết JIT cho mỗi công nhân:
[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)
Việc triển khai JIT cũng có thể tận dụng tính năng thực thi truy vấn song song. Để làm ví dụ, trước tiên hãy tắt tính năng song song:
[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)
Lệnh tương tự với các truy vấn song song được bật sẽ hoàn thành trong một nửa thời gian:
[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)
Tôi cảm thấy thú vị khi so sánh kết quả từ các thử nghiệm được thảo luận trong bài đăng này, trong giai đoạn triển khai JIT ban đầu so với phiên bản cuối cùng. Trước tiên, hãy đảm bảo các điều kiện trong thử nghiệm ban đầu được đáp ứng, tức là cơ sở dữ liệu phải vừa với bộ nhớ:
[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
Tải xuống Báo cáo chính thức hôm nay Quản lý &Tự động hóa PostgreSQL với ClusterControlTìm hiểu về những điều bạn cần biết để triển khai, giám sát, quản lý và mở rộng PostgreSQLTải xuống Báo cáo chính thức Chạy các bài kiểm tra với JIT bị vô hiệu hóa:
[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)
Tiếp theo, hãy chạy các bài kiểm tra với JIT được bật:
[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)
Đó là tốc độ tăng khoảng 25% cho trường hợp thử nghiệm đầu tiên và 40% cho trường hợp thứ hai!
Cuối cùng, điều quan trọng cần nhớ là đối với các câu lệnh đã chuẩn bị, quá trình biên dịch JIT được thực hiện khi hàm được thực thi lần đầu tiên.
Kết luận
Theo mặc định, biên dịch JIT bị tắt và đối với các hệ thống dựa trên RPM, trình cài đặt sẽ không gợi ý về việc cần cài đặt gói JIT cung cấp mã bit cho nhà cung cấp mặc định LLVM.
Khi xây dựng từ các nguồn, hãy chú ý đến các cờ biên dịch để tránh các vấn đề về hiệu suất, chẳng hạn như nếu các xác nhận LLVM được bật.
Như đã thảo luận trong danh sách pgsql-hacker, tác động của JIT đối với chi phí vẫn chưa được hiểu đầy đủ vì vậy cần phải lập kế hoạch cẩn thận trước khi kích hoạt toàn bộ cụm tính năng, vì các truy vấn có thể được hưởng lợi từ việc biên dịch có thể thực sự chạy chậm hơn. Tuy nhiên, JIT có thể được kích hoạt trên cơ sở mỗi truy vấn.
Để biết thông tin chuyên sâu về việc triển khai biên dịch JIT, hãy xem lại nhật ký Git của dự án, các bản cam kết và chuỗi thư pgsql-hacker.
Chúc JITing vui vẻ!