PG-Strom v5.1リリース
概要
PG-Strom v5.1における主要な変更は点は以下の通りです。
- パーティションに対応したGpuJoin/PreAggのサポートを追加しました。
- GPUコードのビルドを起動時に実行環境で行うようになりました。
- pg2arrowが並列処理に対応しました。
- CUDA Stackのサイズを適応的に設定するようになりました。
- 累積的なバグの修正
動作環境
- PostgreSQL v15.x, v16.x
- CUDA Toolkit 12.2 以降
- CUDA ToolkitのサポートするLinuxディストリビューション
- Intel x86 64bit アーキテクチャ(x86_64)
- NVIDIA GPU CC 6.0 以降 (Pascal以降; Volta以降を推奨)
パーティション対応
PostgreSQLパーティションへの対応自体はPG-Strom v3.0でも行われていましたが、うまく実行計画を作成できない事が多く、実験的ステータスを脱する事のできないものでした。そこで、PG-Strom v5.1では内部の設計を根本的に見直して再度実装し、再び正式な機能として取り入れました。
以下のlineorder
テーブルがパーティション化されており、date1
テーブルが非パーティション化テーブルである場合、これまでは、lineorder
配下のパーティションテーブルから読み出したデータを全てAppend
ノードによって結合された後でなければJOINする事ができませんでした。
通常、PG-StromはCPUをバイパスしてNVME-SSDからGPUへデータをロードして各種のSQL処理を行う(GPU-Direct SQL)ため、JOINに先立ってCPU側へデータを戻さねばならないというのは大きなペナルティです。
ssbm=# explain (costs off)
select sum(lo_extendedprice*lo_discount) as revenue
from lineorder,date1
where lo_orderdate = d_datekey
and d_year = 1993
and lo_discount between 1 and 3
and lo_quantity < 25;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate
-> Hash Join
Hash Cond: (lineorder.lo_orderdate = date1.d_datekey)
-> Append
-> Custom Scan (GpuScan) on lineorder__p1992 lineorder_1
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 91250920 -> 11911380]
GPU-Direct SQL: enabled (GPU-0)
-> Custom Scan (GpuScan) on lineorder__p1993 lineorder_2
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 91008500 -> 11980460]
GPU-Direct SQL: enabled (GPU-0)
-> Custom Scan (GpuScan) on lineorder__p1994 lineorder_3
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 91044060 -> 12150700]
GPU-Direct SQL: enabled (GPU-0)
-> Custom Scan (GpuScan) on lineorder__p1995 lineorder_4
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 91011720 -> 11779920]
GPU-Direct SQL: enabled (GPU-0)
-> Custom Scan (GpuScan) on lineorder__p1996 lineorder_5
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 91305650 -> 11942810]
GPU-Direct SQL: enabled (GPU-0)
-> Custom Scan (GpuScan) on lineorder__p1997 lineorder_6
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 91049100 -> 12069740]
GPU-Direct SQL: enabled (GPU-0)
-> Custom Scan (GpuScan) on lineorder__p1998 lineorder_7
GPU Projection: lo_extendedprice, lo_discount, lo_orderdate
GPU Scan Quals: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric)) [rows: 53370560 -> 6898138]
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on lineorder__p1999 lineorder_8
Filter: ((lo_discount >= '1'::numeric) AND (lo_discount <= '3'::numeric) AND (lo_quantity < '25'::numeric))
-> Hash
-> Seq Scan on date1
Filter: (d_year = 1993)
(37 rows)
PG-Strom v5.1では、非パーティションテーブルをプッシュダウンし、パーティション子テーブルから読み出したデータとJOINしてから結果を返す事ができるようになりました。
場合によってはGROUP-BY処理まで済ませた上でCPU側に戻す事もでき、例えば以下の例では、総数6億件のパーティション子テーブルから検索条件を満たす7千万行を返さねばならないところ、非パーティションテーブルであるdate1
とのJOINと、その次に実行する集約関数SUM()
をパーティション子テーブルにプッシュダウンする事で、CPUでは僅か8行を処理するだけで済んでいます。
INNER側の読出しが複数回発生するというデメリットはありますが(※将来のバージョンで改修される予定です)、このような書き換えによってCPUで処理すべきデータが大幅に減少し、処理速度の改善に寄与します。
ssbm=# explain (costs off)
select sum(lo_extendedprice*lo_discount) as revenue
from lineorder,date1
where lo_orderdate = d_datekey
and d_year = 1993
and lo_discount between 1 and 3
and lo_quantity < 25;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Aggregate
-> Append
-> Custom Scan (GpuPreAgg) on lineorder__p1992 lineorder_1
GPU Projection: pgstrom.psum(((lineorder_1.lo_extendedprice * lineorder_1.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_1.lo_discount >= '1'::numeric) AND (lineorder_1.lo_discount <= '3'::numeric) AND (lineorder_1.lo_quantity < '25'::numeric)) [rows: 91250920 -> 11911380]
GPU Join Quals [1]: (lineorder_1.lo_orderdate = date1.d_datekey) ... [nrows: 11911380 -> 1700960]
GPU Outer Hash [1]: lineorder_1.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1993 lineorder_2
GPU Projection: pgstrom.psum(((lineorder_2.lo_extendedprice * lineorder_2.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_2.lo_discount >= '1'::numeric) AND (lineorder_2.lo_discount <= '3'::numeric) AND (lineorder_2.lo_quantity < '25'::numeric)) [rows: 91008500 -> 11980460]
GPU Join Quals [1]: (lineorder_2.lo_orderdate = date1.d_datekey) ... [nrows: 11980460 -> 1710824]
GPU Outer Hash [1]: lineorder_2.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1994 lineorder_3
GPU Projection: pgstrom.psum(((lineorder_3.lo_extendedprice * lineorder_3.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_3.lo_discount >= '1'::numeric) AND (lineorder_3.lo_discount <= '3'::numeric) AND (lineorder_3.lo_quantity < '25'::numeric)) [rows: 91044060 -> 12150700]
GPU Join Quals [1]: (lineorder_3.lo_orderdate = date1.d_datekey) ... [nrows: 12150700 -> 1735135]
GPU Outer Hash [1]: lineorder_3.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1995 lineorder_4
GPU Projection: pgstrom.psum(((lineorder_4.lo_extendedprice * lineorder_4.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_4.lo_discount >= '1'::numeric) AND (lineorder_4.lo_discount <= '3'::numeric) AND (lineorder_4.lo_quantity < '25'::numeric)) [rows: 91011720 -> 11779920]
GPU Join Quals [1]: (lineorder_4.lo_orderdate = date1.d_datekey) ... [nrows: 11779920 -> 1682188]
GPU Outer Hash [1]: lineorder_4.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1996 lineorder_5
GPU Projection: pgstrom.psum(((lineorder_5.lo_extendedprice * lineorder_5.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_5.lo_discount >= '1'::numeric) AND (lineorder_5.lo_discount <= '3'::numeric) AND (lineorder_5.lo_quantity < '25'::numeric)) [rows: 91305650 -> 11942810]
GPU Join Quals [1]: (lineorder_5.lo_orderdate = date1.d_datekey) ... [nrows: 11942810 -> 1705448]
GPU Outer Hash [1]: lineorder_5.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1997 lineorder_6
GPU Projection: pgstrom.psum(((lineorder_6.lo_extendedprice * lineorder_6.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_6.lo_discount >= '1'::numeric) AND (lineorder_6.lo_discount <= '3'::numeric) AND (lineorder_6.lo_quantity < '25'::numeric)) [rows: 91049100 -> 12069740]
GPU Join Quals [1]: (lineorder_6.lo_orderdate = date1.d_datekey) ... [nrows: 12069740 -> 1723574]
GPU Outer Hash [1]: lineorder_6.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1998 lineorder_7
GPU Projection: pgstrom.psum(((lineorder_7.lo_extendedprice * lineorder_7.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_7.lo_discount >= '1'::numeric) AND (lineorder_7.lo_discount <= '3'::numeric) AND (lineorder_7.lo_quantity < '25'::numeric)) [rows: 53370560 -> 6898138]
GPU Join Quals [1]: (lineorder_7.lo_orderdate = date1.d_datekey) ... [nrows: 6898138 -> 985063]
GPU Outer Hash [1]: lineorder_7.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
-> Custom Scan (GpuPreAgg) on lineorder__p1999 lineorder_8
GPU Projection: pgstrom.psum(((lineorder_8.lo_extendedprice * lineorder_8.lo_discount))::double precision)
GPU Scan Quals: ((lineorder_8.lo_discount >= '1'::numeric) AND (lineorder_8.lo_discount <= '3'::numeric) AND (lineorder_8.lo_quantity < '25'::numeric)) [rows: 150 -> 1]
GPU Join Quals [1]: (lineorder_8.lo_orderdate = date1.d_datekey) ... [nrows: 1 -> 1]
GPU Outer Hash [1]: lineorder_8.lo_orderdate
GPU Inner Hash [1]: date1.d_datekey
Inner Siblings-Id: 2
GPU-Direct SQL: enabled (GPU-0)
-> Seq Scan on date1
Filter: (d_year = 1993)
(82 rows)
GPUコードの起動時ビルド
以前のバージョンのPG-Stromでは、予めビルドされたGPU向けのバイナリモジュールを配布する方式をとっていました。 これはシンプルではあるのですが、PG-Strom(PostgreSQL)実行環境のCUDA ToolkitやNVIDIAドライバのバージョンの組合せによっては、GPUバイナリモジュールを認識できず実行時エラーを起こしてしまう事がありました。典型的には、RPMパッケージをビルドした環境よりも古いバージョンのCUDA ToolkitやNVIDIAドライバが実行環境にインストールされている場合です。
PG-Strom v5.1では、起動時にGPU用のソースコードやCUDA Toolkitのバージョンを確認し、差分があればGPU向けバイナリモジュールをビルドするように変更されました。この修正により、PG-Stromは実行環境にインストールされたGPUデバイス、およびCUDA Toolkit向けのGPUバイナリモジュールを利用することができるようになりました。
一部のPG-Strom用GPUモジュールにはビルドに時間がかかるものがあります。そのため、PG-StromやCUDA Toolkitのバージョンアップ後、初回の起動時にはPG-Stromの機能が利用可能となるまで数分の時間がかかる場合があります。
pg2arrowの並列実行
pg2arrow
は新たに-n|--num-workers
オプションと-k|--parallel-keys
オプションをサポートするようになりました。
-n N_WORKERS
は指定した数のスレッドがそれぞれPostgreSQLに接続し、並列にクエリを実行した結果をApache Arrowファイルに書き込みます。クエリには特殊な文字列$(N_WORKERS)
と$(WORKER_ID)
を含む事ができ、これらはPostgreSQLにクエリを投げる際に、それぞれワーカー数とワーカー固有のID値に置き換えられます。ユーザはこれを利用して、各ワーカースレッドが読み出すタプルが互いに重複したり欠損したりしないように調整する必要があります。
もう一つの-k|--parallel-key
オプションは、引数で与えたカンマ区切りのキー値のそれぞれに対してワーカースレッドを起動し、クエリ中の$(PARALLEL_KEY)
をキーと置き換えた上で、これをPostgreSQLで実行した結果をApache Arrowファイルとして書き込みます。
例えば、lineorder
テーブルがパーティション化されており、子テーブルとして、lineorder__sun
, lineorder__mon
, ... lineorder__sat
が存在した場合、個々のワーカースレッドがパーティションの子テーブルをそれぞれスキャンするといった形で処理を並列化できます。
この場合、-k
オプションは-k sun,mon,tue,wed,thu,fri,sat
と指定し、-c
オプションにはSELECT * FROM lineorder__$(PARALLEL_KEY)
と指定すれば、7個のワーカースレッドがそれぞれパーティションの子テーブルをスキャンする事になります。
$ pg2arrow -d ssbm -c 'SELECT * FROM lineorder__$(PARALLEL_KEY)' -o /path/to/f_lineorder.arrow -k=sun,mon,tue,wed,thu,fri,sat --progress
worker:1 SQL=[SELECT * FROM lineorder__sun]
worker:3 SQL=[SELECT * FROM lineorder__tue]
worker:2 SQL=[SELECT * FROM lineorder__mon]
worker:4 SQL=[SELECT * FROM lineorder__wed]
worker:5 SQL=[SELECT * FROM lineorder__thu]
:
: