Dive into Apache Arrow(その3)- SSD-to-GPU Direct SQL対応

ここ最近取り組んでいた Arrow_Fdw 機能がようやく動くようになったので、性能ベンチマークを行ってみた。
今回のエントリでは順を追って説明する事にしてみたい。

Arrow_Fdwとは

PostgreSQLにはFDW (Foreign Data Wrapper) という機能があり、PostgreSQL管理下にあるデータ(テーブルなど)だけでなく、外部のデータソースをあたかもテーブルであるかのように読み出す事ができる。一部のモジュールでは書込みにも対応している。
例えば、CSVファイルは構造化データを表現するための方法の一つである。もちろん、PostgreSQLの内部形式とデータ形式が違うので、そのままではコレをSQLの世界から取り扱う事ができないが、間にfile_fdwというモジュールが介在する事で、CSVPostgreSQLの内部形式へとデータ形式を変換している。

この枠組みは多種多様なデータソースに対して適用する事が可能で、リモートのPostgreSQLサーバをデータソースとするpostgres_fdwや、変わり種としては、Web APIを用いてTwitterのタイムラインからデータを取得するtwitter_fdw*1などがある。
これを利用してApache Arrow形式のファイルを外部テーブルにマップし、SQLから参照できるようにするのがArrow_Fdwモジュールである。

例えば、/opt/nvme/t.arrowというArrow形式ファイルをArrow_Fdwを用いてマップする場合は、以下の構文を用いる事ができる。

postgres=# IMPORT FOREIGN SCHEMA hoge
             FROM SERVER arrow_fdw
             INTO public OPTIONS (file '/opt/nvme/t.arrow');
IMPORT FOREIGN SCHEMA
postgres=# \d hoge
                   Foreign table "public.hoge"
 Column |  Type   | Collation | Nullable | Default | FDW options
--------+---------+-----------+----------+---------+-------------
 id     | integer |           |          |         |
 aid    | integer |           |          |         |
 bid    | integer |           |          |         |
 ymd    | date    |           |          |         |
 cat    | text    |           |          |         |
 md5    | text    |           |          |         |
Server: arrow_fdw
FDW options: (file '/opt/nvme/t.arrow')

もちろん、いつも通りにCREATE FOREIGN TABLE構文を用いてもよいが、Arrow形式ファイルにはそれ自体スキーマ定義が含まれており、列リストの定義がこれと厳密に一致していないとエラーとなるため、IMPORT FOREIGN SCHEMA構文を用いてArrow形式ファイルから自動的に外部テーブルの定義を作り出す方がお勧めである。

Apache Arrowファイル形式自体の説明は、こちらのエントリをご覧いただきたい。
kaigai.hatenablog.com

要は、列指向でデータが編成されているため、集計・解析系ワークロードに対して全てのデータをストレージから読み出す必要はなく、被参照列のデータのみを読み出せばよいためにI/Oを効率的に(高い密度で)行う事が可能となるという話である。

ベンチマーク

早速ベンチマークを行ってみる。使用したのは、いつもの1Uラックサーバ でスペックおよびシステム構成は以下の通り。

chassis Supermicro SYS-1019GP-TT
CPU Intel Xeon Gold 6126T (2.6GHz, 12C) x1
RAM 192GB (32GB DDR4-2666 x6)
GPU NVIDIA Tesla V100 (5120C, 16GB) x1
SSD Intel SSD DC P4600 (HHHL; 2.0TB) x3
(md-raid0によるストライピング構成)
HDD 2.0TB (SATA; 72krpm) x6
network 10Gb ethernet x2 ports
OS Ret Hat Enterprise Linux 7.6
CUDA 10.1 + NVIDIA Driver 418.40.04
DB PostgreSQL v11.2
PG-Strom v2.2devel

ベンチマークに使用したワークロードは、いつもの Star Schema Benchmark で、これも今まで通り scaling factor=401 なので、DBサイズは353GB。これをExt4で初期化したNVME-SSD区画上にArrow形式ファイルとして書き出すと約310GBであった。

実行結果は以下の通り。じゃん。

列指向データなので、クエリが参照する列の数と幅によって大幅にパフォーマンスが変わってくるのは致し方ないが、2.2~2.3GB/s前後であったPostgreSQL v11や、7.2~7.9GB/s前後であったPG-Strom v2.2develの結果と比べて、大幅に性能が改善されている事が分かる。
単なる逆数ではあるが、49GB/sというのは300GB超のテーブルに対する全件スキャン+集計が6秒台で返ってくる速度、25GB/sというのは12秒台で返ってくる速度なので、これ位のデータ処理能力をシングルノードで実現できるのであれば、システム構成を検討する時の様々な前提条件が変わってくるだろう。

こちらはもう一つ別の指標で。このベンチマークを採取するにあたり、Star Schema Benchmark 全13本のクエリを3回ずつ実行し、クエリ毎の最良値を使ってスループットを計算しているが、13本 x 3回 = 39回のクエリを実行するのに要した時間を積算したもの。

PG-Strom + Arrow_Fdwのケースだと、ほんの僅か、子供のオムツ替え程度の時間であるが、元々のPostgreSQLのケースでは2時間弱と長めのお昼寝程度の時間を要している。
しかもこれは全て同一のハードウェア、特にデータを全て高速なNVME-SSDに載せているがために、元々のPostgreSQLでも2.2~2.3GB/sのパフォーマンスが出ているが、既存システムでAll-Flashという構成は多くはないと考えられる。旧来の磁気ディスクを用いたDBシステムとの比較であれば、どの程度のスコアになるだろう??

測定結果の妥当性

なお、測定ミスなどであると恥ずかしいので、性能の妥当性を考察してみた。

以下は Arrow_Fdw を用いてArrow形式ファイルをマップした flineorder テーブルの列定義であるが、28.7GB/sのクエリ実行スループットを記録した Q2_1 を例に取ると、lo_suppkeylo_orderdatelo_orderpriorityおよびlo_supplycost列が参照されている。

 Foreign table "public.flineorder"
       Column       |     Type      | Size
--------------------+---------------+--------------------------------
 lo_orderkey        | numeric       | 35.86GB
 lo_linenumber      | integer       |  8.96GB
 lo_custkey         | numeric       | 35.86GB
 lo_partkey         | integer       |  8.96GB
 lo_suppkey         | numeric       | 35.86GB   <-- ★Q2_1で参照
 lo_orderdate       | integer       |  8.96GB   <-- ★Q2_1で参照
 lo_orderpriority   | character(15) | 33.61GB   <-- ★Q2_1で参照
 lo_shippriority    | character(1)  |  2.23GB
 lo_quantity        | integer       |  8.96GB
 lo_extendedprice   | bigint        | 17.93GB
 lo_ordertotalprice | bigint        | 17.93GB
 lo_discount        | integer       |  8.96GB
 lo_revenue         | bigint        | 17.93GB
 lo_supplycost      | bigint        | 17.93GB   <-- ★Q2_1で参照
 lo_tax             | integer       |  8.96GB
 lo_commit_date     | character(8)  | 17.93GB
 lo_shipmode        | character(10) | 22.41GB
FDW options: (file '/opt/nvme/lineorder_s401.arrow')  ... file size = 310GB

これら被参照列の行データサイズの合計は、310GB中96.4GBである。つまり、このクエリではファイル全体の31.1%だけしかデータを読み出す必要がなく、読出しサイズ(96.4GB)をQ2_1の実行時間11.06sで割ると、96.4÷11.06 = 8.7GB/s なので、Intel DC P4600 x3 の読出し速度としては概ね妥当な値という事になる。(一安心である)

今後の展望

現状「とりあえず動きました」という段階なので、まだこの後、試験や修正などに相応の時間がかかる事はご容赦いただきたい。

更なる性能改善に向け、今後やってみたいと考えているのが、マルチGPUを搭載したサーバによるベンチマーク

上記の構成イメージは、Supermicro社のHPC向けサーバ SYS-4029GP-TRT2に、Tesla GPUを4台と、外付けNVME-SSDのエンクロージャを接続するHBAカード*2を4台搭載する。このマシンはPCIeスイッチを搭載しているので、SSDGPU間で直接データ転送を行う場合にはCPUに負荷を与えない。

ブロック図で記載すると以下のようになる。GPU 1台あたりNVME-SSD 4台が一個のユニットとなり、一台のサーバの中でそれぞれが並列に動作する。

今回のベンチマークは、PCIeスイッチが無いとか、SSDの台数が3台だという細かな違いを除けば、概ねこちらの構成で1ユニット分の性能であると言う事ができるだろう。それで15GB/s~49GB/sの性能値を出せているという事は、期待値としてその4倍、シングルノード100GB/sの処理性能というのはあながち実現不可能な絵空事という訳でもないだろう。


こちらは、今年の正月に品川神社に奉納した絵馬。
ちょっと目標値の設定が低かったかもしれぬ。

*1:今でもメンテナンスされてるんだろうか?

*2:NVMEoFを使う場合、100Gb-NICでも可。むしろ巨大データだとこちらの方がスケーラビリティは高くなる。