PostgreSQL 11から、JIT機能が追加されました。
あまり理解できていないのですが、どんな感じで動くのか、まずは触ってみたいと思います。
導入と動作確認にあたり、以下の資料を参考にさせていただきました。
https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/07/pg11_report-6.pdf
また、llvmについては参考リンクのページに事前に目を通しておくと、どんな事をしているのかイメージしやすいと思います。llvmのtutorialは、ざっと目を通しておくと良いかもしれません。
インストール
試してみた環境は以下です。
- Mac OS X mac High Sierra 10.13.5
- iMac (27-inch Late 2012) Intel Core i5 3.2GHz
llvmのインストール
PostgreSQLは、JITの実装としてデフォルトでLLVMを使用するようになっているようです。私の環境は、Homebrewを使用していますので、LLVMを以下のようにしてインストールします。
1 |
$ brew install --with-toolchain llvm |
PostgreSQL11beta2のインストール
自作のpgenv2[1]https://github.com/moritoru81/pgenv2でサクッとインストールします。
まず、configureオプションで--with-llvm
を追加します。
1 |
$ echo "--with-llvm" | pgenv configure -e |
続いて11beta2をインストールします。
1 2 3 4 5 |
$ CC="$(brew --prefix llvm)/bin/clang" \ CXX="$(brew --prefix llvm)/bin/clang++" \ CLANG="$(brew --prefix llvm)/bin/clang" \ LLVM_CONFIG="$(brew --prefix llvm)/bin/llvm-config" \ pgenv install -f -v -d 11beta2 |
11beta2をデフォルト使用にします。
1 2 3 |
$ pgenv global 11beta2 $ psql -V psql (PostgreSQL) 11beta2 |
JITに関連するGUCs
PostgreSQL 11のソースツリーにあるsrc/backend/jit/README
を参照すると、以下のGUCがjitの動作に関係してくるようです。テストで動作確認したい場合、以下のパラメータに小さなコスト値を設定することで、JITを発動しやすくできます。
jit | jitの有効または無効化。デフォルトON (利用可能な場合) |
jit_above_cost | jitコンパイル適用のコスト閾値。デフォルト100000 |
jit_optimize_above_cost | jitコンパイルされたプログラムを最適化するコスト閾値。デフォルト500000 。 |
jit_inline_above_cost | jitコンパイルされたプログラムの関数や演算をインライン化するコスト閾値。デフォルト500000 。 |
参考 https://www.postgresql.org/docs/11/static/runtime-config-query.html
1 2 3 4 |
SET jit TO on; SET jit_above_cost TO 10; SET jit_optimize_above_cost TO 10; SET jit_inline_above_cost TO 10; |
参考
- https://www.postgresql.org/docs/11/static/jit-decision.html
- src/backend/optimizer/plan/planner.c
- src/include/jit/jit.h
JITを動かしてみる
JITが活きてくるのは、CPUバウンドかつ長時間実行されるような処理、とドキュメントでも説明されています。そうでない場合は、JITコンパイルのオーバヘッドの方が大きくなってしまいます。
まずはJITが動くかクエリを実行してみます。GUCでJITが有効になるよう設定して、適当なクエリを実行してみます。演算式や関数が含まれるクエリが良いでしょう。
1 2 3 4 5 6 7 8 9 10 |
SET jit TO on; SET jit_above_cost TO 10; SET jit_optimize_above_cost TO 10; SET jit_inline_above_cost TO 10; DROP TABLE IF EXISTS t1; CREATE TABLE t1 (id int PRIMARY KEY, message text); INSERT INTO t1 SELECT i, md5(i::text) FROM generate_series(1,10000) AS i; explain analyze SELECT id, substring(message from 2 for 5) as msg FROM t1 WHERE id % 3 = 0; |
上記のクエリを実行してみます。explainすると、JIT…という記述があるのが分かります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
$ psql -a -d postgres -f jit-test.sql SET jit TO on; SET SET jit_above_cost TO 10; SET SET jit_optimize_above_cost TO 10; SET SET jit_inline_above_cost TO 10; SET DROP TABLE IF EXISTS t1; DROP TABLE CREATE TABLE t1 (id int PRIMARY KEY, message text); CREATE TABLE INSERT INTO t1 SELECT i, md5(i::text) FROM generate_series(1,10000) AS i; INSERT 0 10000 explain analyze SELECT id, substring(message from 2 for 5) as msg FROM t1 WHERE id % 3 = 0; QUERY PLAN ----------------------------------------------------------------------------------------------------- Seq Scan on t1 (cost=0.00..244.15 rows=53 width=36) (actual time=41.886..43.290 rows=3333 loops=1) Filter: ((id % 3) = 0) Rows Removed by Filter: 6667 Planning Time: 0.156 ms JIT: Functions: 4 Generation Time: 0.747 ms Inlining: true Inlining Time: 8.330 ms Optimization: true Optimization Time: 22.257 ms Emission Time: 11.185 ms Execution Time: 44.248 ms (13 rows) |
次は、JITコンパイルの効果が出ているのがわかるように、もう少し複雑なクエリを実行してみます。(実行するクエリは、参考資料と同様、実際には大容量のデータの入ったテーブルを準備するのでなく、実行時にタプルを生成するようにしています)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
$ psql -a -d postgres -f jit-test2.sql SET jit_above_cost TO 0; SET SET jit_optimize_above_cost TO 0; SET SET jit_inline_above_cost TO 0; SET CREATE OR REPLACE FUNCTION bigtable() RETURNS SETOF bigint ROWS 100000000 LANGUAGE sql AS $$ SELECT i from generate_series(1, 100000000::bigint) AS i; $$; CREATE FUNCTION ------------- Test1 ------------- -- JIT off SET jit TO off; SET explain analyze SELECT i + 1 FROM bigtable() AS i; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------- Function Scan on bigtable i (cost=0.25..1250000.25 rows=100000000 width=8) (actual time=30235.864..44473.956 rows=100000000 loops=1) Planning Time: 0.112 ms Execution Time: 49630.957 ms (3 rows) -- JIT on SET jit TO on; SET explain analyze SELECT i + 1 FROM bigtable() AS i; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------- Function Scan on bigtable i (cost=0.25..1250000.25 rows=100000000 width=8) (actual time=29792.983..42322.049 rows=100000000 loops=1) Planning Time: 0.040 ms JIT: Functions: 2 Generation Time: 0.414 ms Inlining: true Inlining Time: 7.773 ms Optimization: true Optimization Time: 10.477 ms Emission Time: 5.095 ms Execution Time: 47463.748 ms (11 rows) ------------- Test2 ------------- -- JIT off SET jit TO off; SET explain analyze SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))) FROM bigtable() AS i; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------- Function Scan on bigtable i (cost=0.25..6000000.25 rows=100000000 width=16) (actual time=29855.820..63744.774 rows=100000000 loops=1) Planning Time: 0.055 ms Execution Time: 68899.182 ms (3 rows) -- JIT on SET jit TO on; SET explain analyze SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))) FROM bigtable() AS i; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------- Function Scan on bigtable i (cost=0.25..6000000.25 rows=100000000 width=16) (actual time=29844.647..55540.690 rows=100000000 loops=1) Planning Time: 0.049 ms JIT: Functions: 2 Generation Time: 0.685 ms Inlining: true Inlining Time: 5.877 ms Optimization: true Optimization Time: 25.856 ms Emission Time: 24.207 ms Execution Time: 60690.048 ms (11 rows) ------------- Test3 ------------- -- JIT off SET jit TO off; SET explain analyze SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))) FROM bigtable() AS i WHERE i % 3 = 0 GROUP BY G; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ GroupAggregate (cost=0.25..1528752.75 rows=200 width=16) (actual time=54172.701..54172.701 rows=1 loops=1) Group Key: (i % '3'::bigint) -> Function Scan on bigtable i (cost=0.25..1501250.25 rows=500000 width=16) (actual time=29738.813..43309.330 rows=33333333 loops=1) Filter: ((i % '3'::bigint) = 0) Rows Removed by Filter: 66666667 Planning Time: 1.470 ms Execution Time: 54232.246 ms (7 rows) -- JIT on SET jit TO on; SET explain analyze SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))) FROM bigtable() AS i WHERE i % 3 = 0 GROUP BY G; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ GroupAggregate (cost=0.25..1528752.75 rows=200 width=16) (actual time=48123.079..48123.079 rows=1 loops=1) Group Key: (i % '3'::bigint) -> Function Scan on bigtable i (cost=0.25..1501250.25 rows=500000 width=16) (actual time=30027.104..41229.939 rows=33333333 loops=1) Filter: ((i % '3'::bigint) = 0) Rows Removed by Filter: 66666667 Planning Time: 0.089 ms JIT: Functions: 11 Generation Time: 1.914 ms Inlining: true Inlining Time: 12.941 ms Optimization: true Optimization Time: 67.625 ms Emission Time: 44.255 ms Execution Time: 48204.660 ms (15 rows) |
私の環境では、結果は以下のようでした(本来は、複数回の計測を行ないばらつきを考慮すべきですが、以下では簡単のため省略しています)
Test | クエリ | Execution Time (msec) | |
JIT OFF | JIT ON | ||
1 | SELECT i + 1 FROM bigtable() AS i; | 49630.957 | 47463.748 |
2 |
SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))) FROM bigtable() AS i; |
68899.182 | 60690.048 |
3 |
SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))) FROM bigtable() AS i WHERE i % 3 = 0 GROUP BY G; |
54232.246 | 48204.660 |
Test1は、単純な加算処理ですが、演算対象の行数が多いためか、JITがONの場合には多少の改善が見られました。
Test2・3では、関数と演算を組み合わせてみましたが、数秒程度の改善が見られました。
厳密なベンチではないので適正な評価はできませんが、どのようなケースでJITが有効か、ざっと雰囲気は掴めたような気がします。
JIT機能を少し追ってみる
『PostgreSQLのJITは、どのような仕組みなのだろうか?』、ざっと雰囲気が掴めたら、次は内部を知りたくなってきます。あまり深いところまで追いきれていませんが、入り口部分を覗いて見ました。
JITプロバイダ
PostgreSQLでは、JIT機能のデフォルト実装はLLVMとなっているが、他のJIT実装も可能なよう設計されている(といっても、私はLLVM以外であまり知りません。。)。
変数jit_provider
で指定される動的ライブラリをロードし、実装ごとの_PG_jit_provider_init
関数内でJitProviderCallbacks
構造体オブジェクトの関数ポインタに実装ごとの関数のアドレスが登録される。デフォルト実装はLLVMであるが、LLVMの場合はllvmjit.cに_PG_jit_provider_init
定義がある。
参考 src/backend/jit/llvm/llvmjit.c
JITプロバイダの初期化は、SQL関数を呼ぶことによって明示的に初期化することもできる。以下を実行すると、backendプロセスで、jit_provider
で指定される動的モジュールをロードすることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ psql psql (11beta2) Type "help" for help. postgres=# set client_min_messages to DEBUG1; SET postgres=# select pg_jit_available(); DEBUG: probing availability of JIT provider at /Users/guests/workspace/github/pgenv2/versions/11beta2/lib/postgresql/llvmjit.so DEBUG: successfully loaded JIT provider in current session pg_jit_available ------------------ t (1 row) |
pg_jit_available
関数の呼び出し前後で、lldbで動的ライブラリの適当なシンボルが見えるか確認してみる。以下は、LLVMCreateBuilder
関数が見えるかbackendプロセスにアタッチして調べた例である。pg_jit_available
関数からprovider_init
関数が呼ばれ、llvmjit.so
がロードされたことがわかる。
1 2 3 4 5 6 7 |
(lldb) image lookup -r -n LLVMCreateBuilder # provider_init()呼び出し前は、シンボル見つからず (lldb) image lookup -r -n LLVMCreateBuilder 2 matches found in /Users/guests/workspace/github/pgenv2/versions/11beta2/lib/postgresql/llvmjit.so: Address: llvmjit.so[0x0000000000ad3a78] (llvmjit.so.__TEXT.__text + 11347560) Summary: llvmjit.so`LLVMCreateBuilder Address: llvmjit.so[0x0000000000ad3a3c] (llvmjit.so.__TEXT.__text + 11347500) Summary: llvmjit.so`LLVMCreateBuilderInContext |
JITの対象
PostgreSQLでは、JIT機能はbackendプロセスで動作し、JITコンパイルの対象は以下となっている。
- expression evaluation
- WHERE句、ターゲットリスト、集約、射影などの式評価
- tuple deforming
- ディスク上のタプル表現のインメモリ表現への変換
参考 https://www.postgresql.org/docs/11/static/jit-reason.html#JIT-ACCELERATED-OPERATIONS
JIT Contextと生成されるコード
以降はLLVMを前提で記述する。
JITコンパイルのエントリーポイントは、jit_compile_expr
関数である。
1.expression evaluationでは、LLVM IRでevalexpr_<module_generation>_<counter>
という関数が生成される。module_generationは、LLVMモジュールの生成数(名前はpg
)、counterはオブジェクトの出力回数であり、これらの変数はLLVMJitContext
構造体で定義されている。
参考 src/backend/jit/llvm/llvmjit_expr.c
2. tuple deformingでは、LLVM IRでdeform_<module_generation>_<counter>
という関数が生成される。
参考 src/backend/jit/llvm/llvmjit_deform.c
LLVMJitContext
は、plan実行時に作成され、トランザクションの終わりで解放される。
参考 src/backend/executor/execMain.c
1、2で生成されるLLVM IRをみるには、jit_dump_bitcode
パラメータをon
にすればよい。パラメータをon
にして、JITが働くクエリを実行すると、pgdata下に.bcファイルが生成される。.llで人が読める形式に変換するにはllvm-dis
を実行する。
試しに以下のようなクエリを実行してみる。
1 2 3 4 5 6 7 |
postgres=# set jit_dump_bitcode to on; SET postgres=# select abs(-100); abs ----- 100 (1 row) |
私の実行した環境では、以下のファイルが生成された。
- 90645.14.bc
- 90645.14.optimized.bc
optimizedは最適化されたビットコードである。90645.14.bcをllvm-dis
でテキストとして読めるLLVM IR形式に変換してみた結果が以下である。
最初の%
で始まる定義は、型定義である。LLVM IRでは、%
で始まる名前はローカル識別子、@
で始まる名前はグローバル識別子を示している[2]https://llvm.org/docs/LangRef.html#identifiers。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
$ llvm-dis 90645.14.bc $ cat 90645.14.ll ; ModuleID = '90645.14.bc' source_filename = "pg" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.13.0" %struct.ExprState = type { %struct.Node, i8, i8, i64, %struct.TupleTableSlot*, %struct.ExprEvalStep*, i64 (%struct.ExprState*, %struct.ExprContext*, i8*)*, %struct.Expr*, i8*, i32, i32, %struct.PlanState*, %struct.ParamListInfoData*, i64*, i8*, i64*, i8* } %struct.Node = type { i32 } %struct.TupleTableSlot = type { i32, i8, i8, i8, i8, %struct.HeapTupleData*, %struct.tupleDesc*, %struct.MemoryContextData*, i32, i32, i64*, i8*, %struct.MinimalTupleData*, %struct.HeapTupleData, i32, i8 } %struct.tupleDesc = type { i32, i32, i32, i8, i32, %struct.tupleConstr*, [0 x %struct.FormData_pg_attribute] } %struct.tupleConstr = type { %struct.attrDefault*, %struct.constrCheck*, %struct.attrMissing*, i16, i16, i8 } %struct.attrDefault = type { i16, i8* } %struct.constrCheck = type { i8*, i8*, i8, i8 } %struct.attrMissing = type opaque %struct.FormData_pg_attribute = type { i32, %struct.nameData, i32, i32, i16, i16, i32, i32, i32, i8, i8, i8, i8, i8, i8, i8, i8, i8, i32, i32 } %struct.nameData = type { [64 x i8] } %struct.MemoryContextData = type { i32, i8, i8, %struct.MemoryContextMethods*, %struct.MemoryContextData*, %struct.MemoryContextData*, %struct.MemoryContextData*, %struct.MemoryContextData*, i8*, i8*, %struct.MemoryContextCallback* } %struct.MemoryContextMethods = type { i8* (%struct.MemoryContextData*, i64)*, void (%struct.MemoryContextData*, i8*)*, i8* (%struct.MemoryContextData*, i8*, i64)*, void (%struct.MemoryContextData*)*, void (%struct.MemoryContextData*)*, i64 (%struct.MemoryContextData*, i8*)*, i1 (%struct.MemoryContextData*)*, void (%struct.MemoryContextData*, void (%struct.MemoryContextData*, i8*, i8*)*, i8*, %struct.MemoryContextCounters*)* } %struct.MemoryContextCounters = type { i64, i64, i64, i64 } %struct.MemoryContextCallback = type { void (i8*)*, i8*, %struct.MemoryContextCallback* } %struct.MinimalTupleData = type { i32, [6 x i8], i16, i16, i8, [0 x i8] } %struct.HeapTupleData = type { i32, %struct.ItemPointerData, i32, %struct.HeapTupleHeaderData* } %struct.ItemPointerData = type { %struct.BlockIdData, i16 } %struct.BlockIdData = type { i16, i16 } %struct.HeapTupleHeaderData = type { %union.anon, %struct.ItemPointerData, i16, i16, i8, [0 x i8] } %union.anon = type { %struct.HeapTupleFields } %struct.HeapTupleFields = type { i32, i32, %union.anon.0 } %union.anon.0 = type { i32 } %struct.ExprEvalStep = type { i64, i64*, i8*, %union.anon.1 } %union.anon.1 = type { %struct.anon.25 } %struct.anon.25 = type { i64*, i8*, i32, i32, %struct.FmgrInfo*, %struct.FunctionCallInfoData* } %struct.FmgrInfo = type { {}*, i32, i16, i8, i8, i8, i8*, %struct.MemoryContextData*, %struct.Node* } %struct.FunctionCallInfoData = type { %struct.FmgrInfo*, %struct.Node*, %struct.Node*, i32, i8, i16, [100 x i64], [100 x i8] } %struct.Expr = type { i32 } %struct.PlanState = type { i32, %struct.Plan*, %struct.EState*, %struct.TupleTableSlot* (%struct.PlanState*)*, %struct.TupleTableSlot* (%struct.PlanState*)*, %struct.Instrumentation*, %struct.WorkerInstrumentation*, %struct.ExprState*, %struct.PlanState*, %struct.PlanState*, %struct.List*, %struct.List*, %struct.Bitmapset*, %struct.TupleTableSlot*, %struct.ExprContext*, %struct.ProjectionInfo*, %struct.tupleDesc* } %struct.Plan = type { i32, double, double, double, i32, i8, i8, i32, %struct.List*, %struct.List*, %struct.Plan*, %struct.Plan*, %struct.List*, %struct.Bitmapset*, %struct.Bitmapset* } %struct.EState = type { i32, i32, %struct.SnapshotData*, %struct.SnapshotData*, %struct.List*, %struct.PlannedStmt*, i8*, %struct.JunkFilter*, i32, %struct.ResultRelInfo*, i32, %struct.ResultRelInfo*, %struct.ResultRelInfo*, i32, %struct.List*, %struct.List*, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.ParamListInfoData*, %struct.ParamExecData*, %struct.QueryEnvironment*, %struct.MemoryContextData*, %struct.List*, %struct.List*, i64, i32, i32, i32, i8, %struct.List*, %struct.List*, %struct.List*, %struct.ExprContext*, %struct.HeapTupleData**, i8*, i8*, i8, %struct.dsa_area*, i32, %struct.JitContext* } %struct.SnapshotData = type { i1 (%struct.HeapTupleData*, %struct.SnapshotData*, i32)*, i32, i32, i32*, i32, i32*, i32, i8, i8, i8, i32, i32, i32, i32, %struct.pairingheap_node, i64, i64 } %struct.pairingheap_node = type { %struct.pairingheap_node*, %struct.pairingheap_node*, %struct.pairingheap_node* } %struct.PlannedStmt = type { i32, i32, i64, i8, i8, i8, i8, i8, i8, i32, %struct.Plan*, %struct.List*, %struct.List*, %struct.List*, %struct.List*, %struct.List*, %struct.Bitmapset*, %struct.List*, %struct.List*, %struct.List*, %struct.List*, %struct.Node*, i32, i32 } %struct.JunkFilter = type { i32, %struct.List*, %struct.tupleDesc*, i16*, %struct.TupleTableSlot*, i16 } %struct.ResultRelInfo = type { i32, i32, %struct.RelationData*, i32, %struct.RelationData**, %struct.IndexInfo**, %struct.TriggerDesc*, %struct.FmgrInfo*, %struct.ExprState**, %struct.Instrumentation*, %struct.FdwRoutine*, i8*, i8, %struct.List*, %struct.List*, %struct.ExprState**, %struct.JunkFilter*, %struct.List*, %struct.ProjectionInfo*, %struct.List*, %struct.OnConflictSetState*, %struct.List*, %struct.ExprState*, %struct.RelationData*, i8 } %struct.IndexInfo = type { i32, i32, i32, [32 x i16], %struct.List*, %struct.List*, %struct.List*, %struct.ExprState*, i32*, i32*, i16*, i32*, i32*, i16*, i8, i8, i8, i8, i32, i32, i8*, %struct.MemoryContextData* } %struct.TriggerDesc = type { %struct.Trigger*, i32, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8 } %struct.Trigger = type { i32, i8*, i32, i16, i8, i8, i32, i32, i32, i8, i8, i16, i16, i16*, i8**, i8*, i8*, i8* } %struct.FdwRoutine = type opaque %struct.OnConflictSetState = type { i32, %struct.ProjectionInfo*, %struct.tupleDesc*, %struct.ExprState* } %struct.RelationData = type opaque %struct.ParamExecData = type { i8*, i64, i8 } %struct.QueryEnvironment = type opaque %struct.dsa_area = type opaque %struct.JitContext = type opaque %struct.Instrumentation = type { i8, i8, i8, %struct.timespec, %struct.timespec, double, double, %struct.BufferUsage, double, double, double, double, double, double, double, %struct.BufferUsage } %struct.timespec = type { i64, i64 } %struct.BufferUsage = type { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, %struct.timespec, %struct.timespec } %struct.WorkerInstrumentation = type { i32, [0 x %struct.Instrumentation] } %struct.List = type { i32, i32, %struct.ListCell*, %struct.ListCell* } %struct.ListCell = type { %union.anon.4, %struct.ListCell* } %union.anon.4 = type { i8* } %struct.Bitmapset = type { i32, [0 x i32] } %struct.ProjectionInfo = type { i32, %struct.ExprState, %struct.ExprContext* } %struct.ParamListInfoData = type { %struct.ParamExternData* (%struct.ParamListInfoData*, i32, i1, %struct.ParamExternData*)*, i8*, void (%struct.ParamListInfoData*, %struct.Param*, %struct.ExprState*, i64*, i8*)*, i8*, void (%struct.ParseState*, i8*)*, i8*, i32, [0 x %struct.ParamExternData] } %struct.ParamExternData = type { i64, i8, i16, i32 } %struct.Param = type { %struct.Expr, i32, i32, i32, i32, i32, i32 } %struct.ParseState = type opaque %struct.ExprContext = type { i32, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.MemoryContextData*, %struct.MemoryContextData*, %struct.ParamExecData*, %struct.ParamListInfoData*, i64*, i8*, i64, i8, i64, i8, %struct.EState*, %struct.ExprContext_CB* } %struct.ExprContext_CB = type { %struct.ExprContext_CB*, void (i64)*, i64 } ; Function Attrs: norecurse nounwind ssp uwtable define i64 @evalexpr_14_0(%struct.ExprState*, %struct.ExprContext*, i1*) #0 { entry: %v.state.resvalue = getelementptr inbounds %struct.ExprState, %struct.ExprState* %0, i32 0, i32 3 %v.state.resnull = getelementptr inbounds %struct.ExprState, %struct.ExprState* %0, i32 0, i32 2 %3 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 1 %v_scanslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %3 %4 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 2 %v_innerslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %4 %5 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 3 %v_outerslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %5 %6 = getelementptr inbounds %struct.ExprState, %struct.ExprState* %0, i32 0, i32 4 %v_resultslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %6 %7 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_scanslot, i32 0, i32 10 %v_scanvalues = load i64*, i64** %7 %8 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_scanslot, i32 0, i32 11 %v_scannulls = load i8*, i8** %8 %9 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_innerslot, i32 0, i32 10 %v_innervalues = load i64*, i64** %9 %10 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_innerslot, i32 0, i32 11 %v_innernulls = load i8*, i8** %10 %11 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_outerslot, i32 0, i32 10 %v_outervalues = load i64*, i64** %11 %12 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_outerslot, i32 0, i32 11 %v_outernulls = load i8*, i8** %12 %13 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_resultslot, i32 0, i32 10 %v_resultvalues = load i64*, i64** %13 %14 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_resultslot, i32 0, i32 11 %v_resultnulls = load i8*, i8** %14 %15 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 8 %v.econtext.aggvalues = load i64*, i64** %15 %16 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 9 %v.econtext.aggnulls = load i8*, i8** %16 br label %b.op.0.start b.op.0.start: ; preds = %entry store i64 100, i64* inttoptr (i64 140524024251152 to i64*) store i8 0, i8* inttoptr (i64 140524024251149 to i8*) br label %b.op.1.start b.op.1.start: ; preds = %b.op.0.start %17 = load i64, i64* %v.state.resvalue %18 = load i8, i8* %v.state.resnull %19 = getelementptr i64, i64* %v_resultvalues, i32 0 %20 = getelementptr i8, i8* %v_resultnulls, i32 0 store i64 %17, i64* %19 store i8 %18, i8* %20 br label %b.op.2.start b.op.2.start: ; preds = %b.op.1.start %21 = load i64, i64* %v.state.resvalue %22 = load i8, i8* %v.state.resnull %23 = trunc i8 %22 to i1 store i1 %23, i1* %2 ret i64 %21 } attributes #0 = { norecurse nounwind ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } |
71行目付近に関数定義があり、evalexpr_xx_x
という名前になっていることがわかる。
LLVM IRを生成しているllvm_compile_expr
関数をみると、LLVMのIRの構造に沿って、コード作成されていくことがわかる。
LLVMModuleCreateWithName
でModuleを作成し、LLVMAddFunction
でModuleにFunctionを追加し、LLVMAppendBasicBlock
でBasicBlockを追加、BuilderでInstructionの構築・・・といった感じである。
参考リンク
- https://www.postgresql.org/docs/11/static/jit.html
- https://github.com/postgres/postgres/blob/master/src/backend/jit/README
- https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/tags/REL_11_BETA2
- PostgreSQL 11 検証レポートレポート
- https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/07/pg11_report-6.pdf
- https://llvm.org/devmtg/2016-09/slides/Melnik-PostgreSQLLLVM.pdf
- https://h50146.www5.hpe.com/products/software/oe/linux/mainstream/support/lcc/pdf/PostgreSQL_11_New_Features_beta1_ja_20180525-1.pdf
- https://llvm.org/docs/LangRef.html
- https://postd.cc/llvm-for-grad-students/
- http://llvm.org/docs/tutorial/LangImpl01.html
- http://kkida-galaxy.blogspot.com/2018/04/postgresql11-with-jit-01.html
- http://www.cs.umd.edu/~awruef/LLVM_Tutorial.pdf
インプレス
売り上げランキング: 87,944
コメント