従前、AWSの提供するGPUインスタンス g2.* に搭載されているGPUはGRID K520というちょっと古いモデルで、PG-Stromは非対応だった。
理由は、一年ほど前にComputing Capability 3.5以降で対応のDynamic Parallelism機能を使うように全面的に作り直したからで、詳細は以下のエントリを参照。
その後、昨年の10月にAWSは新世代*1のGPUインスタンスを新たにリリースした。
これでPG-Stromの動作要件を満たすようになった上に、特にメモリ搭載量で相応の強化が行われたため、例えばPGconf.ASIAで発表を行った創薬領域の類似度サーチのような、I/Oが支配的でないようなワークロードであれば相応の効果が見込める、ハズである。
発表から少し間が空いてしまったが、p2.*インスタンスで動作検証を行い、当該環境をAMIイメージとして公開してみた。
環境構築
基本的にはAMIを探してポチポチするだけである。
最初に、リージョンを p2.* インスタンス提供済みのリージョンに切り替える。
現状、東海岸の「バージニア北部」、西海岸の「オレゴン」、欧州の「アイルランド」での提供が開始されている模様。東京はまだである*2。
とりあえず、今回は「オレゴン」リージョンを使用。
また、AWSアカウントを作成直後は p2.* インスタンスの起動制限数が 0 に設定されているため、p2.xlargeインスタンスを起動できるよう、EC2ダッシュボードの『制限』から『制限緩和のリクエスト』を行う必要がある。豆知識。
次に、コミュニティAMIから『PGStrom』で検索すれば、夜なべして作ったAMIイメージがヒットする。
これは PG-Strom v1.0 + PostgreSQL v9.5.5 + CUDA 7.5環境を CentOS7(x86_64) 上に構築したもので、テスト用のデータも既にセットアップ済みのものである。
次に、起動すべきインスタンスを選択する。
p2.*インスタンスは以下の3種類提供されており、今回は『p2.xlarge』タイプを使用した。
なお、Amazon様も『高性能データベース』用途ときちんと書いていてくれておりますw
一応、PG-StromはマルチGPUにも対応しているが、CPU並列を使えないPostgreSQL v9.5系列ではGPUへのデータ供給の方が先にボトルネックになってしまうので、あまり意味はない。
この辺は、現在開発中の PostgreSQL v9.6 ベースの実装でうまくハンドリングできるようになる。
インスタンスの構成を確認して起動。ストレージはroot区画に24GBをアタッチするだけのシンプルなものがデフォルト構成。
しばらく待っているとインスタンスが立ち上がってくる。
『ec2-user』でログインする事ができる。
動作確認
早速動作確認を行う。ec2-userはlocalhost経由でPostgreSQLサーバに接続できるよう設定されている。
[ec2-user@ip-172-31-24-196 ~]$ psql postgres psql (9.5.5) Type "help" for help. postgres=#
Tesla K80 GPUが搭載されている事が分かる。
物理的には一枚のカード上にGPU 2個を搭載するデバイスなので、一枚のカードを他の誰かとシェアしている事になる。
postgres=# SELECT * FROM pgstrom_device_info(); id | property | value ----+-------------------------------------------------------+-------------- 0 | Device name | Tesla K80 0 | Total global memory size | 11519 MBytes 0 | max threads per block | 1024 0 | Maximum block dimension X | 1024 0 | Maximum block dimension Y | 1024 0 | Maximum block dimension Z | 64 0 | Maximum grid dimension X | 2147483647 0 | Maximum grid dimension Y | 65535 0 | Maximum grid dimension Z | 65535 0 | Maximum shared memory available per block | 48 KBytes 0 | Memory available on device for __constant__ | 64 KBytes 0 | Warp size in threads | 32 0 | Maximum pitch in bytes allowed by memory copies | 2147483647 0 | Maximum number of 32bit registers available per block | 65536 0 | Typical clock frequency in kilohertz | 823 MHz 0 | Alignment requirement for textures | 512 0 | Number of multiprocessors on device | 13 0 | Has kernel execution timeout | False 0 | Integrated with host memory | False 0 | Host memory can be mapped to CUDA address space | True 0 | Compute mode | Default 0 | Alignment requirement for surfaces | 512 0 | Multiple concurrent kernel support | True 0 | Device has ECC support enabled | True 0 | PCI bus ID of the device | 0 0 | PCI device ID of the device | 30 0 | Device is using TCC driver model | False 0 | Peak memory clock frequency | 2505 MHz 0 | Global memory bus width | 384 bits 0 | Size of L2 cache in bytes | 1536 KBytes 0 | Maximum threads per multiprocessor | 2048 0 | Number of asynchronous engines | 2 0 | Device shares unified address space | True 0 | PCI domain ID of the device | 0 0 | Major compute capability version number | 3 0 | Minor compute capability version number | 7 0 | Device supports stream priorities | True 0 | Device supports caching globals in L1 | True 0 | Device supports caching locals in L1 | True 0 | Maximum shared memory per multiprocessor | 112 KBytes 0 | Maximum number of 32bit registers per multiprocessor | 131072 0 | Device can allocate managed memory on this system | True 0 | Device is on a multi-GPU board | False 0 | Unique id if device is on a multi-GPU board | 0 (44 rows)
早速、シンプルな集計のクエリを叩いてみるが、その前にメインのテーブルに対してpg_prewarm
を実行しておくが吉。
gp2ストレージタイプの場合、バッファに載っていないデータをロードするのに割と時間がかかるようなので、I/Oネックになってしまうと、CPUとかGPUとかいう次元の遥か手前で引っかかってしまう。
postgres=# SELECT pg_prewarm('t0'::regclass); pg_prewarm ------------ 833334 (1 row)
実行計画を確認する。JOIN+GROUP BYをGPUで実行するようにプランが選択された事がわかる。
postgres=# EXPLAIN SELECT cat,count(*),avg(ax) FROM t0 NATURAL JOIN t1 GROUP BY cat; QUERY PLAN --------------------------------------------------------------------------------------------- HashAggregate (cost=3578311.16..3578311.48 rows=26 width=12) Group Key: t0.cat -> Custom Scan (GpuPreAgg) (cost=14139.24..2873626.84 rows=234 width=48) Reduction: Local + Global GPU Projection: cat, ax -> Custom Scan (GpuJoin) on t0 (cost=10139.24..2838812.04 rows=98599882 width=12) GPU Projection: t0.cat, t1.ax Depth 1: GpuHashJoin, HashKeys: (t0.aid) JoinQuals: (t0.aid = t1.aid) Nrows (in/out: 98.60%), KDS-Hash (size: 13.47MB, nbatches: 1) -> Seq Scan on t1 (cost=0.00..1935.00 rows=100000 width=12) (11 rows)
1億行のテーブルのJOINとGROUP BYで11.3秒要している。
セッションに接続後の初回実行だと、これに加えて、GPUコンテキストの初期化やGPUコードのJITコンパイルの時間も余分にかかるが、オンプレに比べるとAWSの環境ではこの辺が多少もっさりする印象がある。
postgres=# SELECT cat,count(*),avg(ax) FROM t0 NATURAL JOIN t1 GROUP BY cat; cat | count | avg -----+---------+------------------ nnn | 3845736 | 49.9122204644341 ccc | 3842975 | 49.9253161146813 ddd | 3841209 | 49.9346989307005 aaa | 3848221 | 49.9265219996308 kkk | 3843481 | 49.9232348058601 fff | 3845484 | 49.9434949581969 iii | 3846743 | 49.9227199719054 jjj | 3846076 | 49.9292863471083 qqq | 3845646 | 49.945213365697 hhh | 3842519 | 49.9266693322143 ttt | 3846725 | 49.9232478784252 ooo | 3847927 | 49.9346102129999 zzz | 3847116 | 49.9320751724648 lll | 3848447 | 49.927865906661 www | 3846691 | 49.9537192102014 bbb | 3845315 | 49.9281298849625 ppp | 3850842 | 49.9149069792416 eee | 3846285 | 49.931458202446 xxx | 3845570 | 49.9577920754281 ggg | 3845044 | 49.9409383291351 rrr | 3847816 | 49.9341189910578 uuu | 3845813 | 49.9295202543591 vvv | 3849157 | 49.9253944163053 yyy | 3843414 | 49.9364087656463 mmm | 3848758 | 49.9033622681507 sss | 3846990 | 49.9213589517191 (26 rows) Time: 11326.834 ms
一方、PG-Stromを無効にした場合は以下のように、同じクエリの実行に51.0秒を要している。トゥットゥルー。
postgres=# EXPLAIN SELECT cat,count(*),avg(ax) FROM t0 NATURAL JOIN t1 GROUP BY cat; QUERY PLAN ----------------------------------------------------------------------------- HashAggregate (cost=3937016.94..3937017.26 rows=26 width=12) Group Key: t0.cat -> Hash Join (cost=3185.00..3197517.82 rows=98599882 width=12) Hash Cond: (t0.aid = t1.aid) -> Seq Scan on t0 (cost=0.00..1833334.00 rows=100000000 width=8) -> Hash (cost=1935.00..1935.00 rows=100000 width=12) -> Seq Scan on t1 (cost=0.00..1935.00 rows=100000 width=12) (7 rows) postgres=# SELECT cat,count(*),avg(ax) FROM t0 NATURAL JOIN t1 GROUP BY cat; cat | count | avg -----+---------+------------------ nnn | 3845736 | 49.9122204644368 ccc | 3842975 | 49.925316114681 ddd | 3841209 | 49.934698930695 aaa | 3848221 | 49.9265219996321 kkk | 3843481 | 49.9232348058579 fff | 3845484 | 49.9434949581975 iii | 3846743 | 49.9227199719 jjj | 3846076 | 49.9292863471066 qqq | 3845646 | 49.9452133657041 ttt | 3846725 | 49.9232478784215 hhh | 3842519 | 49.926669332217 zzz | 3847116 | 49.932075172461 ooo | 3847927 | 49.9346102129966 lll | 3848447 | 49.9278659066662 www | 3846691 | 49.9537192102029 bbb | 3845315 | 49.9281298849615 ppp | 3850842 | 49.9149069792369 eee | 3846285 | 49.931458202444 xxx | 3845570 | 49.9577920754269 ggg | 3845044 | 49.9409383291352 uuu | 3845813 | 49.9295202543602 rrr | 3847816 | 49.9341189910555 vvv | 3849157 | 49.9253944163048 yyy | 3843414 | 49.9364087656454 mmm | 3848758 | 49.9033622681445 sss | 3846990 | 49.9213589517182 (26 rows) Time: 51031.084 ms
p2.* インスタンスの登場でPG-Stromを使用するための環境の準備はずいぶんお手軽になった。デプロイ即利用できるようAMIイメージも用意してみたので、まだPG-Stromを触った事がないという方は、ぜひ試してみて頂ければと思う。