第1項:導入
1.1 課題
以下のコードスニペットは、典型的なDBMSクエリ評価アルゴリズムの構造を例示する。
/*construct database*/
schemas:=DefineRelationSchemas();
rels:=PopulateRelations(schemas);
/*iterate over queries*/
loop{
query:=ReadQuery();
query plan:=OptimizeQuery(query,schemas);
/*process query:iterate over tuples*/
ans:=Exec(query plan, rels, schemas);
Output(ans);
}
データベースは、まず、一連の関係スキーマを定義してから、次いで、これらのスキーマによって指定された関係を投入することによって構築される。スキーマは、関係の名前、属性の数、それらの名前、および型等、各関係に関するメタデータを指定する。この後にクエリ評価が続き、クエリが読み込まれ、クエリ計画がクエリオプティマイザによって生成され、この計画がSQLエンジンによって実行され、取得された応答が出力される。このプロセスが繰り返される。クエリオプティマイザは、実装レベルの決定を行うため(例えば、クエリ内の結合演算はハッシュ結合またはソートマージ結合の実装レベルの演算にマッピングされる場合がある)、かつクエリ演算のための効率的な実行計画を決定するために、データベース内の関係に関するメタデータを使用する。オプティマイザによって生成されるクエリ計画は究極的には、クエリを表すツリーであり、リーフノードがデータベース関係で、内部ノードが演算である。クエリ評価エンジンは、クエリ計画に指定された演算をデータベース内の関係に適用し、関係内のタプルを反復し、スキーマメタデータを使用することで、タプルをパースして属性を抽出し、処理する。
ところが、データモデルによって導入される汎用性は、性能のさらなる向上に対する課題を提示する。タプルの属性にアクセスする場合を検討する。このようなアクセスは、メタデータを参照することが必要である。スキーマを含むカタログは、この抽出された関係内のタプルの各属性値に対してアクセスされなければならない。このカタログルックアップは最適化されているが、そのオーバーヘッドは大量の関係に対してやはり蓄積され、相当のオーバーヘッドとなる。
DBMSの実行時挙動の注意深い検証によって、上記のクエリ評価プロセスは、各クエリの評価を通じてローカルで不変量である、いくつかのデータ構造の反復解釈が関与することが明らかになった。例えば、アクセスされなければならない一連の関係は各クエリで一定、すなわち、そのスキーマから取得され、そのタプルをパースするために使用される、そのような関係の属性の種類およびオフセットに関する情報は、クエリの実行を通じて不変量である。しかしながら、関係スキーマ情報は、DBMSコードがコンパイルされるときには不明であるため、この情報はクエリ評価コードに伝播することができず、処理される各タプルに対して反復される動作である、スキーマデータの解釈によって取得されなければならない。別の例として、クエリ内の選択または結合演算の表現は、構文ツリーとして表現され、これは、各タプルに対して評価されなければならない。この構文ツリーは、所与のクエリでは一定であり、クエリがDBMSに対して示された後にのみ明らかになるため、DBMSがコンパイルされるときにコード内にコンパイルすることができない。正当なサイズのデータベース内のクエリを処理することは、何百万ものタプルのルックアップが関与する場合があるため、これらの解釈オーバーヘッドは、命令数、ならびに命令キャッシュミスおよびデータキャッシュミスの両方の点で、相当なオーバーヘッドにまで蓄積する可能性がある。
DBMSコードの動的特化の機能性は、できる限り多くの不要な実行時オーバーヘッドを削減することを狙いとする。これは、DBMSのクエリ評価ループ内の上記のような実行時不変値に対する多数の参照を有する部分を識別し、実際の実行時不変値に特化したコードを動的に生成し、この動的に生成されたコード(へのポインタを)をDBMSのクエリ評価ループに挿入することによって行われる。特化手法が適用される微細な粒度のため、この手法は「マイクロ特化」と呼ばれる。
1.2 DBMS特化手法
データベース研究コミュニティは、汎用性から生じる非効率性を回避する方式でカスタマイズされたバージョンを提供することによって、DBMSの効率を向上するためのDBMS特化手法を調査している。DBMS性能を向上する既存の努力のほとんどは、多様なレベルの粒度による特化と特徴付けることができる。アーキテクチャレベルでは、DBMSの全体的なアーキテクチャは、アプリケーションのクラスのサポートを向上するように適合される。例として、オンライン解析処理(OLAP)のための列指向ストア、オンライントランザクション処理(OLTP)のためのHストア、およびストリーム処理DBMSが挙げられる。構成要素レベルでは、特定の種類のデータに指向された構成要素がDBMSに追加される。例として、新しい型の演算子、インデックス、ロックモードが挙げられる。ユーザ規定レベルでは、ユーザは、機能性の特化およびより優れた性能を達成するために、トリガおよびユーザ定義関数を作成する。これらの3つのレベルの特化の欠点は、アーキテクチャ特化の場合は一般的な適用性の欠落、構成要素特化の場合はクエリ最適化、DBMS開発、試験、および保守における複雑性の増加、ならびにユーザ規定特化の場合は複雑なユーザ関与の必要性である。
1.3 クエリコンパイルベースの手法
研究努力の別の流れは、解釈様式におけるクエリの評価ではなく、実行可能コードに対して直接クエリをコンパイルすることに注目する。この種の手法は、クエリコンパイルエンジンによって多数のコード最適化技法が採用され、クエリコンパイル時に適用されることを可能にする。クエリのコンパイルは、JITコンパイル技法の形として考えることができ、クエリの実行時特徴を活用することによって生成された実行可能コードの特化を有効にし、これによってコード最適化技法が積極的に利用されることを可能にする。
このクエリコンパイルベースの手法は、特に近年、DBMSを稼動しているサーバはしばしば、非常に大型の表を保持するに十分大型のメモリを備えて構成されるということを考慮すると、クエリ性能に顕著な利益を与えることができる。したがって、クエリ評価はしばしば、従来考えられていたようなI/O指向ではなく、CPUが関係するタスクであり得る。生成されたコードが個別のクエリにカスタマイズされ、これによって、解釈クエリ評価手法よりもはるかに緊密なコード(フットプリントがかなり小さくなる)になるという事実に加えて、別の重要な利点は、生成されたコードが、コンパイラによる所定のCPUのアーキテクチャと緊密に連結するように最適化できることであり、命令キャッシュミスを相当に削減する。
しかし、クエリコンパイル手法は、クエリコンパイラの設計に対する課題を提示する。具体的には、より多くのデータ型および演算子が導入されるので、対応する構成要素がコンパイラ内に追加される必要があり、コンパイラの複雑性の増加となる。より重要な点として、JITコンパイルは、プログラムの実行時特徴を観察することによって、コード特化が実行時のみに適用されるように制限する。すなわち、特定のDBMSの多くの実行時特徴がコンパイル時に識別することができる場合であっても、実行時にコンパイルオーバーヘッドが導入される。例えば、関係スキーマが定義された後、この関係にアクセスするDBMS内のコードは、特化をクエリ評価時に延期するのではなく、早ければスキーマ定義時に特化することができる。
1.4 マイクロ特化
本発明の実施形態は、値がクエリ評価ループ内部でローカルに不変量である変数(典型的に、スキーマメタデータまたはクエリ特異定数)を識別することによって、DBMSの特定の環境に特異的な情報を利用する。このような変数は、静的に決定される不変量と、さらに高速に変化する変数との間である。そうではなく、このクエリ評価ループの概念、すなわち、実行時不変量(複数可)が存在する、DBMSに特定の概念が存在する。このような不変量に関するこの特定の特徴は、頻繁にとられる実行パスに沿って不要な演算子を排除する微細粒度の特化に利用され、小型化および高速化の両方となる、更に最適化されたコードを導く。しばしば、このループは、下位関係(複数可)内のあらゆるタプルに対して評価され、このために、相当な性能向上の可能性を提供する。特化に使用される不変量は実行時のみ有効であるため、このような特化は、静的技法を使用して実行することができず、実行時まで保留されなければならない。これは、特化プロセスそのものが非常に軽量でなければならないことを暗示する。
スキーマメタデータおよびクエリ特異値に基づいた特化に加えて、関係内の値そのものという、動的特化のための別の機会が識別された。このような値が比較的少ない、または比較的共通である場合、このような値の特化は非常に効果的であり得る。革新は、個別の関係に関連するデータ、またはさらに個別のタプル内部のデータに基づいてDBMSコードを特化することである。
このような微細粒度の下位の動的特化は、前項で検討したような、DBMSで有効な他のさらに上位の特化から区別するために、DBMSマイクロ特化と呼ばれる。マイクロ特化は、第1.1項に記載したように、値がクエリ評価ループ内部で不変量である(典型的に、スキーマメタデータまたはクエリ特異定数)個別の構成要素内部の変数を識別することによって、DBMSの特定の環境に特異的な情報を利用する。この情報は、頻繁にとられる実行パスに沿って不要なコードを排除する微細粒度の特化のために使用され、小型化および効率化両方である最適化されたコードにつながる。しばしば、このループは、下位関係(複数可)のあらゆるタプルに対して評価され、このために、相当な性能向上の可能性を提供する。しかしながら、特化のために使用される不変量は実行時のみ有効であるため、このような特化は、従来のコンパイラ技法を使用して実行することができず、マイクロ特化はDBMS実行時に適用される。これは、特化プロセスそのものが非常に軽量でなければならないことを暗示し、様々な重要な技術的課題が生じる。
マイクロ特化は、粒度がもっと粗い特化の不利益を全く発生させない。DBMSアーキテクチャは変更されないため、サポートすることができるアプリケーションの範囲を制約しない。マイクロ特化は追加の構成要素を全く追加しないので、DBMSの複雑度を増加しない。マイクロ特化はユーザ関与が不要である。さらに、マイクロ特化は、他の3つの形の特化と連携して適用される可能性を有する。例えば、列指向DBMSおよびメインメモリベースのDBMS、ならびに新しい種類の演算子に直接適用することができる。
1.5 本開示の構造
本開示において、第2項では、単純なクエリであっても性能を向上する単独のマイクロ特化事例研究が検討される。この事例研究では、特定のコード変更が確認され、性能向上が予想、検証される。第3〜6項では、3つの種類のマイクロ特化を生じる、3つの汎用クラスの不変値を分類して広くマイクロ特化の機会が確認される。次いで、マイクロ特化によって必要な機構をサポートする実行時環境が導入される。この実行時環境の実装およびPostgreSQLへの組み込みが説明される。本発明では、他の種類のDBMSも使用されてもよいことを理解されたい。第8項では、TPC−HおよびTPC−Cベンチマークの包括的な一連の実験を通じて、マイクロ特化の効果およびコストが検討される。第9項では、マイクロ特化を複雑なDBMSに導入するための開発環境の構造に関する検討が提供される。特化標的をどのように識別するか、適用する特化手法をどのように決定するか、マイクロ特化を有効にするためにAPIへの呼び出しをどのように挿入するかが検討される。第10および11項では、より広義の状況のDBMSにおけるマイクロ特化およびコンパイラベースの特化が説明される。
第2項:事例研究
PostgreSQLの下位ソースコード内部からどのようにマイクロ特化の機会が利用されるかを示すための例が最初に提供される。この特定のマイクロ特化の性能メリット解析も提供される。
前項に記載したように、関係スキーマカタログのルックアップは、クエリ評価に対する相当な非効率を提示する。スキーマ特異値を参照する複雑な分岐文を含む関数に適用された単一のマイクロ特化が確認される。このマイクロ特化は小型のクエリであっても性能を向上することが示された。この事例研究において、特異的コード変更が検討され、性能向上が予想され、次いで、予想が実験で検証される。
DBMSには、クエリ評価ループ内部でローカルに不変量(定数)であり得る多数の変数が存在する。例えば、関係のスキーマが定義されると、属性の数は定数である。さらに、各属性の型、各固定長属性の長さ、ならびにいくつかの属性(可変長属性によって先行されない)のオフセットはこの関係では定数である。
リスト1は、PostgreSQLのソースコードの関数、slot_deform_tuple()のスニペットである。この関数は、タプルがフェッチされると必ず実行され、記憶されたタプルからの値をロング整数の配列に抽出する。関数は、各属性を抽出するためにループ(第11行から開始)に依存する。各属性に対して、タプルの記憶されたバイト内部の属性の値をロング整数に変換するために、コードシーケンス(第12行〜第43行)のパスが実行される(すなわち、バイト、ショート、および整数はロングにキャストされ、文字列はポインタにキャストされる)。各属性のカタログ情報は、thisattという名前の構造体に記憶される。リスト1が示すように、属性の長さ(attlen)、属性の物理的記憶整合(attalign)、および属性のオフセット(attcacheoff)は全て、特定の実行パスを選択する際に関与する。
従来のDBMS実装内部では、これらの変数の値はクエリされている特定の関係に依存するため、変数は分岐条件チェックで評価される。このような実装は、DBMSの汎用性をサポートするために必要であるが、性能向上の機会を提供する。マイクロ特化は、このような変数に注目し、これらがクエリ評価ループ内部で定数である場合、対応するコードシーケンスを大幅に短縮することができる。
マイクロ特化の適用を示すための例として、TPC−Hベンチマークのorders関係が利用される。最初に、orders関係のslot_deform_tuple()関数を特化するために、定数である変数が識別される。スキーマによると、この関係では、ヌル値は全く許可されない。したがって、13〜18行目のヌルチェック文は、不要である。代わりに、特化コードの開始でisnull配列全体にfalseを代入することができる。isnull配列の各値は1バイトであるため、代入は、いくつかの型キャストに分解することができる。例えば、isnull[0]からisnull[7]までの8つの代入は、以下の単一の非常に効率的な文に変換することができる。
(long*)isnull=0;
前述のように、リスト1の変数のうちのいくつかは、任意の特定の関係で定数である。orders関係の場合、natts(属性の数)変数の値は9である。ループの展開は、for文の条件チェックおよびループカウンタの増分命令を回避するために適用される。得られるプログラムは単に9つの代入文を有する。
values[0]=...;
values[1]=...;
…
values[8]=...;
次に、型特有の属性抽出文に注目する。orders関係の最初の属性は、4バイトの整数である。したがって、条件文でattlen変数を参照する必要性は全くない。代わりに、この文では、タプルから直接整数値が代入される。
values[0]=*(int*)(data);
データ変数は、物理的タプルが記憶されるバイト配列であることに注意されたい。第2の属性もまた整数であるため、同じ文がここでも適用される。第1の属性の長さが4バイトであることを受けて、第2の属性のオフセットとして、4がデータに加えられる。
values[l]=*(int*)(data+4);
得られたorders関係の特化コードは、リスト2に示される。リスト1のforループは、関連する関係の属性の数に応じて、何回も実行されることに注意されたい。その結果、特化コードは、元のコードよりもはるかに少ない命令を実行する。
実行可能オブジェクトコードの手動検証では、forループは、以下のクエリを実行する際にorders関係で約319の機械命令(x86)を実行することが見出された。
SELECT o_shippriority FROM orders;
特化コードを実行するために、リスト3に示されるように、GetColumnsToLongs()関数への関数呼び出しがforループを置換するために挿入される。リスト3では、6〜11行目は、GCLroutineへの呼び出しを表すことに注意されたい。15行目から開始して、コードブロックは、リスト1に示される3〜43行目によって囲まれたコードブロックと同一である。GCLルーチンは、上記のコードブロックに効果的に代わる。特化GCLルーチンは、64回しか命令を実行せず、約255(319−64)回の命令の削減となる。
実際に実現される性能メリットを判定するため、上記のクエリが詳細に研究された。このクエリは、1.5Mのタプルを有する、orders関係上のシーケンシャルスキャンを要求する(TPC−Hデータセットでスケールファクタを1に設定した場合)。特化コードが255の命令を節約し、コードが1.5M回起動されるとすると(クエリ評価ループ内部のタプル1つにつき)、命令の合計数は、382M減少すると予想される。
実行プロファイルを収集するために、CALLGRIND[3]が利用された。CALLGRINDによって生成された要約データは、実行された命令の合計数、各関数の命令数、他の実行時情報を記述する。まず、実行された命令のカウントに注目する。
このクエリの実行は、普通のPostgreSQLおよびforループを置換するより短いコードを含むものの両方でプロファイルされた。(第4項を参照)。これらの2つの実行の詳細のプロファイル要約は、リスト4およびリスト5にそれぞれ提供される。表記Irは、実行された命令の数を表すことに注意されたい。
予想どおり、普通のPostgreSQLの命令の最高数(548M)(リスト4に示される)に寄与する、slot_deform_tuple関数は、forループに代わるために特化されたGetColumnsToLongsルーチンが実行されると、はるかに少ない命令(84M)を実行した。この特化されたルーチンは、リスト5に示されたそのアドレス(0x00000000043f53c0)によって表され、96Mの命令を必要とする。
具体的には、普通のPostgreSQLの実行された命令の合計数は3.448Bであり、このマイクロ特化は、約11%(382M/3.448B)の(推定)削減を生じることを暗示する。特化されたPostgreSQLによって実際に実行された命令の合計数は、3.107Bであり、341Mの命令の(測定された)削減、または10%で、我々の推定に一致する。クエリの合計実行時間は、普通のPostgreSQLおよび特化バージョン上で測定され、それぞれ、652ミリ秒および592ミリ秒である。9%を超える実行時間の向上はプロファイル解析に一致する。
クエリ評価ループ内部の単一ルーチンを特化することによって、汎用のslot_deform_tuple()関数は、わずかな変数だけに関して、39行のコード(PostgreSQLの380,000コード行のうち)を特化バージョンに置換し、単純なクエリ上で7.2%の実行時間の向上が達成された。この向上は、マイクロ特化を積極的に適用することの実現可能性および利点を示唆する。
各マイクロ特化は、その値がクエリ評価ループ内部で定数である、1つ以上の変数を識別する。これらの変数を参照する元のコードは、次いで、各々がそれらの変数の各々の単一の値に固有である、特化コードの複数のコピーによって置換される。上記の例において、変数は、スキャンされている特定の関係に関係した。したがって、GetColumnsToLongs()の特化バージョンは、各関係に必要である。
マイクロ特化は、汎用コードを高度に特化されたコードと置換する。この動的なコード特化ベースの手法は、他の積極的な特化技法に緊密に関係する。例えば、Krikellasらは、クエリコンパイルベースの特化手法を研究した[8]。クエリのコンパイルは究極的に、クエリ評価コードを特定のクエリに特異的であるように特化するため、得られる特化コードは、高度に最適化され、このために、非常に効率的であり得る。このような手法は、マイクロ特化によって達成することが可能な性能利点に上限を提供する。Krikellasによって報告されるように、TPC−Hベンチマークにおいてクエリ3に対して達成された実行時の高速化は、10倍を上回った。マイクロ特化によって実現される一般的な性能の利点の研究は、第8項に提供される。現在の実装では、わずかなビー(bee)ルーチンだけで、マイクロ特化によってクエリ3では、10%を超える性能向上(または1.1X高速化)が実現され、最も顕著な向上は、最高38%(または1.6X高速化)である。多数のビールーチンの機会が存在するため、ビーがより完全に活用される場合、数倍の高速化に対する可能性が存在する。
第3項:手法
前項に例示されるように、あるマイクロ特化を適用する経験から、マイクロ特化は、DBMS内部のその広い適用可能性の分類によって汎用化される。さらに、いつ、多様な種類のマイクロ特化が適用可能か、および適用されるべきであるか、詳細理論が提供される。
3.1 用語
以下の用語が導入される。
・特化コードは、コンパイルされ、リンクされた後、「ビー(bee)」と呼ばれる。これは、特化コードのサイズが小さく、実行中に効率的であるためである。可能性として、クエリを評価するために、特化コードの多数のインスタンスを採用することができる。特化コードは、ビーの特徴に類似する。事例研究で検討されるように、特定の関係に関連するビーは、「関係ビー」と呼ばれる。
・ビーは、複数の「ビールーチン」を有することができ、各々、クエリ評価ループ全体でローカルに不変量であると識別された1つ以上の変数に関してDBMSソースコードにおいて所定の場所で特定のマイクロ特化によって生成される。
・ビーのソースコードがコンパイルされると、「プロトビー」が生成される。プロトビーは、実際に実行可能なビーを形成するために、実行時の値で充填される、「穴」を含む場合があるので直接実行可能ではない。すなわち、プロトビーは、実際のビーのプロトタイプとして機能する。さらに、複数のバージョンのプロトビーが存在してもよく、各々、ビーによって実施されるタスクによって必要な個別のコード分岐を表す。
・ビーの構築、起動、リソース割当および再利用のサポートを含む、実行時のビーの管理は、「ハイブ(Hive)実行時環境」(HRE)、によって自動的に処理され、DBMSに依存しないAPI(モジュール)がDBMSに組み込まれ、マイクロ特化を可能にする。
事例研究において、マイクロ特化は、各関係で一定である値(属性の長さ等)に適用され、ビールーチンの結果も一定である。この特定のビールーチンは、特化されたGetColumnsToLongs()ルーチンの短縮形として、GCLと呼ばれる。データベースに定義されたあらゆる関係に固有の関係ビーが存在することになる。
heap_fill_tupleという名前の、ロング整数配列から記憶されるタプルを構築する別のPostgreSQL関数が特化され、各関係に対してSetColumnsFromLongs()(SCL)という名前の追加のビールーチンになる。このため、各関係ビーは2つのビールーチンを有するようになる。
この一般的な手法では、どこでマイクロ特化を適用できるか、およびタイムライン中のいつ、クエリ評価に対する関係スキーマ定義からマイクロ特化を実行することができるか、という2つの重要な疑問が生じる。
3.2 どこでマイクロ特化を適用するか?
マイクロ特化に対する手法の分類100は、記憶されたデータ112および内部データ構造114という、マイクロ特化を適用することができるDBMSの「変数」の2つの型に基づいて、図1に示される。分類100はまた、アーキテクチャレベルの特化104、構成要素レベルの特化106、およびユーザ規定レベルの特化108を含む、他のもっと粗い粒度の特化も含む。
上記は、関係ビー120内部の2つのビールーチンである。これらの特化コードは、個別の関係の多様な特徴に基づき、したがって、特化は、関係スキーマ116の特化である。この特定の事例において、各属性の長さ、オフセット、整合、およびヌル可能な属性の存在、ならびに関係の属性の数に関して特化する。
マイクロ特化の適用は、タプルビー124を導入することによって、個別のタプルにまで拡張することができ、特化は、タプル内部の特定の属性の値に注目する。「性別」等、2、3の特有値118を持つ属性を検討する。値抽出ルーチンが属性の長さ、オフセット、整合を計算する代わりに、この属性の値を要求すると、values[x]=‘M’;等の単一の代入でこの属性の値抽出を正しく満たすことができる。これは、そのタプルに関連するタプルビー内部で発生し、これは、このようなタプルに、ビーID(beeID)と呼ぶ、適切なタプルビーを識別する短いインデックスを含めることによって実行される。したがって、例えば、各性別に1つずつ、2つのタプルビーだけが存在する場合、または生成されるタプルビーが多すぎない限り、他の属性に関しても特化する場合があるので、関係内のタプル全てに対して少数のタプルビーが生成される。タプルビーの数量の選択は、特定の実装検討を含めて第4項に説明する。
最後の種類のビーは、クエリ評価中に発行された内部データ構造114に関して特化され、データ構造内の値のうちのいくつかは、クエリの評価ループ中に一定である。例えば、述語が関与するクエリは、述語をエンコードするためにFuncExprStateデータ構造体(struct)を利用する。述語「age<=45」の場合、述語のデータ構造は、属性ageのID、<=演算子、および定数45を含む。クエリの述語がわかると、これらの変数に特化を適用することができる。このようなクエリに関するデータ構造を特化することから得られるビーは、このために「クエリビー」128と呼ばれる。
さらに、この分類には、「ページビー」122、「修正ビー」130および「コンパイルビー」126の概念が組み入れられる。具体的には、ページビー122は、ページが完全または空である、およびページ内部のタプルの数等、特定のページ内部に記憶された情報を利用することができる。修正ビー130は、削除または更新を実施する特定の型のトランザクションに合わせてカスタム化することができる。最後に、PostgreSQL等の従来のDBMSは、クエリ評価時に、switch−caseベースの非効率的な実装を回避するために、例えば、コンパイル前の型特定比較関数である、関数ポインタを利用することが認識される。これらのコンパイル前の型特定関数は、これらの関数がDBMSコンパイル時にコンパイルされるという事実を除いて、ビーに対する実質的な類似性を示す。したがって、これらは「コンパイルビー」126と呼ばれる。
この分類は、ビーを作成するために特化する変数の種類に応じて、6つの異なる種類のビーを特徴付ける。クエリ評価ループ内部で頻繁に実行されるコードによって使用される値を識別することによって、多くのビールーチンを作成することができる。各ビールーチンは、クエリおよび修正のサブセットを独立的に高速化する。
3.3 どのようにマイクロ特化を適用することができるか?
マイクロ特化は、DBMS内部の多数の場所に適用することができる。マイクロ特化は、これらの値が属する実際のDBMS構成要素に特定ではなく、クエリ評価ループ内に存在する特定の値に適用されるという事実に起因して、マイクロ特化の適用における多様性は実質的な複雑性を生じない。
一般に、マイクロ特化の適用は、以下のシーケンスの5つのタスクを利用する。これらのタスクの簡単な説明をここに記載する。これらのタスクは後の項でさらに説明する。
・ビー設計は、クエリ評価ループおよびこのループ内部に存在する実行時不変量である変数を識別するために、動的および静的両方のプログラム解析に依存する。次いで、これらの識別された不変量の値に応じて、特化バージョンのソースコードが、通常、個別のコード分岐に対応するコードスニペットの形式で、作られる。
・ビーソースコード作成では、適切なコードスニペットを組み合わせることによって、ビーソースコードが作成される。
・プロトビー生成は、実行可能コードを形成するために、上記のタスクから生成されたソースコードをコンパイルし、プロトビーとなる。このステップでは、Cコンパイラが起動され、わずかとはいえないオーバーヘッドが生じるため、この特定のタスクがいつ実施されるかについて、注意しなければならないことに注意されたい。
・ビーインスタンス化によって、プロトビーに存在する所定の定数の値等、実行時情報で実行可能コードを更新する。ビーインスタンス化は究極的に、(動的)リンカーの役割を果たし、最終実行可能ビーを生成する。その値が実行時にのみ決定することができる、このような定数を全く含まない関係ビー等、いくつかのビーの場合、ビーインスタンス化ではプロトビーに対する修正は全く不要である。
・ビー起動は、実行時にDBMSによってインスタンス化されたビーが起動される。
ビーインスタンス化の1つの機構は、プロトビー上のオブジェクトコード操作である。プロトビーは、究極的にプレースホルダーを持つオブジェクトコードであり、各々がビーソースコード内にハードコードされたマジックナンバーとして表され、ソースコードをオブジェクトコードにコンパイルする際に容易に識別することができる。プレースホルダーの使用の一例は、関数アドレスである。ビーソースコードが設計される際、動的リンクを管理することを要求する関数呼び出しは、直接利用することができない。代わりに、ビー内部の各関数呼び出しは、動的関数アドレスとして表される。ビーのインスタンス化時、これらのマジックナンバーは、ビーによって起動される実際の関数アドレスに置換される。
3.4 いつマイクロ特化を適用できるか?
表3.1は、各種ビーに対して、DBMSコンパイルからクエリ評価までのタイムライン上で、5つのタスクの各々が実施されなければならないタイミングを要約する。タイムラインは、DBMSが設計、実装される時点である「DBMS開発」から開始する。「DBMSコンパイル」は、DBMSのソースコードが実行可能プログラムにコンパイルされる時点を表す。DBMS実行可能ファイルがユーザによってインストールされ、DBMSサーバが起動すると、データベースを作成することができる。この時点は「データベース作成」と呼ばれる。作成されたデータベース内部で、指定されたデータ定義言語(DDL)文にしたがって関係が作成され得る。この段階は「スキーマ定義」と呼ばれる。クエリがDBMSに提示されると、クエリ計画を演算するために、最初に「クエリ準備段階」が起動される。その後、実際のクエリ評価およびタプルアクセス/修正を実行することができる。それぞれの連続するタスクは、その前のタスクの完了後に発生しなければならないことに注意されたい。もう1つの重要な問題は、ビー生成がゆっくりであることで、コンパイラを起動することが必要である。このため、ビー生成は、可能であれば、クエリ評価中は回避すべきである。
前述のように、コンパイルビーの例は、PostgreSQLに実装されたコンパイル前の型特異比較関数である。これらの関数のソースコードは、DBMSソースコードと共に実装される。これらの関数の実行可能コードの生成は、DBMSがコンパイルされる際に実施される。生成されたコードのインスタンス化は、コンパイラおよびリンカーによって直接行われる。
事実、全ての種類のビーでは、設計はDBMSソースコードが開発されると同時に発生することが必要である。これは、ビーの設計は主にホストDBMSに存在するコード実行パスに依存するためである。実行時にビーがこれらの代替実行パスと同じ方式で実施されることを保証するためには、ビーのソースコードスニペットを作成するために既存のソースコードを直接利用することで十分である。
コンパイルビーは、クエリ評価時およびタプルアクセスまたは修正時の両方で起動することができる。
関係ビーの場合、そのソースコードスニペットは、属性型特異的である。属性の型(INT、CHAR、またはNUMERIC)それぞれで、対応する属性値は、異なるコード分岐を含むタプルから抽出される。関係のスキーマが定義されると、属性型全てが明らかになる。したがって、特定の関係に対する関係ビーを形成するために、対応するコードスニペットを選択することができる。関係定義は実施に重要なタスクではないため、関係が定義された後、得られたビーのソースコードは、関係プロトビーを作成するためにコンパイルすることができる。さらに、各関係ビーは全体的にスキーマ定義および特定の関係の存在に依存するため、関係プロトビーは、次いで、スキーマ定義時にインスタンス化することができる。
タプルビーは、以下の第5項に詳細を説明するように、どちらも関係スキーマ特定であるため、関係ビーに非常に類似するので、タプルビーもソースコードの作成はスキーマ定義時に実施される。どちらにせよ、タプルビーの生成を実施するタイミングは、関係ビーより後である。スキーマ特定であることに加えて、タプルビーは、属性値特定でもある。その後、新しいタプルビーの生成は、修正中、具体的には、挿入および更新中にのみ実行することができる。タプルビーが生成された後、修正と共にインスタンス化される。
関係ビーおよびタプルビーの両方は、クエリ評価およびタプルアクセスまたは修正中に起動することができる。ビーは、インスタンス化された後にのみ起動することができ、インスタンス化によって、ビーの実行に必要な実行時情報を組み入れるようにビーのオブジェクトコードを修正することに注意されたい。表3.1には、タプルビーが、修正前に発生するクエリ評価中に起動されると示されるが、タプル挿入等、修正中にインスタンス化されていたタプルビーだけがクエリ評価中に起動することができる。
クエリビーのソースコードは、DBMSコンパイル時点で作成され、クエリビーは究極的には、DBMSとともに設計される、クエリ評価アルゴリズムの特化の(小さい)部分である。クエリビーの生成を実施するには、DBMSコンパイル時点またはデータベース作成時点のいずれかという、2つの選択肢が存在する。本明細書に開示される実施形態の実装において、クエリビーはデータベース作成中に作成される。クエリ特定の情報は、クエリの計画が作成された時点のみ利用可能であるため、クエリビーのインスタンス化は、クエリ準備時点でのみ実行することができる。
クエリビーは、クエリ評価および修正中に起動することができる。
修正ビーの場合、挿入、更新、および削除操作はスキーマ依存であるため、修正ビーの作成は、スキーマ定義中に発生してもよい。修正ビーの生成は、スキーマ定義時にも予定することができる。実際の修正中、修正ビーは、適切な実行時値でインスタンス化することができる。
修正ビーは、修正中にのみ起動されなければならない。
最後に、ページビーの作成は、データベース作成時に発生すべきで、ページのサイズおよびページの種類(メモリ内およびディスク上)等の重要な情報は、通常、データベース作成中に構成される。ページビーは、スキーマ定義中にも作成することができ、スキーマ情報は、ページアクセスコードをさらに特化するために利用することができる。例えば、属性全てが固定長である場合、ページ内部の各タプルのオフセットは、ページヘッダーを読み出すことなく、ページビーによって効率的に計算することができる。ページビーは、データベース作成およびスキーマ定義中に生成することができる。DBMSバッファプール内で機能するメモリ内ページの場合、データベース作成中に生成することができる。タプルが記憶されるディスク上のページの場合、スキーマ関連の情報は、対応するページビーを考慮することが必要である。したがって、ディスク上のページビーは、スキーマ定義時に生成されなければならない。ページが割り当てられ、クエリおよび修正とともに再請求されるため、ページビーのインスタンス化は、クエリ評価および修正中に予定されなければならない。
ページビーは、クエリ評価時およびタプルアクセスまたは修正時のどちらの間でも起動することができる。タプルビーに類似して、修正中にインスタンス化されていたページビーだけがクエリ評価時に起動することができる。
個別のビーを作成し、生成することによって、追加のコードが(特化コードおよびビーのソースコードを作成するコードを含む)DBMSに追加されているように思えるかもしれない。事実、導入された特化コードは元のコードを置換する。したがって、DBMSの実行可能ファイルのサイズは削減される。ビーのソースコード作成を実施するコードに関して、このようなコードは、同じ種類のビーの間で共有され、例えば、関係ビーの作成は全て同じ関数を利用する。その後、ビーのソースコード作成を実施するためには、少数の行のソースコードだけが必要である。実行時、事例研究に例示されるように、特化コードによって相当な数の命令を削減することができる。したがって、メモリ内のクエリ評価コードのフットプリントは、各追加のビーによって実際に削減され、命令キャッシュミスも削減する(しかし、HREが増加すると、フットプリントは、固定量分がやや増加する)。マイクロ特化の性能利点のより総合的な研究は、第8項に提供される。
第4項:HIVE実行時環境をDBMSに組み入れる
本項では、DBMSの代わりにビーを作成し、ビールーチンを実行することに関連する基底の管理タスクの多くを実施するDBMS独立モジュールである、ハイブ(Hive)実行時環境(HRE)が導入される。HREは、DBMSにAPIを提供し、それによって、既存のDBMSへの必要な修正を抑制しながら、マイクロ特化がDBMSに適用されることを容易にする。
4.1 HIVE実行時環境のアーキテクチャ
図2は、HREを実装するために修正されたDBMSシステム150の従来のアーキテクチャ[5]を示す。最初に、従来のDBMSアーキテクチャ構成要素を説明し、その後、修正を説明する。
システム150は、データストレージモジュール(例えば、ディスク)上に記憶され、オペレーティングシステムによってアクセスされてもよい、データベース152とシステムカタログ154とを含む。システム150は、データベース152またはカタログ154の一部として記憶されるDBMS情報へのアクセスを制御するように構成される上位の記憶データ管理モジュール156も含んでもよい。システム150は、データベース管理者(DBA)担当者158、クエリを形成する通常のユーザ160、プログラミング言語を使用してプログラム164を作成するアプリケーションプログラマー162、およびパラメトリックユーザ166のためのインタフェースを含む。DBA担当者158は、データベース152を定義し、DDL文170および他の特権命令コマンド172を使用して、その定義への変更を行うことによって調整してもよい。
DDLコンパイラ174は、DDL文170に指定されたスキーマ定義を処理し、スキーマの記述をDBMSカタログ154に記憶する。
データベースからの情報を時々必要とする通常のユーザ160は、システム150と相互作用するために、インタラクティブクエリインタフェース176を利用してもよい。クエリは、それらを内部形式にコンパイルする、クエリコンパイラ178によってコンパイルされてもよい。この内部クエリは、クエリオプティマイザ180によって最適化されてもよい。クエリオプティマイザ180は、記憶されたデータに関するシステムカタログ154情報にアクセスしてもよく、クエリに必要な操作を実施する実行可能コードを生成し、実行時データベースプロセッサ182を呼び出す。
アプリケーションプログラマー162は、プリコンパイラ184へ送信される(例えば、Java(登録商標)、C、またはC++の)プログラム164を作成する。プリコンパイラ184は、DMLコマンドを抽出し、データベース152にアクセスするためにコンパイルされたトランザクション188にコンパイルするために、それらをDMLコンパイラ186へ送信する。プログラム164の残りは、ホスト言語コンパイラ190へ送信される。DMLコマンドのオブジェクトコードおよびプログラムの残りはリンクされ、実行時データベースプロセッサ182によって実行されてもよい、コンパイルされたトランザクション188を形成する。
HREの構成要素は、注釈DDLプロセッサ192と、ビーコード生成機能194と、注釈干渉モジュール196と、ビー再構築機能198と、ビーソースコードレポジトリ200と、ビー作成機能202と、ビー収集機能204と、ビーキャッシュ206と、ビーキャッシュマネージャー208と、ビー配置オプティマイザ210とを備える。HREは、大部分がDBMSから独立した様式で、ビーの構成、ビーの生成、ビーの起動機能を提供する。
HRE APIによって提供されたメソッドを起動するためにDBMSに追加されたコード、およびDBMS内部の既存のコードに必要な他の変更は、注釈モジュール212、注釈DDLプロセッサ起動機能214、挿入/更新プロセッサ216、ビー起動機能218、ビーデータプロセッサ222、および注釈メタデータ224を含む。図1の分類におけるビー種類全てを完全にサポートするために、3つの既存のDBMS構成要素(DDLコンパイラ174、実行時データベースプロセッサ182、および記憶データ管理機能156)、2つのレポジトリ(システムカタログ/データ辞書154および記憶データベース152)、およびスキーマ(DDL文170)は、追加されたコードを含めて拡大されることが必要である。太線は、HREの構成要素への呼び出しを表し、破線はスキーマ情報の記憶またはアクセスのいずれかを表す。
マイクロ特化を適用する、すなわち、いくつかの汎用DBMSコードを特化ビールーチンの呼び出しと置換する開発者は、i)どのビールーチンを設計するか、ii)どのように対応するソースコードを作成するか、iii)どのようにプロトビーを生成するか、iv)どのようにプロトビーをインスタンス化するか、およびv)どのようにビールーチンの実行を有効にするか、を決定しなければならない。関連のタスクは、以下の第9項に記載する。ビーに対応するために従来のDBMSのアーキテクチャに必要な変更は、「ビー構成グループ」230および「クエリ評価グループ」232という2つのグループにそれぞれ対応して分類することができる。各グループ内部の構成要素をここで検討する。
4.1.1 ビー構成グループ
ビー構成グループ230は、ビーのソースコード作成およびプロトビーの生成を実施する。開発者は、多様なコード分岐に対応するソースコードスニペットを設計し、ビー構成グループは実行時に、適当なスニペットを合わせることによって、ビールーチンのソースコードを作成する。スニペットの選択は、マイクロ特化が適用される不変量の特定の値に合わせてカスタマイズされる。タプルビーおよびクエリビーの作成および生成の詳細、特に、このグループ内の構成要素がこれら2つのタスクにどのように関与するかについては、それぞれ、第5項および第6項に記載する。
4.1.2 クエリ評価グループ
クエリ評価グループ232は、ビーが、DBMS自体内部の動作によって正しく管理され、連結されることを保証するためのタスクを実施する8つの構成要素の集合体である。
まず、特定のビーのソースコードが作成された後、ビー作成機能が起動されて2つのタスクを実施する。まず、プロトビーは、ビーソースコードをコンパイルすることによって、生成される。次に、プロトビーは、存在する場合、プロトビーのプレースホルダーを置換する、正しい実行時値でインスタンス化される。このオブジェクトコードインスタンス化タスクによって、実際に実行可能なビーが生成される。ビー作成機能は、ビーのためのソースコードをコンパイルするためにGCCに依存する。得られるオブジェクトファイル、例えば、実行可能およびリンク可能フォーマット(ELF)[6]は、ヘッダー、テーブル、変数、および関数本体を含む情報の集合体を含み、これらは全て、その従来の使用において、ELFファイルがリンクされ、実行のためにメモリにロードされる際に有用である。しかし、ビーを組み立てるためには、ビールーチンに対応する関数本体だけが必要である。このため、ビー作成機能は関数本体を抽出し、それらを使用して、個別のビーを作成する。
ビーキャッシュマネージャーコンポーネントは、ビーがメインメモリにある間、ビーを管理する。ビーソースコードがオブジェクトコードにコンパイルされると、ビーが形成され、ビーキャッシュマネージャーによって管理される専用の実行可能メモリ領域に記憶される。メモリ内ビー記憶領域は、ディスク上に記憶されることが必要である。このディスク上のビーの記憶領域は、「ビーキャッシュ」と呼ばれる。データベース接続が終了されると、この接続から作成されたビーが、ビーキャッシュマネージャーによってディスクファイルにプッシュされるという簡単なポリシーが実装される。現在、ビーキャッシュは、電源喪失またはディスククラッシュ時に存続することが保証されていないが、安定したビーキャッシュは、ログに関連する元に戻す/再度実行ロジックを通じて実現することができる。DBMSサーバが起動すると、ビー全て(またはおそらく必要なビーのみ)が実行可能メモリ領域にロードされるので、ビーが直接起動することができる。
クエリが評価されると、ビー呼び出し元は、適切な引数を取得する。例として、GCLルーチンはタプルへのポインタを必要とする(リスト2に示されるデータ引数)。ビー呼び出し元は、必要な引数をビーキャッシュマネージャーに渡し、ビーキャッシュマネージャーは、これらの引数で正しいビールーチンを起動する。ビーは、ビーを実行することによって生じる命令キャッシュミスが抑制されるように、ビー配置オプティマイザ210によって、メモリ内の専用場所に配置される。このようなメモリ場所をどのように選択するかの詳細検討は第7項に記載する。
最後に、ビー収集機能が、ビーキャッシュマネージャー(メインメモリ内のビー)およびディスク上のビーキャッシュ両方から、終了したビー(例えば、関係削除に起因して使用されることがないビー)をガーベッジコレクションする。
要約すると、図2に示される構成要素は、完全に詳細なHREを提供する。HREは、6000行のソースコード行(SLOC)からなる。上述のように、HREは、既存のDBMSに最小限の変更を招く。我々の実装において、PostgreSQLへの変更は、図2の濃い影のボックスによって表されるように、約600SLOCである。この変更は、ビーを有効にするためにのみ必要である、引数準備、メモリ割り当て、リソースリクラメーション、およびビー起動の文を含む。DBMSに行われた変更は、380KのSLOCのPostgreSQL DBMSに比較すると、最小限である。
一般に、このマイクロ特化手法の利点は、ビーがDBMS外部で生成され、オンザフライで起動され、したがって、既存のDBMSに対する変更は、HREがDBMSに独立的であることを効果的に可能にし、このために大規模なリファクタリングを行うことなく他のDBMSで採用可能である、関数ポインタの割り当て、パラメータの初期化、およびリソースのリクラメーション等、最小限の組のサポート文だけを含むことである。
4.2 技術的検討事項
ここでは、HREの設計および実装中に存在する3つの技術的課題、およびこれらの課題のために採用される解決策を中心とする。
4.2.1 タプルビー内のコード共有を可能にする
第4.1.2項で導入されたように、ビーキャッシュは、ビーが配置されるディスク上の記憶領域である。具体的には、ビーキャッシュは、ビールーチンセクションおよびデータセクションという、2つのセクションからなる。技術的には、各ビーは、専用ルーチンセクションおよびデータセクションからなる。ルーチンセクション内のルーチンは、実行時にデータセクションにアクセスする。このような構成の欠点として、特に新しいビーおよび新しいビールーチンが追加されると、ビー全ての全体として必要となる記憶領域が急速に増大する可能性がある。同じ関係のタプルビーの全てが実際に同じ機能を共有し、タプルから値を抽出し、生の値からタプルを構築するという点から、したがって、これらのタプルビーは、同じ組のビールーチンを効果的に利用することができる。その後、特定の関係の同じプロトビーバージョンからインスタンス化されるタプルビー全てに対して、グローバルルーチンセクションがビーキャッシュに構築される。
タプルビールーチンコードを共有する利点は、ディスク上およびメモリ内にタプルビーを記憶するために必要な記憶領域を抑制することである。具体的には、コード共有戦略は、レベル2キャッシュに対する負担を効果的に抑制し、実験マシン上では256KBである。加えて、レベル1の命令キャッシュ性能もまた、多数の異なるタプルビーが起動されるにもかかわらず向上し、特定のビールーチンに属する同じ組の命令は、各タプルビーが個別のルーチンセクションを要求する場合、除去されることなく、命令キャッシュに残される。
4.2.2 動的に生成されたコードを実行する
ビーソースコードはGCCを使用してコンパイルされる。この結果、多様なヘッダーおよびコードセクション[6]を含むオブジェクトファイルが得られる。これらの構成要素は、オブジェクトファイルがその従来の使用で実行またはリンクされる際の重要な情報を含む。これらの構成要素の中でも、実際の実行可能なマシンコードを記憶する、.textセクションがビーに変換される。技術的懸念は、.rela.text[6]セクションも存在する際に発生する。この特定のセクションは、オブジェクトコード再配置を実施するためのリンカーのデータを含む。具体的には、ソースコードがハードコードされた文字列またはswitch−case文を含む場合、.rela.textセクションが生成される。.rela.textセクションをビー起動および実行機構に組み入れると、実行時のオーバーヘッドおよび技術的複雑性が導入される。ビーを小さいままに保ち(ビーコード内のこの追加のコードセクションを避けることによって)、かつビーコードに高度なリンカーを実装することを避けるために、ビーソースコードが、コンパイル後に.rela.textセクションに変換されるコンストラクトを含まないことを保証することによって、再配置演算が迂回される。この目標を達成するために、ビーソースコードを作成する際に以下の2つの規則に依存した。
・ソースコード内に文字列を直接ハードコードする代わりに、データセクションに文字列を個別に記憶する。実行時に正しいデータセクションにアクセスするために、特定のデータセクションのオフセットを計算するために、ビーIDを採用する。したがって、ソースコード内では、データセクションに記憶された文字列は、メモリアドレスを参照することによって直接アクセスされる。
・switch−case文を避けるために、同等な代替として、if−else文を使用する。
実行時に、究極的には実行可能コードであるビーは、メインメモリにロードして実行する。このビーロード操作は、OS API機能の集合体によって実行される。具体的には、メインメモリの実行可能領域は最初に割り当てることが必要である。これは、posix_memalign()関数によって実行される。ビーがこの割り当てられたメモリ内にロードされ、内部で実行されることを可能にするため、このメモリ領域に読み取り可能、書き込み可能、実行可能フラグを設定するために関数mprotect()が呼び出される必要がある。関数は、以下の表現規則で呼び出される。
mprotect(memory_address,memory_size,PROT_READ|PROT_WRITE|PROT_EXEC);
ビーがこの割り当てられたメモリ領域内にロードされると、メモリブロック全体は、以下の関数呼び出しによって再度保護されるはずである。
mprotect(memory_address,memory_size,PROT_READ|PROT_EXEC);
PROT WRITEフラグを排除することによって、このメモリブロックに記憶された内容はもはや変更することができない。DBMSサーバが終了すると、割り当てられたメモリページ全ては解放される。
4.2.3.ビー配置
第4.1.2項で導入したように、ビーは、命令キャッシュミスを削減するために、「ビー配置オプティマイザ」210によってメインメモリに配置される。各ビーに対して、ビーを配置し、記憶するために、3ページのメインメモリページ(各々4KB)が必要である。これは、実験マシンにおいて、CPU上の各コアは、各キャッシュ行が64バイトの幅を備え、32KB4方式設定関連レベル1命令キャッシュで構成されるためである。この命令キャッシュは、2ページのメモリページに相当する、合計で8KBのメインメモリ空間にマッピングされる。すなわち、8KBのメインメモリは、128の固有のキャッシュスロット全てを網羅する。例えば、第1のキャッシュスロットが、割り当てられた8KBのメモリの最初の64バイトにマッピングされ、128番目のキャッシュスロットは、この割り当てられたメモリ領域の最後の64バイトにマッピングされる。したがって、最後のキャッシュ行の64バイトを超える場合がある、ビー全体がメモリ内に正しく記憶できるように、追加のメモリページが必要となる。
4.3 HIVE実行時環境API
APIは、HREがDBMSに組み込まれることを可能にするように設計された。このAPIは、HREが多数のDBMSによって使用できるように、DBMS独立であるように設計される。このAPIの詳細は、図3に示されるAPIの静的コールグラフ250で図示される。
図3において、左側に挙げられた8つの関数252は、DBMS258によって直接起動される。これらの8つの関数252は、連携してAPIを形成する。右側に挙げられた10の関数254は、HRE内部で内部的に起動される補助関数である。DBMS258は、これらの10の関数254を直接起動しない。このAPIに存在する8つの関数252の各々の総合的な説明をここに記載する。
4.3.1 HRE APIでマイクロ特化を適用する
マイクロ特化を適用するために必要な5つのシーケンシャルタスクは、第3.3項で既に検討した。これらは、リスト3に例示したように、各ビーに対しての、(a)設計、(b)作成、(c)プロトビーを生成、(d)インスタンス化、そしてその後(e)起動である。API機能の検討をここで、これらのタスクを実施する状況で要約する。図4は、各API関数が起動されるタイミングを図示するフローチャート290を表す。
DBMS258がコンパイルされる前に、ビー全てが設計される。このタスクには、API関数は全く不要である。クエリビーの場合、ソースコードは、DBMS開発中に、DBMSソースコードとともに作成される。クエリプロトビーの生成は、DBMSコンパイル中に実施される。
DBMS258は、InitQueryBees270およびInitRelationAndTupleBees272関数を呼び出すことによって、HREとの相互作用を初期化する。
CreateAndGenerateRelationAndTupleBees278関数は、新しい関係が定義されると、DBMS58によって起動される。この関数278は、関係およびタプルビーのためにビーソースコードの作成およびプロトビー生成タスクを実施する。上述のように、HRE APIは、クエリビーのソースコードを作成するための関数を一切提供する必要はない。
関係がドロップされると、DBMS258によってGarbageCollectRelationAndTupleBees284関数が起動される。
クエリ準備時に、DBMS258は、クエリビーのインスタンス化タスクを実施する、InstantiateQueryBee282関数を起動する。関係およびタプルビーのインスタンス化は、InstantiateRelationOrTupleBee280関数によって実施され、関係およびタプルビーが生成された後に実行される。
ビーのインスタンス化後、DBMS258によってPlaceBees276関数が起動される。第2項に詳細を記載したように、ビー起動は、DBMS258に挿入された関数呼び出しによって実施されるため、ビーを起動するためにHRE−API関数は全く利用されない。
最後に、DBMSサーバが終了する際、割り当てられたリソースを解放するために、FreeAllBees274関数が呼び出される。
4.3.2 データ構造定義
struct RelationDefinition{
int database_id;
int relation_id;
int num_attributes;
bool no_nullable_attribute;
AttributeDefinition*attributes;
}
関係およびタプルビーの作成は、関係スキーマに依存する。異なるDBMSは、多様な方式でスキーマを内部管理する一方、関係およびタプルビーの作成に必要な情報が抽出され、HREによって利用されるRelationDefinitionデータ構造体が設計された。database_idおよびrelation_idのフィールドは、それぞれ、DBMSによってデータベースおよびこの関係を一意に識別する。これらの2つの変数は、HRE APIへの起動の際の関与とともに後述する。RelationDefinition構造体内のこれらの2つのフィールドは、DBMSによって管理されるデータベースカタログを照合するためにHREによって使用されるので、HREは、関係およびデータベースの追加のIDを必要としない。
num_attributesは、現在の関係内の属性の数を指定する。no_nullable_attributeフラグは、現在の関係がヌル可能な属性(複数可)を許可するかどうかを示す。このフラグは、関係およびタプルビー作成中に利用される。第2項に記載するように、関係がヌル可能な属性を許可しない場合、ヌル値をチェックするコードを排除することができる。
最後に、HREは、関係およびタプルビーの作成に必要な属性特異的情報を記憶するために、AttributeDefinition構造体を定義する。
struct AttributeDefinition{
int attribute_num;
int attribute_type;
int attribute_maxlength;
int domain_cardinality;
bool attribute_specialized;
}
AttributeDefinition構造体内で、attribute_numフィールドは、各属性に対しゼロベースのインデックスである。attribute_typeフィールドは、整数で現在の属性の型を記述し、HREによって内部でサポートされる。DBMS開発者は、この整数フィールドと各DBMSによってサポートされる属性定義との間にDBMSに依存するマッピングを提供することが必要であることに注意されたい。例えば、属性型ロングは、PostgreSQLでは20と表される。HREでは、ロング属性型は値5によって示される。
attribute_maxlengthフィールドは属性の長さを指定する。可変長の属性の場合、これは、DDLに指定された最大長であることに注意されたい。例えば、VARCHAR(128)に従うと、attribute_maxlengthは128である。属性の最大長をキャプチャすることは特にタプルビー作成で重要である。第5項で詳細に検討する、ビーIDに基づいて計算されたオフセットによってデータセクションのアクセスを有効にするために、関係に関連するデータセクションは全て同じ長さを有することが求められる。各属性に対して指定された最大に許可される長さによって、データセクションが同じ長さであることを保証する。
domain_cardinalityフィールドは属性の個別の値の数を記憶する。domain_cardinalityは、特化される属性を決定するために、タプルビー作成中に利用される。特化属性は、attribute_specializedフラグをTRUEに設定することによって示される。特化されていない属性の場合、デフォルトで0の値がdomain_cardinalityフィールドに割り当てられる。
4.3.3 beecachemanager.c
beecachemanager.c構成要素256(図3を参照)は、ビーキャッシュ管理を提供する。ホストDBMSには4つの関数が有効である。
int InitQueryBees()
この関数は、クエリビーをメモリにロードする。この関数は、DBMSサーバプロセス開始中に、DBMSによって起動される。この関数は、成功時0または、エラーが発生すると戻りコードを返す。戻りコードおよび原因は、表4.1に説明する。これらのAPI関数は全て重要なタスクを実施するため、これらの関数の実行中に発生するどのエラーでも、DBMSサーバプロセスは終了されるべきである。
int InitRelationAndTupleBees(int database_id)
この関数は、database_idパラメータによって示されたデータベースに属する関係およびタプルビーだけをディスクからメモリにロードする。この関数は、DBMSサーバメインプロセスによって、ならびにPostgreSQLによって提供されるANALYZE設備等、関係の統計を計算するDBMSバックエンドプロセスによって直接起動することができる。このような設備は、短時間バックエンドで実行され、特定の関係に関してのみ操作される。その後、このようなプロセスのためにHRE全体を初期化する代わりに、関連の関係ビーおよびタプルビーだけが必要である。
この関数は、成功時0または、エラーが発生すると戻りコードを返す。表4.2にエラーコードおよび原因を記載する。
この関数は、ビーキャッシュによって割り当てられたメモリ領域を全て解放する。FreeAllBeesは、データベース毎のサーバプロセスシャットダウン中にDBMSによって起動される。
int PlaceBees()
この関数は、ビー配置オプティマイザ210(図3のbeeplacementoptimizer.c関数264も参照)によって実行された最適化に応じてビー配置を実施する。PlaceBeesは、関係が定義された後、またはクエリ計画が特定のクエリに対してDBMSによって生成された後に起動され、その時点で、クエリに関連するビー全てがインスタンス化される。
この関数は、成功時0または、エラーが発生すると戻りコードを返す。表4.3にPlaceBees関数に関連する戻りコードおよび原因を要約する。
4.3.4 beemaker.c
この関数は、「CREATE TABLE DDL」文の実行中に起動される。CreateAndGenerateRelationAndBeeは、対応する関係およびタプルビーソースコードを作成するために、指定された関係スキーマを使用する。この関数は、生成されたソースコードをコンパイルするためにコンパイラを起動する。
この関数は、前述した関係定義という1つの入力パラメータをとる。DBMSコードは最初に、DBMS属性型に対して、上記のマッピングを使用して、関係スキーマに含まれた情報からこのデータ構造を初期化しなければならない。
この関数は、属性の型に基づいて、コードスニペットを合わせて、関係またはタプルプロトビーにステッチングされる。複雑な事例では、このコード構築機構は、ソースコード翻訳という形で、DBMS開発者によって提供されることが必要な場合がある。潜在的に、実行時にソースコード作成が必要なビーは複数種類存在するかもしれない。対応するビーコード作成関数は、各種のビーに対して実装されることが必要である。これらの実装された関数は、HREが作成されているビーの種類に応じて適切な関数を起動できるように、関数ポインタとして体系化されなければならない。
この関数は、成功時0または、エラーが発生すると戻りコードを返す。表4.4にCreateAndGenerateRelationAndBee関数に関連する戻りコードおよび原因を挙げる。
この関数は、関係からのrelation_idへのマッピングを安定した記憶に保持することも必要である。
この関数は、スキーマ定義時に関係ビーが生成された後、または修正中にタプルビーが生成された後に起動される。最初のパラメータrelation_idは、インスタンス化された関係またはタプルビーが関連付けられている関係を示す。リスト3に示されるように、relation_idは、特定のビーを起動するためにも利用される。次のパラメータ、placeholder_listは、第3.3項で説明されるように、ビーコードのプレースホルダーを置換するために挿入される値のリストを表す。この情報は、この呼び出しを追加するためにDBMSソースコードが修正される時点で明らかになる。
この関数は、成功時0または、エラーが発生すると戻りコードを返す。表4.5にInstantiateRelationOrTupleBee関数に関連する戻りコードおよび原因を示す。
この関数は、各計画ノードの初期化タスクを実施する関数によって起動される。
関数は、関与する計画ノードに関連するビーをインスタンス化する。InstantiateQueryBeeは、入力として4つのパラメータを受け取る。最初のパラメータは、特定のクエリ計画ノードに対応するプロトビーのIDである。これらのIDは、クエリプロトビーが生成されると割り当てられる。次のパラメータは、インスタンス化されたビーのプレースホルダーを置換するために挿入される値のリストを表す。call_addressesパラメータは、この現在のビー内部から起動されるビーのアドレスを提供する。このアドレスの配列は、DBMSがコンパイルされた後にのみ知られる、標準Cライブラリ関数等、補助関数も含むので、したがって、インスタンス化されたクエリビーは、実行時にビーコードおよびライブラリコードをリンクするために、リンカーを起動することなく、これらの関数を直接起動することができる。(第6.4項のクエリビーの起動についての説明を参照すること)コスト概算パラメータは、ビーがインスタンス化される計画ノードのコストモデル概算の値を表す。具体的には、この値が大きければ大きいほど、この計画ノードに関連するDBMS関数が実行される頻度が高くなる。このパラメータは、DBMSコードの実行時実行頻度を提供するので、インスタンス化されたビーの最適なキャッシュ配置を判定するために、ビー配置オプティマイザによって利用される。
この関数は、成功時0または、エラーが発生すると戻りコードを返す。表4.6にInstantiateQueryBee関数に関連する戻りコードおよび原因を示す。
4.3.5 beecollector.c
int GarbageCollectRelationAndTupleBees(int relation_id)
beecollector.c関数262(図3を参照)は、関係が削除されたためにもはや必要とされない関係およびタプルビーのガーベッジ収集を実施する。具体的には、影響のある関係ビーは、メモリ内およびディスク上両方のビーキャッシュから削除される。GarbageCollectRelationAndTupleBees関数に対する呼び出しは、「DROP TABLE DDL」文によってトリガされる。
この関数はパラメータとして、ドロップされた関係を識別する、relation_idを受け取る。GarbageCollectRelationAndTupleBeesは成功時に0、エラーが発生すると戻りコードを返す。表4.7にこの関数に関連する戻りコードおよび原因を示す。
第5項:関係およびタプルビー詳細
前項で導入されたように、関係ビーおよびタプルビーは、それぞれ、関係内部に記憶された特定のスキーマおよび値に関連する。さらに、PostgreSQLの属性値抽出関数に基づいて特化される、GCLという名前の関係/タプルビールーチンが検証された。ここでは、関係およびタプルビーがクエリ評価中にどのように設計され、利用されるかについて検討することとする。関係およびタプルビーがクエリ評価中に所定の共通性を共有する(簡潔に説明)が、タプルビーはより興味深い設計洞察を提供することから、タプルビーに絞って検討する。
5.1 関係およびタプルビーによるクエリの実行
図5には、以下の単純なクエリを評価する際のタプルビーの特化役割に関する理解を提供するための例が示される。
SELECT*FROM orders;
図5は、元の関係スキーマ300からの変換、およびタプルビーが存在する対応する特化スキーマ302を表す。この変換は、「元のスキーマ」300から「特化スキーマ」302への矢印によって示される。「ビーキャッシュ」206(図2を参照)は、実行時のメモリ内の記憶空間を表し、この関係に関連するタプルビー全てが特定される。指定されたクエリが評価される際、ビーキャッシュ206内のタプルビーが起動される。「戻されたタプル」310として示される、特定のタプルを戻すために、対応するタプルビーは、(特化スキーマ302に従って)関係から関連属性値をフェッチし、これらの値およびタプルビー内部に記憶された特化値で戻されたタプル310を構築する。
図5において、「元のスキーマ」300は、TPC−Hベンチマークの順序関係からである。このスキーマ300の3つの属性、o_orderstatus304、o_orderpriority306、およびo_shippriority308は、制限された離散値ドメインを有する。これらの値は、したがって特化され、タプルビー内部に記憶される。特化する属性(複数可)をどのように選択するかについては、第5.3項に詳細を記載する。特化後、「特化スキーマ」302によって示されるように、関係にはもはやこれらの属性が含まれない。代わりに、これらの属性の値は、「ビーキャッシュ」206内部の「データセクション」に記憶される。概念的には、各ビーは、ルーチンセクション307およびデータセクション309からなる。この例では、順序関係のタプルビーおよび関係ビーは、同じ組のビールーチン、すなわち第3項の事例研究で検討された、GCLおよびSCLルーチンを共有することに注意されたい。したがって、実行時、タプルビーだけが起動される。一方、関係が特化属性を一切含まない場合、タプルビーに記憶されるタプル特異データは存在せず、この特定の関係の関係およびタプルビーは1つで同じである。
この例において、ルーチンセクション307は、順序関係のタプルビー全てによって共有され(第4.2.1項の関連技術詳細を参照)、各データセクション309は、ビーIDを介して参照される。ビーIDは、タプルヘッダーに記憶される仮想属性である(「記憶されたデータ」の破線ボックスによって示される)。ビーIDは、1バイトの長さであり、したがって、追加の記憶空間が不要であるように、タプルヘッダーに隠れるように注意深く設計され得る。ビーIDは1バイトであるために、タプルビーの最大許可数を256に制限する。各関係は、最大256までの個別のタプルビーを可能にすることに注意されたい。256は、TPC−Hベンチマークの関係全てに対して十分な数であることが見出された。少数のビーは、ビーを記憶するための全体的に必要とされる記憶領域を削減する。
上記のクエリが評価される場合、「記憶されたタプル」を特定するために、関係スキャン操作が最初に実施される(インデックスが存在しないと想定する)。この例では、例えば、第1のタプルがビーID7に関連付けられる。このタプルが関係からフェッチされると、ビーIDは、「記憶されたタプル」とともにGCLルーチンへ送られる。このルーチンは、ビーID7を使用して、7番目のデータセクションを特定し、ハードコードされた値をフェッチする。このルーチンは、オフセットも計算し、カタログを参照することなく、ハードコードされていない属性の値を抽出する。最後に、ルーチンGCLは、その型がホストDBMSによって内部定義される、配列を組み立て、この配列内のそれらの対応するスロットに、ハードコードされた値および入力タプルから抽出された値を入れる。この配列は、次いで、ルーチンGetColumnToStringに渡されて、「戻されたタプル」に変換され、ハードコードされた値がハイライトされる。
タプルビーが起動された場合であっても、クエリ評価は従来の手法と同じ方式で従来どおり実行されることに注意されたい。すなわち、DBMSのいかなる部分のアーキテクチャを変更する必要性がないばかりか、マイクロ特化に適するようにクエリ言語を変更する必要性もない。
タプルビーは、ヌル値(複数可)を含む属性(複数可)を有するか、各属性の型、属性の数、およびタプル内部の各属性の値の場所等、特定のタプルの仕様を利用することができるため、非常に効率的である。
DBMS内の汎用コードをスキーマ内またはタプル内であっても存在する不変量に合わせてカスタマイズされた特化コードに置換するプロセスは、より特化され、そのためにより効率的である、コード[2、10]を示すために、変数の値に関する知識を使用するコンパイラ最適化と同様である。マイクロ特化は、ビーを実行時不変量に関連付けることによって、高度に積極的な特化および定数畳み込みを可能にする。
5.2 関係ビーを作成する
特定の関連ビーのソースコードの作成は第2項に上述した。ここでは、汎用コードブロックを、ビーソースコードを作成するために利用される、特化コードスニペットにどのように変換するかに関して、より一般的な検討を示す。
上記のリスト1において、20〜41行目のコードブロックは、リスト2の6行目等、属性の型にしたがって特化され、代入文に変換される。この変換は、この特定の例では2つのステップで実施されることに注意されたい。最初のステップでは、究極的に現在の属性のオフセットがキャッシュされているかどうかを検証し、キャッシュされていない場合はオフセットを演算するという、リスト1の20〜36行目のコードブロックは、リスト2の6行目の+4によって示されるように、特化コード内の現在の属性のオフセットに対応する実値に変換される。2番目のステップでは、タプルから属性値をフェッチする、リスト1の37行目のコード文は、属性型特異コードスニペットに変換される。この場合、4バイトの整数属性は、リスト2の第6行目のint*キャスト文によってフェッチされる。
この特定の事例研究において、汎用コードブロックを特化コードスニペットに変換する際の複数のステップは、得られるコードの複雑度を抑制し、生成される実行可能コードの性能も最大限にするように、ソースコードのロジックおよび構造の手動解析によって結合される。しかしながら、より一般的な事例では、特に、変換がより系統的様式で実施される場合、手動解析が存在しないかもしれない。代わりに、第4.3.4で検討されるように、元のコードを変換して特化スニペットを生成するために、ソースコード変換機構を採用することができる。次いで、別のステップとして、マッピングされたコードスニペットに最適化を適用することができる。
5.3 タプルビーを作成する
第2項に記載される事例研究において、属性型特異のビールーチンのインストールを示すために順序関係を利用した。この関係では、o_orderstatus、o_orderpriority、およびo_shippriority属性は全て、小さいドメインの離散値を有し、このために、タプルビーが適用されることが可能であることを見出した。タプルビーの適用を促進するために、リスト6において利用されたCARDINALITYキーワードによって示されるように、構文拡張によって順序関係を作成するためのDDL文を注釈する。
CARDINALITYの使用は「注釈」と呼ばれる。このような注釈の効果は、注釈された属性のドメインサイズ、すなわち、離散値全ての数を指定することである。上記の例において、属性o_orderstatus、o_orderpriority、およびo_shippriorityのドメインサイズは、それぞれ、3、5、および1である。1組の注釈された属性は、属性特化の候補と考えられる。我々の現在の実装においては、特化のためにINT、CHAR、およびVARCHAR型をサポートする。
注釈は、小さい濃度を有する「性別」等の属性を指定するために、タプルビーの作成において使用される。注釈は、DBAによって明示的に指定することができるか、または(SQLドメインから等)推論することができる。ビー構成グループの残りの構成要素は、関係のスキーマの変更によってトリガされる、ビー再構築である。
現在の実装は、候補属性のドメイン濃度に基づいて、自動的な属性特化を実施するために最長マッチを行うアルゴリズムを使用する。特化する属性(複数可)を決定するために、アルゴリズム(アルゴリズム1を参照)は最初に、指定された濃度によって候補属性を昇順にソートする。カウンタprod_cardは、ソートされた候補リストを反復する際に、選択された属性の濃度の積を追跡するために使用される。prod_cardの値が、ビーの最大許可数である、max_num_beesに到達すると、アルゴリズムが終了し、最終的に特化するために選択された組の属性(複数可)を生成する。このアルゴリズムは、属性特化がI/OおよびCPU両方の効率を同時に達成することが可能であるため、特化のための属性数を最大にする。属性特化の詳細利点の解析は第8項に記載する。十分であるが、利用されたアルゴリズムは、属性のドメインサイズを考慮するだけでなく、属性型およびサイズ等、多様な他のファクタを考慮するように拡張されなければならない。
PostgreSQLのシステムカタログのデータ構造は、属性が特化されるかどうかを指定するフラグを追加することによって修正された。このフラグは、ビーが作成されるとき、特化属性値を取り扱うためにコードスニペットを必要とする属性(複数可)を決定するために参照される。次項には、特化される属性の構成を宣言して操作する、SQL DDL構文拡張について検討する。
属性の値に加えて、特に可変長属性の後に特定された属性のタプル内部のオフセットは、マイクロ特化を適用する不変量として利用することができる。具体的には、可変長属性が他の属性の前に出現する場合、可変長属性の後にある属性のオフセットは、各個別のタプルに対して、この属性の実値の長さに依存する。いうまでもなく、可変長属性は、定義によって128バイトの最大長を有する場合、残りの属性に対して、最大128の可能なオフセットが存在する。存在する可変長属性が多くなると、可変長属性の後に特定された属性のオフセットの可能な合計数は、その前の可変長属性全ての長さの積である。このような特化は、得られるタプルビーのコードサイズをさらに削減し、それらの効率を向上することができる。しかしながら、必要なタプルビーの合計数は急激に増大する可能性がある。さらに、属性値特化およびオフセット特化を比較すると、前者は、関係から特化属性を排除することによって、CPU時間を向上するだけでなく、I/Oオーバーヘッドも削減する。このため、属性値特化は、オフセット特化よりも優先されるべきである。
タプルの他の特化の機会として、特定のタプルが暗号化を必要とするかどうか、またはどのような種類の暗号化が採用されるか、または一部または全ての属性が圧縮されるかどうかが挙げられる。
関係あたりの最大許可タプルビーの数は、エンジニアリングの考慮に基づいて256に制限された。もちろん、マイクロ特化を適用するために多種の値を利用することができる。一般に、可能なビーの数は、実行時不変行であるそのような変数の発生によって決定される。マイクロ特化を適用する際の1つの課題は、これらの不変量を識別することである。このような実行時不変量をどのように体系的に識別するかの検討は、第9項に記載する。
5.4 タプルビーをインスタンス化する
第3.2項で検討したように、多数のタプルが同じタプルビーを共有することができる。さらに、タプルはすべて、実際には、GCL等、同じ関係ビールーチンを共有することができる。多様なタプルビーの間の相違は、データ値である。クラスタ化された記憶は、個別のデータ値全てに対して作成される。この記憶域は、「データセクション」と呼ばれる。各データセクションは、わずかな属性値を含み、小さい。データセクションが、例えば、一括ロード中に頻繁に作成されるとき、メモリ割り当てを起動するオーバーヘッドは高価になり得る。スラブ割り当て[1]技法は、データセクション作成の前に、メモリ割り当てを取り扱うために適用される。本実装において、ビールーチンが作成されると、ビーキャッシュを記憶するために3つのメモリページが割り当てられ、その中にビールーチンがシーケンシャルに記憶される。次いで、新しいタプルビーが生成されると、ルーチンセクションの後に、データセクションが作成され、記憶される。新しいタプルが必要かを決定するために、特化される属性の値は、シーケンシャル様式で既存のデータセクション全てに対して検証される。このようなシーケンシャルチェックは効率が低い。いずれにせよ、ビーIDの設計に起因して、各関係には最大256のタプルビーが許可されるため、既存のデータセクションを全てを検証することは、相当なオーバーヘッドではない。TPC−Hベンチマークのためのビーキャッシュのサイズの要約は、第8.1.5項に記載される。作成されるデータセクションが多くなると、ビーキャッシュのサイズが8KB(2ページ)を超えた場合、新しいページが許可される。ここでも、データセクションの小さいサイズおよびタプルビーの合計数が小さいことから、TPC−Hベンチマークの各関係に対して全てのビーを保持するには、2ページだけのメモリページで十分であり、そのため、追加のメモリ割り当ては不要である。タプルビー作成のオーバーヘッドを数量化するために、第8.1.5項には詳細の実行プロファイル解析が記載される。
タプルビーが特定のタプルのためのクエリに対して適切な値で応答できるように、データセクションにアクセスするために、関係ビールーチンは、リスト2の7、19、および26行目に示されるように、特化属性に対して「穴」を有するように修正される。bee_id引数は、タプルビーが関連付けられるデータセクションを識別するために使用される。したがって、各タプルとともにビーIDを記憶することが必要である。PostgreSQLに実装されたタプルヘッダーが1バイトの自由空間を有することから、ビーIDは、各タプルのヘッダーに隠すことができ、このため、追加の空間要件は全く発生しない。ビーIDは、限定された値ドメインを含む属性だけが特化されるべきであるという点で十分である。発明者のTPC−Hベンチマークの経験に基づき、関係によって必要とされるタプルビーの最大数は、8つの関係の間では28である。リスト2に示されるマジックナンバー1000、1001、および1002は、正しいデータセクションのアドレスが特定のタプルビーでインスタンス化できるように、プレースホルダーを識別する際に使用される。
5.5 関係およびタプルビーのコストとメリット
関係およびタプルビーは、関係が定義されるときに作成、生成される。DBMS実行時にはコンパイラを起動するオーバーヘッドが導入されるが、関係の作成は、DBMSユーザによって実施されるインタラクティブタスクである。したがって、関係の作成は、性能が重視されるタスクとは考えられず、関係およびタプルビーを生成するコンパイルのオーバーヘッドは許容することができる。
関係およびタプルビーは、クエリ評価時にDBMSによって直接起動され、したがって、このようなビーを管理するための追加のオーバーヘッドは、クエリ評価時には全く導入されない。タプルビーに関して、これらのインスタンス化は、前述のように、一括ロード等、修正時に発生する。大型の関係では、タプルビーのインスタンス化のオーバーヘッドは、事実、タプルビーによって実現されるCPUおよびI/O両方の節約によって補償することができる。ところが、わずかなタプルだけを含む関係では、タプルビーをインスタンス化するオーバーヘッドは、節約を超える可能性がある。しかし、わずかなタプルを含む小さい関係を一括ロードする際、実行時間はそもそも顕著ではない。したがって、タプルビーをインスタンス化するオーバーヘッドは無視することができる。
第6項:クエリビーの詳細
マイクロ特化を適用する原則は、関係ビー、タプルビー、およびクエリビー全体で類似し、最初に、クエリ評価ループ内部で実行時不変量を識別し、次いで、これらの不変量への不要な参照を排除する。しかしながら、不変量の型および発生源の相違に起因して、クエリビーは、関係およびタプルビーとは区別される様式で適用される。クエリビーを適用する機構を詳細に示すため、リスト7に示されるように、例として、TPC−Hベンチマークからクエリ14の別の事例研究が示される。
元のクエリ14は、累積を含む、複雑なSELECT節を含む。関数の累積によるマイクロ特化がまだ調査されていないことから、SELECT文は、単純な属性プロジェクションに変換される。図6には、グラフでクエリ計画312が示される。計画312によって示されるように、内部関係部分が最初にメモリ内へハッシュされる。指定された述語を満たす外部の関係行項目からフェッチされた各タプルの場合、内部ハッシュ表に対してハッシュ結合が実施される。内部および外部両方のタプルからの結合キーが一致すると、SELECT文によってプロジェクトされた属性が戻される。
このクエリにクエリビーを適用する際、各計画演算子は、その演算子に特化する特定のクエリビーを必要とする。例えば、スキャンは、スキャン方向およびスキャンキーの存在を含む、いくつかの実行時不変量を含む。マイクロ特化は、複数のバージョンの特化されたスキャン演算子(関数)を生成するために、これらの値に適用され、各バージョンが特定の方向ならびにスキャンキーの存在(または不在)を処理する。同様に、マイクロ特化は、各演算子の特化コードを生成するために、このクエリ計画全体に適用される。
このクエリは、普通のPostgreSQLおよびビーが有効になったPostgreSQL両方で実行される。クエリを実行時、両方のDBMSによって生成されたクエリ計画が同一であることが確認された。実行時間(ウォームキャッシュ使用)は、それぞれ、前者のPostgreSQLでは1220ミリ秒、後者のDBMSでは961ミリ秒であった。性能は、21%向上した。
6.1 クエリビーの種類:事例研究
このクエリの各計画演算子でマイクロ特化がどのように適用されるかの詳細をここに記載する。
6.1.1 スキャンクエリビー
前述のように、関係スキャン演算子の汎用実装は、複数の可能なケースを処理するための分岐文に依存する。第1に、前方、後方、および移動なしを含む、スキャンの方向には、3つのコード分岐が必要である。第2に、関係スキャンが実行されると、スキャンされた関係の属性のうちの1つに関連する述語が存在する場合、スキャンキーが存在する。したがって、2つのコード分岐が必要である。さらに、関係が空であるかに依存して、関係が空の場合、直接戻る文が実行されるように、2つのコード分岐が実装される。
個別のクエリの評価中、実行パスが必ず一意であることが見出された。すなわち、分岐文に含まれるこれらの変数は実際には不変量である。例えば、スキャン操作の方向は、クエリの実行中は変更されず、また、述語の存在は、関連のスキャンキー処理が必要となるかどうかを判定する。一般に、クエリ毎に、関係スキャン演算子の汎用コードの小さい部分だけが実行される。
これらの発見に基づいて、スキャンクエリプロトビー全てが構築され、各々は特定のケースに対応する。前述のように各変数に対するコード分岐の数から、合計で12(3×2×2)のバージョンのプロトビーが必要である。関係が空である場合、スキャン方向およびスキャンキーを処理する必要性は存在しないことに注意されたい。これは、必要なプロトビーバージョンの合計数は、空でない関係を処理するために6つのプロトビー、および空の関係のための1つのプロトビーを含む、7つであることを示す。
これらの過剰な分岐文およびコード分岐自体を同時に削除することで、コードサイズが削減し、コードの実行効率が向上する。
6.1.2 ハッシュクエリビー
ハッシュ演算子が実行されると、最初に、このハッシュ演算子の下の子計画ノードから物理タプルを抽出する。子計画ノードの種類に依存して、このタプルは、スキャン演算子から直接フェッチされる、内部タプルとして戻される、または外部タプルとして戻されることが可能である。クエリ計画の特定のハッシュ演算子について、その子計画ノードの種類、したがって、関連するタプルの入力源は、クエリ評価中不変量である。さらに、各タプルからハッシュされる属性の数もまた定数であり、クエリ計画に組み入れることができる。
ハッシュ演算子のタプルフェッチコードは、タプルが正しいソースから検索されるように指示するために、switch文を利用する。switch文が排除され、3つの個別のバージョンのハッシュクエリプロトビーが構築された。
別の特化機会は、ハッシュ演算に存在する。多様な型の値をハッシュするには、多様な演算アルゴリズムが求められる。例えば、文字列をハッシュすることと整数をハッシュすることには、異なる手法が必要である。PostgreSQLに既に存在する型特異演算に対する最適化は、関数ポインタを利用することである。文字列ハッシュを実施する関数は、クエリ計画生成中にポインタの形でハッシュ演算子に関連付けることができる。この手法によって、実行時に適切なコード分岐に実行を指示する効率の低いスイッチ文の必要性を排除する。しかし、関数ポインタは、間接的な関数呼び出しによってのみ起動することができ、クエリ評価ループ内で蓄積されると相当なオーバーヘッドとなり得る。
関数ポインタを利用する代わりに、そのような間接的起動文それぞれが、関連するダミーのターゲットアドレスを用いて直接的な呼び出し命令に変換された。ダミーアドレスは、オブジェクトコードから容易に識別できる一意の整数である。実行時に、これらのマジックナンバーは、関数の実際のアドレスと置換された。したがって、間接的呼び出しは、ビーインスタンス化の間に直接的関数呼び出しと置換され、プロトビーが最終の実行可能ビーに変換された。
6.1.3 ハッシュ結合クエリビー
ハッシュ結合演算子の場合、多くのシナリオは、汎用実装で処理されることが必要である。第1に、左側結合、セミ結合、およびアンチ結合等の様々な種類の結合は、実行時に異なる実行パスをとる。第2に、ハッシュ結合演算子は、内部サブ計画および外部サブ計画ノードから、それぞれ、2つのタプルを受け取る。このようなノードそれぞれは、関連するタプルをフェッチするために異なるルーチンが必要な場合がある。例えば、外部サブ計画は、ハッシュ演算子またはスキャン演算子のいずれかであり得る。サブ計画ノードの型は、PostgreSQLのPlanStateデータ構造によって提供される型という名前の変数によって識別される。究極的には複雑なswitch文である、ディスパッチャは、サブ計画ノードを認識し、対応するタプルフェッチ関数を起動する。さらに、結合キー比較は、関数ポインタの起動に関与する、別の型特異演算である。
結合の型は、クエリ計画によって決定され、クエリ計画が演算されると、内部および外部両方のサブ計画の種類もまた不変量である。PostgreSQLは、8つの型の結合を定義することから、8つのバージョンのハッシュ結合クエリプロトビーが構築された。ディスパッチャは、ここでも、実行時に適切なタプル処理関数のアドレスによって置換される、マジックナンバーを利用することによって排除された。最後に、結合キー比較関数の起動は、直接関数呼び出しに変換された。得られた結合評価クエリビールーチンは、「結合評価(EvaluateJoin)」(EVJ)と呼ばれる。
6.1.4 述語クエリビー
述語評価は、述語に型特異比較も関与する、結合キー比較に類似する。このため、同じ技法は、プレースホルダーとして比較関数のアドレスを含む述語クエリプロトビーを生成するために適用される。加えて、ディスパッチャは、述語の中で、タプルがlineitem関係からフェッチされるたびに「1995−04−01」等、定数オペランドを抽出するために利用されることが見出された。この値を毎回抽出する代わりに、各述語クエリビーは、値フェッチコードを削除することによって、単一の述語オペランドに特異であるようにカスタム化される。代わりに、各述語クエリビーに対して、オブジェクトコード内で、オペランドを表す別のマジックナンバーを実値で置換する。この新しいマジックナンバーは、述語比較関数への入力引数のうちの1つとして、述語クエリプロトビーのソースコードで指定される。得られるコードは事実上、値が述語評価コードにハードコードされていることに等しい。述語評価クエリビールーチンは、「EvaluatePredicate」(EVP)と呼ばれる。
6.1.5 クエリビーの他の機会
クエリビーを識別することは、クエリの評価中に存在する実行時不変量を発見することが必要である。このような不変量は通常、データ構造内に特定される。実際に、ここまでクエリビーの形でマイクロ特化が適用される不変量全ては、PostgreSQLに実装された内部データ構造内に存在する。PostgreSQL内部には、70を超えるこのようなデータ構造が見出された。これらのデータ構造の各々は、1つ以上の特定のクエリ計画演算子によって利用される。クエリビーは究極的には、広範囲のクエリにわたるマイクロ特化の性能向上を最大限にするために、これらデータ構造全てで作成することができる。
6.2 クエリビーの作成と生成
クエリ計画演算子にマイクロ特化を適用するための機会は、第6.1項で導入された。ここでは、クエリビーのインストールに関連する特定の機構を中心に記載する。
不変量がコントロールコンストラクトに出現し、この不変量が、2、3の値だけに関連することが知られる場合、このような分岐文および関連の分岐は、特化コードから削除することができる。
第3.4項において、結合演算子クエリビー等のクエリビーに対して、多様な結合型に対応するコード分岐は、クエリ評価時にソースコードのコンパイルが不要であるように、プロトビーに予めコンパイルされることを思い出されたい。
DBMSでは、ネストループ結合、ソートマージ結合、およびハッシュ結合を含む、3つの結合アルゴリズムが通常適合される。PostgreSQLでは、結合型は、3つの種類の結合全てで共通の不変量である。型は、内部結合、外部結合、セミ結合、およびアンチ結合を含む。これらの型の結合の間の違いは、実装という点では、多様な一致要件を処理するために、各型が個別のコードパスに依存することである。例えば、アンチ結合およびセミ結合を処理するために(前者は、内部関係から一致するタプルを全く有さない、外部関係におけるタプル全てが得られ、後者は、外部関係のタプル全てが得られ、各タプルは内部関係から一致したタプルを有する)、PostgreSQLは、リスト8に示される実装(スニペット)を利用する。
現在関与する結合型に対応しない、js.jointype変数および関連の分岐の評価は、結合の種類がクエリ計画で知られている場合、定数折り畳みを介して排除することができる。同様に、結合アルゴリズムでは、JOIN/ONまたはJOIN/USINGから発生する制限条件である、join qualsの存在、およびWHERE節から発生する暗示的にANDされた制限条件である、other qualsの存在である、2つの他のそのような不変量変数が利用された。各々2つの個別の値(0および1)を許可する、両方の存在フラグは、マイクロ特化を適用するために利用される。このため、各結合アルゴリズムには、16の個別のプロトビー(第6.1項で導入されたように)が必要であり、各々は、これらの不変量の値の単一の可能な組み合わせに対応する。16のバージョンのソースコードをプロトビーに作成する代わりに、リスト9に示されるように、汎用バージョンのプロトビーのソースコードが(各々の結合アルゴリズムに対して)コンパイルされ、コンパイラを起動時にDオプションを利用することによって16の値が組み合わされる。例として、リスト9に示されるコードが、オプション、−D JOINTYPE=JOIN−_ANTIを用いてコンパイルされた場合、4行目のif文が排除されることに注意されたい。さらに、8行目および9行目に示されるif文も、その関連分岐とともに、排除される。究極的には、−Dオプションによって、コンパイラは、不要な条件チェックおよび関連の基本的なブロックを排除することができ、高度に最適化されたプロトビーが得られる。プロトビーは、ビーキャッシュに連続的に記憶される。特定のプロトビーを特定するために、不変量の値が利用され、各プロトビーに対して、インデックスのように、値の一意の組み合わせが存在する。例えば、結合が、値3によって内部的に表現される型JOIN_ANTIである場合、この結合が、値1によって示されるjoin qualsを含むが、0によって示されるother qualsは含まない場合、この特定の結合のインデックスは、3×4+1×2+0×2によって計算される、14である。4は、可能な結合型の数であることに注意されたい。同様に、2は、それぞれ、join qualsおよびother qualsの存在の可能なケースの数を表す。
コンパイルビーは、第3.2項で導入された。このようなビーは、究極的に型特異比較関数を含み、直接関数呼び出しを介して、クエリビーによって実行時に起動される。事実、コンパイルビーは、コンパイルビーのオブジェクトコードをインラインすることによってクエリビーに組み入れることができる。コンパイルビーをインラインすることによって、関数呼び出しのオーバーヘッドを排除し、これによって、クエリビーの効率をさらに向上することができる。
6.3 クエリビーによるクエリの評価
ここまで、マイクロ特化が適用可能である多数の場所(演算子)が確認された。特定のクエリ評価を実施するために、いくつかのクエリビーをインスタンス化してから、次いで組み合わせる機構をここで検討する。
クエリ評価中、クエリ計画が決定された後、そのノードは全て、クエリ評価のために不変量となる。クエリビーは、選択されたプロトビーに基づいてインスタンス化される。クエリビーは、最初に、正しいバージョンのプロトビーを選択し、プロトビーを実行可能メモリ領域にロードすることだけによって、インスタンス化される。全てのバージョンの各プロトビーは、ポインタ配列に記憶され、各ポインタは、各バージョンのプロトビーコードの開始アドレスを参照する。正しいハッシュ結合プロトビーを選択するために、対応するバージョンをインデックスするように0から7の範囲である整数値である、結合型が利用される。
次のインスタンス化ステップは、オブジェクトコード操作が必要である。図6に示されたハッシュ結合演算子を例とする。プロトビーが選択された後、前述のように、マジックナンバーは、ターゲット関数の正しいアドレスと置換される。こうして、実際のクエリビーがインスタンス化される。インスタンス化のステップは、実際に、動的リンキングに非常に類似する。ハッシュ結合演算子等の特定の演算子がクエリ計画で複数回出現する場合、ハッシュ結合プロトビーは、複数回インスタンス化されることが必要で、それぞれ、個別の計画ノードに関連するビーが得られる。
得られたクエリビーは、クエリ計画の実行可能コードを形成するために合わせてステッチングされる。従来の実装は、クエリ計画全体を評価する際に、適切な計画演算子を特定し、起動するために、複雑なディスパッチャに依存する。代わりに、構築された計画演算子特異のクエリビーが、ここでも、それらの所望される子クエリビーを用いてビーをインスタンス化することによって、合わせてステッチングされる。クエリビーのソースコードのスニペットの例は、以下のように、このステッチング機構の説明を助けるために提供される。
((FunctionCallInfo)predicate−>xprstate.join_info−>
fcinfo_evj)−>arg[OUTERARGNUM] =
((Datum(*)(TupleTableSlot*, int, bool*))0x440044)(
econtext−>ecxt_outertuple,
0x101,
&((FunctionCallInfo)predicate−>xprstate.join_info−>
fcinfo_evj)−>argnull[OUTERARGNUM]);
この特定の関数起動文(ダミーアドレス0x440044まで)は、実際に、子計画ノードに対応するクエリビーを起動し、外部サブ計画からタプルをフェッチすることに注意されたい。実行時に、マジックナンバー0x440044は、実際の子ビーのアドレスと置換される。
クエリビー全てのインスタンス化に2、3のメモリコピーおよびいくつかのインプレースメモリ更新しか必要としないことから、このように、クエリビーのインスタンス化は非常に効率が高く、最小のオーバーヘッドを発生する。
6.4 クエリビーを起動する
リスト10には、クエリビーを起動する、ExecEvalOperfunctionからのコードスニペットが示される。クエリ評価中、この特定の関数は、計画演算子を実行するために必要な情報を初期化するために、1回呼び出される。この関数は、具体的には、7行目に示されるように、fcache−>xprstateの構造体のevalfuncフィールドを初期化する。evalfuncフィールドに渡された関数アドレスは次いで、タプル毎のベースで、その後の呼び出しによって起動される。この関数は、12行目で第1のタプルを処理するために呼び出され、実際の評価関数によって計算された値を戻す。
評価関数は、8行目に示されるにように、クエリビーに置換される。この例では、クエリビーは、特定の述語を評価するためである。クエリ準備段階で、インスタンス化されたクエリビーのアドレスは、各クエリ計画ノードが、存在する場合、特定のクエリビーによって置換できるように、クエリ計画に関連するデータ構造、すなわちxprstateのデータ構造に記憶される。ビールーチンは、元のDBMSコードに対する変更を抑制するために、普通の関数起動と同じ関数呼び出し署名を有することに注意されたい。
6.5 ホットスワップビー
ここまで、クエリ評価中に定数値をとる変数という、不変量にマイクロ特化を適用することを中心とした。ここで、検討は、各々がクエリ評価中に値が判定される、変数に一般化される。
例として、スキャン演算子、特に、リスト11に示される2つのコード分岐を検証する。rs_inited変数は、スキャン演算子が初期化されているかどうかを示す。すなわち、この変数は、現在のタプルが、クエリ内部でスキャンされた関係からフェッチされている最初のタプルであるかどうかを表す。この変数は次いで、クエリの残りの部分では、真が代入される。定義では、この変数は、厳密には不変量ではない。しかし、この変数は、最初のタプルがフェッチされた直後に定数であることが知られるという事実によって、条件文を評価することは、タプルの残りの部分では冗長になる。
このため、2つの追加のバージョンのスキャンクエリプロトビーが生成される。第1のバージョンは、上記のコードの第1のコード分岐を含み、第2のバージョンは、他のコード分岐からのコードを含む。空以外の関係を処理するために、既に6つのバージョンのスキャンクエリプロトビーが存在することから、合計で13(空の関係を処理するバージョンを含めて、6×2+1)のバージョンが必要となる。
インスタンス後にオブジェクトコードが修正されるほかのビートは異なり、インスタンス化されたスキャンクエリビーは、自己修正の対象となる。このような機構は、図7に示されるコールグラフ320で示される。この図において、ビー324、326、および328は、矩形として表される。普通の実装において、関数SeqNext322は、ヒープファイル内の次のタプルをフェッチするために、関数heap_getnext324を呼び出す。関数heap_getnextは次いで、現在スキャンされているページ上に特定された実際のタプルを呼び出すために、heapgettup_pagemodeという関数を呼び出す。heap_getnextが初めて呼び出された場合、いくつかの初期化が実行される必要がある。図7において、heapgettup_pagemode_initは、含まれた初期化コード分岐だけを含む、特化されたバージョンのheapgettup_pagemodeを表すビー326である。同様に、heapgettup_pagemode_regularは、他のコード分岐だけを含む。heapgettup_pagemode_initの実行中、heap_getnextビー324のオブジェクトコードは、_initバージョンへの元の呼び出しが、_regularバージョンにホットスワップされるように、編集される。ホットスワップは、関数呼び出しアドレスのインプレース更新、この場合、_initビー326への呼び出しを_regularビー328への呼び出しに変更することによって、簡単に実行される。そこから、後のビー328が呼び出される。値のシーケンスに対して、複数のホットスワップが存在し、各々、次の特化バージョンへの呼び出しにスワップする。ホットスワップは、スワップされるビーへの呼び出しもビーであることが必要であるため、この呼び出し元ビーを修正することができる。このため、大型のビーは命令キャッシュ圧力を導入する可能性があるため、呼び出し元ビーは関数全体である必要はない。代わりに、ビールーチンは、ホットスワップしているビーを起動する関数呼び出し文だけの大きさに小さくすることができる。
PostgreSQLソースコードのより詳細研究によって、ソートマージ結合演算子もこのような特化から利益を受ける可能性があることが明らかになった。ソートマージ結合アルゴリズムにはいくつかの状態が関与し、各々は、汎用関数の個別のコード分岐に関連する。ソートマージアルゴリズムの実行は、これらの状態の間で切り替わり、各々の状態は固有の状態に続く。
このような特化がどの状況に適用されるべきかを示す2つの単純な規則が示唆される。まず、分岐文で使用される変数に関連する個別の値はわずかしか存在しない。第2に、この変数は、実行パス選択がこの変数の値に依存するように、分岐文で参照されなければならない。
一般に、実行時に必要なクエリビーだけがインスタンス化され、起動されるので、作成および生成することができるクエリビーの数に制限は存在しないことに注意されたい。このため、クエリ評価性能は、有効なクエリビーの合計数によって影響されず、どのクエリビーが作成されるかを決定する際にトレードオフは存在しない。マイクロ特化を適用するための不変量の選択を最適化する、アルゴリズム1に類似のアルゴリズムは、クエリビーによって要求されない。
動的なオブジェクトコードの操作は、マルチスレッドのクエリ実行環境では、ホットスワップしているビーがビーのオブジェクトコードを更新するために、複数のスレッドによって起動されると、慎重に同期が処理されることが必要であるという懸念が生じる。しかし、PostgreSQLは、各クエリを実行するために1つのプロセスだけを採用するので、このような考慮は我々の実装では生じない。さらに、各クエリ評価は、クエリビーの個別のインスタンス化が必要であることから、コードの再入可能性は、オブジェクトコードが実行時に動的に修正される場合であっても、各スレッドがそれに固有のビーを利用するため、保存される。
CALLGRINDによって収集された実行時プロファイルを使用するクエリビーの性能利点をここで検証する。第1に、コンパイラの起動のオーバーヘッドは、性能解析に含まれないことを特筆する価値がある。これは、コンパイラはクエリ評価中には実行時に決して起動されないからである。代わりに、クエリ評価前にプロトビーがコンパイルされ、したがって、実行時に、実行可能なビーを動的にインスタンス化し、起動するオーバーヘッドはささいである。CALLGRINDによって収集された実行時プロファイルは、プロファイルツールによって採用されるサンプリングベースの機構に起因し、ビーをインスタンス化するためのレコードさえ含まない。
リスト12およびリスト13に示されるのは、それぞれ、普通のDBMSおよびビーが有効なPostgreSQL上で例示的なクエリを実行する際のプロファイル出力の抜粋である。表記Irは、実行された命令の数を表すことに注意されたい。プロファイル結果に示されるように、普通のDBMSは、合計で7429Mの命令を実行した。一方、ビーが有効なDBMSは、4940Mの命令を実行し、実行された命令の数が34%削減した。
次に、性能向上を説明するために、特定の関数の命令カウントが確認される。最も顕著な向上は、slot_deform_tuple関数からである。この関数は、物理的タプルをロング整数の配列に変換する。この関数は、クエリで参照される両方の関係において、各タプルに起動されることに注意されたい。したがって、この関数を特化することで、最も顕著な利点を達成する。リスト13が示すように、slot_deform_tupleは、それぞれ、0x441b3c0および0x442e7c0のメモリ内場所として表される、2つの関係ビーを起動することによって高度に特化される。このような特化の結果、クエリが実行された場合、合計で20%の命令が削減される。
述語の存在は、マイクロ特化を適用するための別の機会を提供する。普通のDBMS内のExecMakeFunctionResultNoSets関数は、述語評価を実施する。対照的に、クエリに存在する2つの述語は、メモリ内のアドレスとして、リスト13に示されるように、2つの述語ビーによって評価された。2つの述語クエリビーだけで、合計で約7%実行された命令を削減した。
各マイクロ特化は性能を向上するが、いくつかのマイクロ特化は、影響が小さい場合がある。heapgettup_pagemode関数は、関係をスキャンする役目がある。この関数の実装は、第6.5項で検討した。普通の実装において、この関数は、スキャンの方向を確認し、述語の存在をチェックすることが必要である。プロファイル結果が示すように、これらの不変量にマイクロ特化を適用することによって、その関数自体の命令の約32%が削減される。138Mの命令の削減は、向上全体の約2パーセントである。普通の実装によって利用されたディスパッチャ、ExecProcNode(プロファイル内では、2つのこのようなインスタンスが存在する)は、合計11Mの命令に寄与する。ビーが有効なPostgreSQLにおいて、このオーバーヘッドは完全に排除される。合計で、マイクロ特化が複数の演算子全体で積極的に適用された場合、比較的性能利点が低いクエリビーによって、さらに約7%の命令が削減された。
実行時にビーをインスタンス化することは、追加の命令が実行される必要があることに注意されたい。ただし、CALLGRINDは、この追加のオーバーヘッドはカウントするには小さすぎるため、このようなデータを収集することができなかった。
要約すると、クエリビーは、最初に、クエリ評価ループ中に不変量を識別することによって利用される。次いで、関連するプロトビーが、実行可能クエリビーとして動的にインスタンス化される。未使用のコード分岐を排除し、間接的な関数呼び出しを直接呼出しに変更する等、いくつかの最適化を適用することによって、顕著な性能利点を達成することができる。
6.6 クエリビーのコストとメリット
第3.4項で検討したように、クエリビーの作成および生成は、DBMSがコンパイルされるときに実施される。すなわち、これらの2つのタスクによって導入されるオーバーヘッドは、コンパイル時のものである。
実行時に、クエリビーをインスタンス化するオーバーヘッドが導入される。したがって、クエリ評価中にクエリビーを起動することによって達成される性能利点は、大部分が、クエリのサイズ、すなわち、クエリの評価中に各クエリが何回実行されるかに依存する。クエリが小型の関係にある場合、クエリビー全てをインスタンス化するオーバーヘッドは、これらのビーが達成することができる実際の性能ゲインを超える場合がある。したがって、クエリ評価中にクエリビーが利用されるべきであるかの選択を行うために、クエリされる関係のサイズ、およびクエリ内の述語の選択性に基づいて、予想モデル等の機構が必要である。
第7項:キャッシュ圧力を緩和する
プログラムの実行中、実行された命令は各々、最初にCPUキャッシュにフェッチされる。キャッシュ内のスロットは(L1キャッシュまたはL2キャッシュまたはIキャッシュ)、キャッシュラインと呼ばれる、命令が特定された場所で、仮想メモリ空間内の命令のアドレスによって決定される。同じキャッシュ行を共有している複数の命令が実行されると、キャッシュ競合が発生し、高価なキャッシュミス違反となる(キャッシュ行の消去、およびメモリ階層内のより下位からフェッチされる命令)。
DBMSでは、クエリ評価は通常、多数の反復から実行される相当量の命令が関与する。このクエリ評価コードの大量のフットプリントは、命令失敗違反に起因して、潜在的に、性能低下につながる可能性がある。GCC等のコンパイラは、コードのローカル性を向上するために、基本ブロック順序変更技法を適用し、それによって命令キャッシュミスを削減するが、DBMSがコンパイルされるときにビーが存在しないため、ビーコードは、この最適化による利益を受けない。このため、この課題に対応するために3つの効果的な手法が提供される。
クエリ評価中にビーを実行するキャッシュの効果を示すために、図8は、クエリの評価中の、ビーの多様な配置(x軸によって示される)と、I1キャッシュミスのパーセント増加(y軸によって示される)との間の関係の研究を示す。第8項に記載するように、実験マシンは、32KのI1キャッシュが装備される。各キャッシュ行は64バイトの幅で、キャッシュは4方向のセット関連性があることから、合計で128のキャッシュスロットが存在し、各々が4つのキャッシュ行からなる。16の配置が、128の可能なキャッシュスロット全て全体で均一にサンプリングされる。すなわち、同じクエリが16回評価され、各タイミングで、各クエリ評価中にビーの異なる配置が実行される。図によって示されるように、ビーが0x10および0x40のアドレスのキャッシュスロットにそれぞれ配置されると、I1キャッシュミスレートが抑制される。しかしながら、ビーが0x68に配置されると、キャッシュミスで130%を超える増加が観察される。一般に、命令キャッシュの全体的性能は、ビーの配置に対する感度が非常に高い。
キャッシュミスレートは、最適化されていないビー配置では顕著に増加し得るが、この特定のクエリでは実際の実行時低下はささいである。これは、図9に示されるように、TPC−Hクエリのほとんどの全体的なI1キャッシュミスレートが約0.3%であるためである。このようなわずかなキャッシュミスレートが130%増加しても、クエリの実際の実行時間に関して顕著な影響はない。
しかしながら、多くのビーがクエリ評価に利用されると、特により複雑なクエリ内部またはより多くの実行時命令が関与するより高度なDBMS内部では、ビーの不用意な配置は、実際の実行時性能に顕著な影響を与えかねない。事実、22のTPC−Hクエリの中で1つの特定のクエリ(クエリ6)では、I1キャッシュミスレートが約1%、実行された命令が4.8BおよびI1キャッシュミスが47Mであることが観察された。第7.3項で検討したように、適切なビー配置では、I1キャッシュミスレートは、わずか0.1%まで削減した。この削減は、高価なキャッシュミス違反に起因して、15%を超える実行時間向上に変換された。したがって、キャッシュの圧力は、実行時性能に直接影響を与える重要な問題と考えられる。適切なビー配置を演算するために、3つの手法が提供される。
7.1 呼び出し元の隣のローカル配置
プログラムを実行時の命令キャッシュミスレートを削減するために、コード生成時に適用される典型的なコンパイラ最適化技法は、生成された実行可能コードで基本的なブロックおよび関数の順序を変更することである。このコードレイアウト最適化は、最初に静的コールグラフを識別してから、次に生成されたコードで呼び出し元およびその呼び出し先を相互に隣り合わせに配置することによって達成される。アイデアは、これらの2つの関数が十分に小さく、キャッシュ全体を占有しないと仮定して、関数が別の関数を呼び出すと、それらをシーケンシャルに配置することによって、これらの2つの関数が相互にオーバーラップしない連続的キャッシュラインにマッピングされることになるということである。この手法は、2つの関数からの命令が同じキャッシュラインにマッピングされる可能性を効果的に削減することができる。
PostgreSQL DBMSおよびビーコードをコンパイルするために、GCCが使用されてもよい。GCCは、呼び出し先を呼び出し元の前に配置することによって、関数レイアウト最適化を実施する。同じ戦略はビーを配置するために採用された。ビー呼び出し元は、PostgreSQL実行可能から容易に識別することができる。したがって、これらのビー呼び出し元のアドレスは、DBMSコンパイル時に決定することができる。ビーを配置すると、キャッシュラインにマッピングすることによって、ビーの呼び出し元から2、3前のスロットである、メモリアドレスが選択された。スロットの数は、ビーのサイズによって決定される。
この配置では、クエリ6のI1キャッシュミスは、元の47Mから19Mに削減し、60%のキャッシュミス削減となった。これで、クエリ6のI1キャッシュミスレートは、0.4%(19M/4.8B)となった。
その簡易性および効率にも関わらず、この手法には欠点がある。クエリ評価によって実行される内部ループのコードは通常、キャッシュ内の大きいフットプリントを占める。このため、その呼び出し元だけを考慮してキャッシュ領域にビーを配置すると、ビーが何らかの他の頻繁に実行される関数とオーバーラップしてしまう場合があり、顕著なキャッシュ性能の低下になる。この懸念は、上記の手法が一般に適用可能ではないことを示す。技術的には、ビーのこのような配置は、一般に他のDBMS関数に関わらず、ビー呼び出し元に対してローカルに限定される。この手法は、「ローカル手法」と呼ばれる。しかしながら、その簡易性のため、それでもビー配置の代替手法として検討される。
呼び出し元の隣のローカル配置の擬似コードを以下に示す。
PlaceBees(executable_file)
bees_to_place=InstantiateBees();
foreach bee in bees_to_place
do
bee_caller=FindBeeCaller(bee,executable_file);
memory_offset=BeginAddress(bee_caller)−SizeofBee(bee);
cache_line=TranslateMemoryAddress(memory_offset);
PlaceBeeatCacheLine(bee,cache_line);
end for
7.2 グローバル関数スコアベース配置
前述の手法の欠点を避けるために、DBMS内の関数全てをビー配置のグローバルビューとして考慮する。具体的には、実行可能コード全体が128のキャッシュスロット全てにマッピングされる。図10は、累積キャッシュスロット参照ヒストグラムを示す。x軸は、キャッシュスロット全てを表す。y軸は、各個別のキャッシュスロットにマッピングされている関数の数を示す。
図10に示されるように、それぞれ、0x07〜0x10および0x56〜0x60、に特定される、2つのキャッシュ領域340および342は、最小の数の関数を含む。このようなキャッシュ領域340および342は「谷」と呼ばれる。直感的な解決策は、これらの谷340および342にのみビーを配置することである。
しかしながら、このような配置戦略の制限は、特定の実行可能プログラムに対して、谷が必ず利用可能であることに保証がないことで、その場合、ビーのために実現可能な配置が存在しない。さらに、この戦略配置手法の別の欠点は、クエリ評価中に、DBMS内の関数全てが起動されないことであり、さらに、起動された命令の中で、それらの全てが実行時性能の影響という点で同等な重要性を持つわけではない点である。例えば、SQLパース関数によって起動される命令は、結合関数からの命令よりも重要度が低く、後で、DBMS操作中に後者よりも頻繁に実行される。図10に示されるように、キャッシュスロット0x10では、最大数の関数が観察されるが、図8は、対照的に、最も少ないI1キャッシュミスは、まさにそのポイントで実行時に発生することを示唆する。
ビーがクエリ評価内部ループ内で頻繁に起動されることから、ビーの配置は特に、これも内部ループに存在する関数からの命令との競合を回避しなければならない。このような関数は、「ホットスポット」関数と呼ばれる。このようなホットスポット関数を検出するために、クエリ評価をプロファイルするためにVALGRINDが利用された。評価クエリでは、DBMSの異なる組の関数が起動され得る。例えば、単純なスキャンクエリを評価する場合、テーブルスキャン関数だけが必要である。しかし、結合を含むクエリでは、テーブルスキャン関数だけではなく、起動された結合アルゴリズムの関数も必要とされる。内部ループの包括的な関数のセットをキャプチャするために、トレーニングサンプルとして、個別の特徴を有するいくつかのクエリのセットが作成される。
ホットスポット関数が識別されると、これらの関数のうちのより高い優先度を統合するために、スコアスキーマが変更される。ホットスポット関数の重要点は、より高いユニットスコアによって単純に表される。標準の命令(ホットスポットからではない)には、0.5のスコア(任意に選択された小さい数)が割り当てられ、ホットスポットからの命令には、各命令は100のスコアが割り当てられる。これらの2つのスコア値の顕著なギャップは、頂点と谷との間の顕著な区別となり得て、谷の識別が容易になる。
この配置手法は、クエリ6を再び評価するために適用された。I1キャッシュミスにおいてさらなる5Mの削減が観察された。I1キャッシュミスレートはこれで0.3%で、14Mを超えるI1キャッシュミスとなる。
グローバル関数スコアベース配置の擬似コードを以下に提供する。
ComputeFunctionScoreHistogram(executable_file,query_loop)
histogram=NIL
foreach cache_line in cache_lines
do
histogram[cache_line]=0
end for
foreach function in GetAllFunctions(executable_file)
do
if function in query_loop
then
histogram[BeginAddress(function)..EndAddress(function)]=
histogram[BeginAddress(function)..EndAddress(function)]+0.5
else
histogram[BeginAddress(function)..EndAddress(function)]=
histogram[BeginAddress(function)..EndAddress(function)]+100
end if
end for
return histogram
PlaceBees(executable_file)
bees_to_place=InstantiateBees();
query_loop=ExtractQueryLoop(executable_file);
histogram=ComputeFunctionScoreHistogram(executable_file,query_loop);
foreach bee in bees_to_place
do
cache_line=FindValleyCacheLine(histogram)
if cache_line is valid
then
PlaceBeeatCacheLine(bee,cache_line)
else
PlaceBeeatCacheLine(bee,0)
end if
end for
7.3 プロファイル解析および内部ループ識別ベースのグローバル配置
上記の2つの手法には利点および欠点がある。第1の手法は、一般に適用するには、ローカルすぎる場合がある。他の関数を考慮せずにビー呼び出し元を考慮することで、キャッシュ効果は、既存のホットスポットおよび不適切に配置されたビーの競合によってさらに増幅される可能性が高い。
第2の手法の主な利点は、PostgreSQL実行可能ファイル全体を考慮することで、解析を完全にする。しかしながら、静的命令マッピングには、重要ではない命令が大量に含まれる。この欠点によって、解析が不正確かつ困難になる。ホットスポット関数が優先付けられる場合でも、スコアスキーマは、谷を正確に識別するには、まだ粗すぎる。
したがって、クエリ評価中に実行される命令だけに注目することによって、実行時の谷をより正確に識別するために、プロファイル解析および内部ループ識別ベースのグローバル配置(PAILIGP)という名前の第3の手法が提供される。この新しい手法は、以下の3つのステップからなる。
・第1に、内部ループは体系的に識別されることが必要である。
・第2に、内部ループは、実行時ホットスポットを正確に反映するために、実行プロファイルと組み合わされることが必要である。
・最後に、キャッシュスロットは、谷を明らかにするために応じてスコアが決定される。
第1のステップでは、PostgreSQLの実行可能コードからのクエリ計画演算子関連関数全てが特定され、それらのアドレスおよび長さが記録される。クエリ準備時に、キャッシュスロット参照を追跡するために、128の数の配列が構築される。各演算子ノードに組み込まれた概算濃度は、演算子が実行される回数として利用される。したがって、演算子がいくつかの特定のキャッシュスロットにマッピングされるたびに、ヒストグラム内の対応するキャッシュスロットの値は、概算された濃度の値だけ増分される。クエリ計画が生成された後、ヒストグラムが完全に構築され、このヒストグラムに基づいて谷を識別する。
静的および動的両方の解析を組み合わせる、PAILIGP配置最適化を使用すると、クエリ6の評価中のI1キャッシュミスはほぼ7Mまでさらに削減される。I1キャッシュミスレートはこれで、わずかに0.1%を超える。実行時間は、I1キャッシュミスレートが1%であった元の実行時間より15%改善される。非常に複雑なクエリを評価する際に関与するさらに多くのビーでは、命令キャッシュミスレートは急速に増大する可能性があり、したがって、ビー配置最適化戦略は、実行時性能を向上する際に非常に重要になる。
要約すると、ビー配置を最適化するために、3つの手法が提供される。これら3つの手法は、TPC−Hベンチマークからクエリ6を評価することによって比較された。不用意なビー配置では、クエリ6を評価するI1キャッシュミスレートは1%だった。呼び出し元の隣のローカル配置では、I1キャッシュミスレートは0.4%に削減された。グローバル関数スコアベース配置は、I1キャッシュミスレートを0.3%に削減した。最後に、プロファイル解析および内部ループ識別ベースのグローバル配置は、I1キャッシュミスレートをわずか0.1%に削減した。一般に、PAILIGP手法が最も効果的であると考えられ、最初の2つの手法に存在する欠点を回避する。
プロファイル解析および内部ループ識別ベースのグローバル配置の擬似コードを以下に提供する。
ComputeHotSpotHistogram(hotspot_functions)
histogram=NIL
foreach cache_line in cache_lines
do
histogram[cache_line]=0
end for
foreach function in hotspot_functions
do
histogram[BeginAddress(function)..EndAddress(function)]=
histogram[BeginAddress(function)..EndAddress(function)]+1;
end for
return histogram
PlaceBees(executable_file,profile_result)
bees_to_place=InstantiateBees();
query_loop=ExtractQueryLoop(executable_file)
runtime_hotspot_functions=
IdentifyHotspotFunctions(query_loop,profile_result);
histogram=ComputeHotSpotHistogram(runtime_hotspot_functions);
foreach bee in bees_to_place
do
cache_line=FindValleyCacheLine(histogram)
if cache_line is valid
then
PlaceBeeatCacheLine(bee,cache_line)
else
PlaceBeeatCacheLine(bee,0)
end if
end for
第8項:評価
マイクロ特化は、多数の分岐を含む汎用コードを、クエリ評価ループで不変量であると識別された値に依存する、高度にカスタマイズされたコードに置換する。その結果、各ビー起動でより少ない命令が起動され、全体で合計されると、ループの周囲でしばしば何百回であり、性能の顕著な向上となり得る。さらに、ビーを実行することによって導入される命令キャッシュ圧力を抑制する手法が前項で考察された。
この項では、事例研究で検討されたような単純な選択クエリ、TPC−Hベンチマーク[13]のOLAP様式クエリおよび高性能の一括ロード、ならびにTPC−Cベンチマーク[12]のOLTP様式クエリおよび修正という、多くの状況に対して、マイクロ特化の性能影響に関する実証的研究が提供される。
TPC−Hのデータセットを生成するために、DBGENツールキット[13]が利用された。データ生成のスケールファクタは、1に設定され、データのサイズは1GBになる。スケールファクタが5および10に設定された性能研究も実施され、それぞれ、5GBデータベースおよび10GBデータベースを生じる。TPC−Cの場合、BENCHMARKSQL−2.3.2ツールキット[9]が使用された。ウェアハウスパラメータの数は、初期のデータセットを作成した際、10に設定された。このため、作業負荷をシミュレーションするために合計で100台の端末が使用された(TPC−Cの資料に指定されるようにウェアハウスあたり10台)。第5項のリスト6に示されるように、TPC−H関係におけるいくつかの低濃度属性を識別するために、DDL注釈も追加された。スケールファクタ、DDL注釈、およびウェアハウスの数を指定した以外、実験データセットを準備するために、TPC−CおよびTPC−Hツールキットで使用される他のパラメータには変更は全く行われなかった。
実験の全ては、4つのコアを含む、2.8GHz Intel i7 860CPUを装備したマシン上で実施された。各コアは、32KBの命令(I1)および32KBのデータキャッシュからなる、64KB レベル1(L1)キャッシュを有する。CPUは、256KBの未統一レベル2(L2)キャッシュでも構成される。プロトタイプ実装は、PostgreSQLバージョン8.4.2を使用し、デフォルトのビルドパラメータを用いて(具体的には、最適化レベルが−O2である)GCCバージョン4.4.3を使用してコンパイルされた。第7.3項で導入したPAILIGP配置が、ビーを配置するために利用された。
8.1 TPC−Hベンチマーク
まず、TPC−Hベンチマークは、ビー有効のPostgreSQLと普通のDBMSの性能を比較するために利用される。TPC−Hベンチマークは、商業用データウェアハウスに類似するデータベースを作成する。ベンチマークで使用されたクエリは、複雑な解析クエリである。このような作業負荷は、集中的な結合、述語評価、および集約を特徴とし、大量のディスクI/Oおよびカタログ参照が関与する。一括ロードの研究において、同じ関係を投入する際の実行時間の向上が数量化された。
TPC−Hベンチマークは、8つの関係を含む。2つの関連ビー(supplierおよびpartsupp)および85のタプルビー(以下の第8.1.5項で記載するように、他の6つの関連のため)の合計サイズは21KBである。クエリビー全てのサイズは59KBである。この追加されたコードは、16MBである、ビーが有効なPostgreSQLの実行可能ファイルのサイズのわずかほんの一部(0.5%)と計測される。第3.4項に記載のように、HREは、ビーが有効なPostgreSQLにコードを追加する。また、実行時に作成されるためにソースコードが必要である、関係およびタプルビーを作成する関数は、ビーが有効なPostgreSQLにコード量を追加する。HREは、約225KBのオブジェクトコードから構成されることが見出された。関係およびタプルビーの作成関数は、わずか2KBを超えるバイナリコードからなる。この合計は、300KB未満である。この追加のコードは、実行時に、(16MBのうち)約2%未満のフットプリントしかPostgreSQLに導入しない。
ビーが有効なPostgreSQLのサイズ(静的)は、普通のPostgreSQLより大きいが、各クエリの評価中、これらのビーの一部しか起動されない。より小さいビールーチンが、実行時のメモリにより大きいフットプリントを有するDBMSの汎用コードを代用するため、実行時の命令キャッシュ性能は実際に向上する。
8.1.1 スケールファクタ=1のクエリ評価性能
TPC−Hに指定された22のクエリ全てが普通およびビーが有効なPostgreSQLの両方で評価された。実行時間は、ウォームキャッシュのシナリオで、ウォールクロック時間として測定された。ウォームキャッシュのシナリオは、最初に、CPU性能を研究するために対処される。メモリ内のデータを維持することで、ディスクI/Oリクエストを排除した。次いで、コールドキャッシュシナリオが検討される。
各クエリは、12回実行された。最大および最低の測定値は、外れ値と考え、したがって排除された。各クエリの実行時間測定値は、残りの10回の実行の平均として考慮された。22のクエリの各々の残りの10回の実行の標準偏差は、1%未満であることが見出された。
結果の有効性および再現性を保証するために、これらの22のクエリを評価する際に、普通およびビーが有効なPostgreSQLの両方が事実同じクエリ計画を使用していることを保証しようと試みた。2つのDBMSが必ず同じ計画を選択することを保証することは、特に、下位の関係がマイクロ特化を通じて2つのDBMSの下で、関係サイズ、タプルサイズ、および関係によって占有されるページ数等、異なる特徴を有するため、困難であった。しかしながら、postgresql.confファイルのdefault_statistics_targetパラメータを1000に設定することによって(100がデフォルト)、クエリのうち21が2つのDBMSで同じ計画を使用していることを保証することが可能であった。異なる計画を用いる唯一のクエリはクエリ21であった。
図11は、バー1−22として示される、ウォームキャッシュで22のクエリの各々のパーセント性能向上を示す。2つの要約測定値が含まれ、斜線バーとして示される、Avg1 344およびAvg2 346と呼ばれる。Avg1 344は、各クエリが同等に加重されるように、22のクエリ全体の平均パーセント向上によって計算される。Avg2 346は、クエリ評価時間全ての合計を比較することによって計算される。クエリ17およびクエリ20は、残りは1〜23秒かかったのに対し、それぞれ1時間および2時間という、はるかに長い時間が終了にかかったことから、Avg2 346は、これらの2つのクエリに非常に偏向する。向上の範囲は、1.4%〜32.8%で、Avg1 344およびAvg2 346はそれぞれ、12.4%および23.7%である。この実験では、タプルビー、関係ビー、およびクエリビーが有効に設定され、GCL、EVP、およびEVJビールーチンが関与する(これらのビールーチンの検討については、第2項および第6項を参照)。図11に示されるように、Avg1 344およびAvg2 346はどちらも大きく、わずかなビールーチンを使用してビーが有効なPostgreSQLで達成された性能向上が顕著であることを示す。
図12は、各クエリで実行された命令数の改善を表す。動的命令の削減カウントは、0.5%〜41%の範囲で、Avg1 350およびAvg2 352はそれぞれ、14.7%および5.7%である。CALLGRINDでプロファイリングすると、プログラム実行は通常、終了するまで、約200回もっと長くかかることに注意されたい。このため、プロファイルデータは、q17およびq20では収集されず、このため、プロファイルに関する結果は、これらの2つのクエリでは省略された。(これは、本項に示されたTPC−Hクエリに関する他のプロファイルベースの研究で同様である。)このプロットは、実行時間の向上は、実行された命令の削減と高い相関関係があることを示し、マイクロ特化のメリットが、削減された命令実行から生まれることをさらに強調する。
タプルビーによって達成されたI/O向上を確実にするために、ディスクI/O時間が実行時間全体の主要な構成要素になる、コールドキャッシュで22のクエリの実行時間が検証された。図13は、コールドキャッシュでの実行時間向上を示す。向上範囲は、0.6%〜32.8%で、Avg1 354は12.9%、Avg2 356は22.3%である。この図と図11の顕著な違いは、q9の性能がコールドキャッシュで顕著に向上している点である。その理由は、q9が6つの関連スキャンを有することである。タプルビーは、lineitem、orders、part、およびnation関係で有効になっている。このため、これらの関係をスキャンすると、特に、最初の2つでは、属性値特化から顕著なメリットが得られ(ディスク読み出しの回数を削減)、このため、コールドキャッシュで17.4%の向上が達成される。
8.1.2 スケールファクタ=5のクエリ評価性能
より大型のデータベースでの性能利点をさらに確認するため、TPC−Hベンチマークのスケールファクタを5に増加した。このより大きいスケールファクタでは、Postgre−SQLのデフォルト構成は十分ではない。具体的には、デフォルト構成では、shared_bufferパラメータおよびwork_memパラメータは、それぞれ、わずか24MBおよび1MBに設定される。このような小さい設定は、クエリ評価中の頻繁なディスク読み出しおよび書き込みを招く。shared_bufferパラメータは、PostgreSQLによって利用されるバッファプールのサイズを表す。shared_bufferは、メインメモリのサイズの最大40%まで、可能な限り大きく構成されるべきであることが示唆される[11]。TPC−Hベンチマークの関係のうちの5つのサイズは、256MB以下である。他の3つの関係は、それぞれ約700MB、900MB、および4GBである。shared_bufferは、256MBに設定され、わずかな関係がメモリにキャッシュされることを可能にする一方、他の関係がディスクから読み出されるように強制する。加えて、work_memパラメータの場合、PostgreSQLの参考マニュアルでは、このパラメータは、「一時ディスクファイルに切り替える前に、内部のソート操作およびハッシュテーブルによって使用されるメモリ量を指定する」と記載されている。[11]このパラメータは、クエリ評価が大型の関係をハッシュおよびソートする間、これらの操作全体がメモリ内で実行されるのではなく、相当量のI/Oをまだ要求するので、100MBに設定された。
TPC−Hクエリ(クエリ17およびクエリ20を除く)は全て、ウォームキャッシュで両方のDBMS上で実行された。ビーが有効なPostgreSQLによって達成された性能向上は、図14に報告される。この図によって示されるように、スケールファクタが5の場合に観察されたAvg1 360およびAvg2 362の性能向上は、図11に報告されたスケールファクタが1の場合に観察された性能向上と(わずかなパーセントポイント分高いものの)同程度である。クエリ17およびクエリ20がないため、図14に示されるAvg2 362は、図11に示されるAvg2よりも低い。
性能ゲインの発生源を究明するため、TPC−Hクエリを評価中に実行プロファイルが収集された。ビーが有効なPostgreSQLおよび普通のPostgreSQLを比較して、実行された命令数における向上は、図15に示される。スケールファクタが1の場合に収集された結果に類似して、クエリ評価時間の向上および実行された命令数の削減の間に強力な相関関係が観察される。スケールファクタを5に設定すると、達成されたクエリ評価時間の向上は、最高36%である。TPC−Hクエリ全体の平均向上は、Avg1 364およびAvg2 366で、それぞれ、14%および10%である。
8.1.3 スケールファクタ=10のクエリ評価性能
ここで、スケールファクタを10に設定して生成される、さらに大型のデータセットの実験に注目を移す。この場合、3つの関係のサイズは256MB以下である。他の関係は、300MB〜10GBまでばらつきがあり、3つの関係はサイズが1GBを超える。この大型データセットに適切に合わせてPostgreSQLを構成するために、クエリ評価中、特にソートおよびハッシュ中に、非常に頻繁なディスクI/Oを削減することができるように、実験用マシンは8GBのメインメモリを有することから、shared_bufferは最大に示唆される3.2GBに設定された。work_memパラメータは、2つの最大の関係が、ディスクベースの手法でソートおよびハッシュされるために必要である、2GBに設定された。
TPC−Hクエリが実行され、ビーが有効なPostgreSQLによって達成された向上を図16に示す(Avg1 368およびAvg2 370を参照)。
スケールファクタが1および5であった場合とは異なり、クエリ評価の向上は、実行された命令数の削減ともはや関連しないことが、図17に報告される(Avg1 372およびAvg2 374を参照)。これは、この大型データセットでは、PostgreSQLは大量のバッファプールを有するように構成されたが、ディスクI/Oが圧倒的なボトルネックになるためである。クエリ6を例として説明する。図17は、実行された命令の削減が40%を超えることを示唆する。しかしながら、クエリ評価時間の向上はわずか約6%である。
クエリ6のクエリ評価時間は、両方のDBMS上で調査された。普通のPostgreSQLでは、クエリ評価時間合計は98秒であることが見出された。CPU時間は18秒であった。
時間の約82%はディスクI/Oに費やされた。ビーが有効なPostgreSQLでは、CPU時間は12秒に短縮し、実行された命令の40%削減と一貫する。しかし、ディスクI/Oはそれでも、90秒のクエリ評価時間合計の最高87%を占める。このため、全体的な向上が相当な影響を受けた。
実験マシンが限定されたメモリを有することから、多様なメモリ構成による性能の向上は、さらに調査することができなかった。96GBのメモリで構成される別のマシンがこの大量のデータセットの性能実験に利用された。
まず、両方のPostgreSQL DBMSは大量のメモリで構成された。具体的には、shared_bufferパラメータは、最大推奨サイズ32GB(96GB×40%)に設定された。work_memパラメータは8GBに設定され、8つのTPC−H関係のうちの7つのハッシュおよびソートがメインメモリ内部で動作することを可能にする。しかしながら、10GBを超えるlineitem関係は、依然として、ディスクベースのソートおよびハッシュが必要である。
クエリ評価時間の向上および実行された命令の削減は、それぞれ、図18(Avg1 376およびAvg2 378を参照)および図19(Avg1 380およびAvg2
382を参照)に示される。大量のメモリを構成したことで、クエリ評価時間の向上はここでも、実行された命令の削減と一貫する。さらに、スケールファクタが10の場合に達成された性能向上は、スケールファクタが5の場合に達成された向上と同程度である。
クエリ評価時間向上に対するI/O効果をさらに理解するために、PostgreSQLのメモリ関連構成を変動させることによって、クエリ7が詳細に研究された。この複雑なクエリは、6つの関係の結合を含む。また、このクエリは、5つの述語、1つの集約関数、および演算子による順番が関与する。
work_memパラメータは、ディスクベースのハッシュおよびソートによって生じたI/Oオーバーヘッドと、マイクロ特化によって達成されたクエリ評価時間の向上との間の関係を調査するために解析された。図20は、work_memパラメータの多様な値で、ビーが有効なPostgreSQLによって達成されたクエリ評価時間の向上を示す。work_memの値が増加すると、クエリ評価時間の向上も増加する。この特定のクエリでは、述語は、lineitem関係からプロジェクトされたタプルの数を顕著に削減するので、得られる中間関係の結合は、相当量のメモリを必要としない。したがって、work_memパラメータを128MBより大きく設定すると、両方のバージョンのPostgreSQLの実際のクエリ評価時間は、変動しない。このため、ビーが有効なPostgreSQLによって達成されるクエリ評価時間の向上は、20%で安定する。
この研究は、クエリ評価中に、ソートおよびハッシュのために追加のI/Oオーバーヘッドが関与する場合であっても、マイクロ特化は、相当な性能向上を達成することが可能であることを示す。
第2の研究では、work_memパラメータの値は固定されたが、shared_bufferパラメータが変動された。これは、関係の読み出しのI/Oオーバーヘッドと、マイクロ特化によって達成される性能向上との間の関連性を研究するためである。この研究では、クエリ7がコールドキャッシュで評価されたことに注意されたい。これは、実験用マシンが96GBのメインメモリで構成され、TPC−H関係全てをキャッシュできるためである。したがって、関係がディスクから読み出されることを要求するために、コールドキャッシュが利用された。また、オペレーティングシステムキャッシュだけがクリアされたことにも注意されたい。PostgreSQLによって管理される共有メモリはクリアされない。共有メモリは通常、関係タプル、中間クエリデータ、およびDBMSによって利用される多様な他のデータによって占有される。shared_bufferパラメータを1GBに設定することによって、クエリ評価中にほとんどの関係がディスクから読み出されることを要求する、PostgreSQLによって最大1GBのデータをキャッシュすることが可能になる。一方、shared_bufferを16GBに設定すると、クエリ評価中により少ないI/Oが要求され、オペレーティングシステムキャッシュはクリアされなくても、TPC−H関係の部分が共有メモリにキャッシュされ得る。shared_bufferが64GBに構成されると、ベンチマークの関係全てはメモリ内に十分にキャッシュすることができる。
DBMSにより多くのメモリが構成されると、クエリ評価により少ないI/Oが要求され、したがって、ビーが有効なPostgreSQLは、より顕著な性能向上を達成すべきであることが予想される。すなわち、性能向上は、メモリを増設することによって、単調に増大すべきである。しかし、図21に示されるように、現実は、この予想よりも複雑である。
図21には、3つの興味深い現象が示される。まず、8Gでは、クエリ評価時間の向上に約15%〜20%を超えるジャンプが存在する。第2に、16Gでは、クエリ評価時間の向上は約10%減少し、より少ないメモリが構成された場合よりもさらに低い。最後に、64Gでは、クエリ評価時間の向上は22%以上急激に上昇する。
これらの現象を理解するために、さらなる実行統計が調査された。表8.1において、num.blocks(普通)およびnum.blocks(ビー)という名前の列は、多様なメモリ構成で、それぞれ、普通のPostgreSQLおよびビーが有効なPostgreSQLで、クエリ7の評価中のディスクから読み出されるブロック数を示す。shared_bufferが1Gで構成される場合、ビーが有効なPostgreSQLは、約10%少ないディスクブロックを読み出す。これは、この場合、lineitem関係でタプルビーを利用することによって、この関係のサイズが10%削減されるためである。追加の詳細は第8.1.5項に記載する。shared_bufferに8Gが構成されると、ビーが有効なPostgreSQLによって読み出されるディスクブロック数に顕著な減少が観察される。いうまでもなく、このような減少は、shared_bufferが8Gの場合の普通のPostgreSQLでは全く観察されない。これは、ここでも、ビーが有効なPostgreSQLによって作成されるTPC−H関係は、一般に、普通のDBMSによって作成される関係より小さいためである。8Gは、この特定のクエリに対して、ビーが有効なPostgreSQLの下でより多くの関係がメモリ内に適合可能な点であり、普通のDBMSは、さらに多くの関係がディスクから読み出されることを要求する。
第2の現象に関して、16Gで、普通のPostgreSQLがより多くの関係をキャッシュできることを観察した。一方、ビーが有効なDBMSは、shared_bufferに8Gが構成されているため、同じ量のディスクブロックが読み出されることを要求する。表8.1において、I/Oパーセント(普通)およびI/Oパーセント(ビー)の列は、それぞれ、普通のPostgreSQLおよびビーが有効なDBMSの全体的なクエリ評価時間におけるパーセントI/O時間を示す。16Gの行の値によって示されるように、I/Oは、普通のPostgreSQLによって、クエリ7を評価するための時間全体の55%を占める。
ビーが有効なPostgreSQLの場合、クエリ評価時間のうちの50%がI/Oによって占められる。これらの2つの値は、8Gの列に示される値と同程度である。したがって、shared_bufferが8Gから16Gになると、クエリ評価中に読み出されるディスクブロック数の減少が、ビーが有効なPostgreSQLによって達成される全体的な性能向上の減少となる。
最後に、64Gの列に示されるように、普通のPostgreSQLおよびビーが有効なPostgreSQLのどちらも、クエリ評価中にディスクブロックがロードされることを要求しない。このため、図21に示されるように、22%である性能向上は、圧倒的にCPU時間によって占められ、100%近くである。
要約すると、マイクロ特化は、I/O時間およびCPU時間両方の向上を同時に達成することが可能である。全体的なクエリ評価性能向上は、さまざまな因子によって決定される。例えば、DBMSに構成されたバッファキャッシュのサイズは、クエリ評価中に要求されるディスクI/Oの量を決定する。小さいキャッシュは、全体的なクエリ評価時間内の高パーセントのI/O時間となる。このような場合、マイクロ特化によって達成される性能向上は大部分がタプルビーに起因する。一方、大量のバッファは、最小のI/Oを保証し、したがって、性能向上は、クエリ評価中に実行される命令数の削減と一貫する。バッファのより現実的な構成は、I/O時間の向上およびCPU時間の向上を平均化する。例として、ソリッドステートドライブ等、より高速のディスクドライブが採用される場合、全体的な評価時間におけるI/O時間のパーセントは、効果的に削減することができ、より顕著なCPU時間の向上、したがってより大きい全体的な性能向上を示す。概して、マイクロ特化は、DBMSバッファの多様な構成によって顕著な向上を達成することができる。
8.1.4 複数のビールーチンの影響
各クエリの性能向上は、起動される追加のビー各々によって増加する。第2項において、関係ビーのGCLルーチンが9%の向上を達成したことを思い出されたい。基本的な質問は、ビーをさらに追加することによって、さらにどの程度の向上を達成することができるのか、である。より重要な点として、多数のビーは相互に悪影響を与えるのだろうか。
多様なビールーチンを有効にする影響が検証された。結果は、図22に要約される。この図によって示されるように、GCLルーチンだけの平均向上は、Avg1 386では7.6%およびAvg2 390では13.7%である。EVPルーチンを有効にすることによって、Avg向上は、11.5%(Avg1)および23.4%(Avg2)まで到達する。
一般に、22のクエリの中で、4つのクラスの向上が観察される。最初のクラスは、クエリ2によって示され、追加のビールーチンが有効になるたびに、性能が大幅に向上する。これは、このようなクエリは、多数の結合および述語評価からなり、このために、クエリビーが性能に顕著な利益を与えることができるためである。第2のクラスは、クエリ6によって示され、述語評価ビールーチンを有効にすると、性能が大幅に向上するが、評価結合ビールーチンを有効にしても向上しない。これは、クエリ6が結合を含まない4つの述語からなるためである。第3のクラスは、クエリ12によって示され、関係およびタプルビーは、性能向上に対する主な寄与因子であり、クエリビーはより低い性能影響を有する。これは、クエリ12がTPC−Hベンチマークで最大の関係のうちの2つを読み出すという事実に起因する。わずかな述語および単一の結合だけが関与するため、全体的な性能向上は、これらの2つの関係からフェッチされたタプル全ての必要な属性値を抽出する、タプルビーによって寄与される。
最後に、第4のクラスは、クエリ1によって示され、全体的な向上は、上述のクラスよりはるかに低い。これは、これらのクエリでは、マイクロ特化が、関与する特定の動作にまだ利益を与えていないためである。例えば、クエリ1は、複雑な集約演算からなり、これは、マイクロ特化によってまだ特化されていない。クエリ9およびクエリ16について、両方のクエリに含まれる述語を評価中に実行される、正規表現のマッチングは、2つのクエリの評価中のほとんどの時間を占める。正規表現のマッチングを実施するコードは、ここでも、特化されていない。さらに、クエリ18は、タプルのソートを要求する。タプルソートコードは、特化されておらず、実行時にクエリ評価時間の大部分を占める。これらの特定のクエリは、クエリ評価中にマイクロ特化をどこで適用するかについて、さらに調査する機会を与えてくれる。
この最後のクラスで、クエリ11は、マイクロ特化がこのクエリに対して限定された性能利点を有することも示す。これは、クエリ11は、3つの非常に小さい関係にアクセスするためである。これらの関係のうちの2つは、タプルビーを有さないが、関係ビーを有する。第3の関係はタプルビーを有するが、この特定の関係においては、25のタプルのみが存在する。このため、達成することができる全体的な性能利点は、これらの関係のサイズによって限定される。この特定のクエリは、第5.5項および6.6項に示されたマイクロ特化の利点についての検討を実際にサポートする。
クエリ3およびクエリ4の場合、EVJルーチンを有効にすることで、性能向上にわずかな低下を示すことにも注意されたい。これらのクエリの実行プロファイルが研究され、EVJルーチンを有効にすることによって、実行された命令の数がわずかに減少することが見出された。さらに、命令キャッシュミスも減少する。このため、性能向上におけるこのようなわずかな低下は、実行時間測定に存在する変動によってのみ、正当に説明することができる。
マイクロ特化手法が繰り返し適用できることが暗示される。マイクロ特化を適用する場所が多くなると、DBMSが達成することができる効率が向上する。増分性能達成というこの特性は、「ビー加法性」と呼ばれる。
DBMSのほとんどの性能最適化は、クエリまたは修正のクラスに利点があるが、他を低速化させる。たとえば、B+ツリーインデックスは、結合の効率を高めるが、更新を低速化させる。ビールーチンは、利点のみであるという優れた特性を有する(修正中のタプルビーのインスタンス化およびビーによって追加されるキャッシュ圧力という2つの警告があるが、それぞれ、第8.1.5項および第7項に記載する)理由は、ビールーチンがクエリによって使用される場合、そのクエリの性能がいずれの場合も影響を受けないからである。一方、ビールーチンがクエリによって使用される場合、特に、ビールーチンがクエリ評価ループ内で実行することから、クエリの性能は顕著に向上し得る。図11および図22はどちらも性能向上の間に差を示すことに注意されたい。例えば、q1、q9、q16、およびq18は全て、比較的低い向上を経験する。理由は、これらのクエリは全て、実装にまだマイクロ特化されていない、複雑な集約演算、サブクエリ評価、ならびに複雑な正規表現によるマッチングを有するためである。これらの向上が低いクエリは、集約、およびおそらくサブクエリ評価に対して、マイクロ特化を適用するための他の機会を示す。
8.1.5 一括ロード
懸念は、新しく挿入されたタプルから特化された属性値が、新しいタプルビーが必要かを判定するために確認されることが必要であるという点で、関係を投入する等、修正中のタプルビーインスタンス化が高価なことがあるということである。さらに、新しいビーが作成されると、このビーを記憶するために、新しいメモリ空間が割り当てられることが必要である。この第2の警告による性能の影響の可能性を解明するために、TPC−Hベンチマークの関係全てで一括ロードが実施された。表8.2は、スケールファクタが1の場合のこれらの関係のいくつかの特徴をまとめ、表8.3は、スケールファクタが10の場合のTPC−H関係の特徴をまとめる。関係およびタプルビーの数、ならびにそれらのサイズはこれらの2つのデータセットで同じままであることに注意されたい。したがって、関連列は表8.3から削除された。
ビーが有効なPostgreSQLの一括ロード性能は、普通のバージョンと比較された。一括ロードではクエリ評価が実施されないため、SCLビールーチンだけが関与する。図23は、各関係に対して、ロード時間の高速化を示す。TPC−Hベンチマークにおいて、region400およびnation402関係は、各々、2つのディスクページだけを占有し、2つの関係をロードする性能影響を測定不可能であるように非常に小さくする。したがって、各関係に対して、1M行を含むデータファイルが作成された。図23に示された、これらの2つの関係をロードする性能は、これらの2つの関係各々に1M行を投入することに基づく。残りの測定値(part404、customer406、orders408、およびlineitem410)は、元のスキーマおよびデータに準じる。これらの性能向上は、スケールファクタが10の場合とほぼ同じであるべきで、一括ロード中に達成された向上は、I/Oの削減と強力に相関する。このような相関のさらなる証拠を図24に示す。
タプルビーを利用しないsupplierおよびpartsupp関係を一括ロードするために、SCLという名前の、タプル構築関係ビールーチンは、達成された性能向上のほとんどに寄与した。これらの2つの関係では、タプルビーが全く存在しないようにディスクスペースが削除されないという事実に起因して、残りの前述の関係と比較して、向上が低い。
図24は、関係400、402、404、406、408、および410の各々に対して、属性特化によって導入されたディスク空間削減を示す。削減は、DDL文によって定義されたタプル長に基づいて計算された。orders関係を例として挙げると、定義によるタプルサイズは、スキーマ定義によって143バイトである。3つのハードコードされた属性の型CHAR(1)、CHAR(15)、およびINTを、それぞれ加算して20バイトになる。すなわち、空間削減は14%である。
図23に示される一括ロード性能向上は、一括ロード中のタプルビー作成のオーバーヘッドは、実際に、マイクロ特化の利点によって補償され、全体的な一括ロード性能が向上することを示唆する。
これらの2つの図は、連携して、一括ロードの性能向上は、I/O時間の向上と強力に相関することを示唆する。実行時命令の面から性能向上をさらに理解するために、orders関係を一括ロードする際の特性が研究された。普通のPostgreSQLおよびビーが有効なPostgreSQLの特性結果は、それぞれ、リスト14および15に示される。
リスト14によって示されるように、この関係を一括ロードする際、普通のPostgreSQLは148Bの命令を実行した。一方、ビーが有効なPostgreSQLは、リスト15によって示されるように、146Bの命令を実行した。前述のように、一括ロード中にタプルビーが作成される。具体的には、タプルビーの作成は、現在挿入されたタプル内の特化属性の値を(既存タプルビー内の)既存のデータセクション全てに対してチェックするオーバーヘッドを導入する。このチェックは、リスト15に示される、InsertDataSectionToBeeCache関数によって実行される。加えて、ビーが有効なPostgreSQL内のヒープフォームタプル関数は、1.97B件の命令を実行する一方、普通のバージョンでは、この関数は1.69B件の命令を実行することが見出された。このほぼ300Mに近い命令の増加は、ビーが有効なPostgreSQL内の各タプルのビーIDを記憶させるコードによって導入された。これらの2つの関数は連携して、追加の1.3B件の命令を発生させたが、普通のPostgreSQLにおいて、物理的タプルを構築するheap_fill_tuple関数が4.6B件の命令を実行したのに対し、ビーが有効なPostgreSQLは、リスト15によって示されるように、代わりに、2.4Bの命令を実行した、SCLビールーチンを利用し、実行された命令数で2.2Bを超える削減を有効にし、orders関係を一括ロードするための全体的な性能はそれでも向上した。
要約すると、一括ロードにおける性能向上は、実行された命令数の削減、ならびにディスク空間の削減、このために入力時間の削減の両方の結果である。両方の利点は、関係ではなく、ビー内部の(個別の)属性値を効率的に記憶することによって、これらの値があたかもオブジェクトコードにハードコードされているかのように、タプルビールーチンによりこれらの値の直接アクセスすることができるように、属性値を特化するタプルビーを利用することによって同時に達成される。
個別の属性値がタプルビー内に記憶されることが必要な場合、必要なメモリを前もって割り当てるために、スラブ割り当て技法が採用され、したがって、タプルビー作成中の高価な小さくかつ頻繁なメモリ割り当てを回避する。新しいビーが必要であるかどうかを判定するために、memcmpを用いて、いくつか(最大256)の可能な値をチェックする。図23は、このステップが有効であることを示す。
要約すると、ビー作成は、DBMS動作の性能に悪影響を与えず、むしろ、単一のビールーチンであってもその利点によって性能が向上する。
8.2 TPC−Cベンチマーク
TPC−Cベンチマークは、スループットを中心とする。このベンチマークは、並列に実行する5つのタイプのトランザクションが関与する。スループットは、1分あたりに処理される「New−Order」トランザクションの数(tpmC)として測定される。この研究の場合、tpmC値が高くなると、性能(スループット)が向上することを示すことに注意されたい。他の4つのタイプのトランザクションは、無作為なクエリおよび修正の混合を生成し、これらは合わせて、ビールーチンを集中的に起動する。
このベンチマークは9つの関係を含む。属性値の仕様に起因して、属性値は全く特化できない。このため、TPC−Cベンチマークでは、関係ビーのみが有効である。関係ビー全体のサイズは26KBである。クエリビー全ては連携して、59KBの記憶容量が必要である。
実験では、ビーが有効なPostgreSQLを普通のDBMSと比較した。各DBMSは、自動バキュームプロセス等、実験用システムならびにDBMSによって導入された差異を削減するために1時間実行された。
マイクロ特化を用いて修正を実施すると、前者は、1分あたり1898トランザクションを完了したが、普通のDBMSは、1分あたり1760トランザクションを実行することができ、7.3%の向上で、実際に高速化した。
TPC−CベンチマークのこのtpmC測定評価基準以外に、異なるトランザクション設定でのスループットも研究された。さらに2つの極めて異なるシナリオが対処された。5つの定義されたトランザクションタイプのうち3つ、New−Order、Payment、およびDeliveryは、クエリおよび修正の両方を含み、一方、Order−StatusおよびStock−Levelはクエリのみを含む。どちらのシナリオでも、New−Orderトランザクションの加重は45%に保たれた。デフォルトの設定は、Paymentトランザクションの加重が43%である、修正主体のシナリオに類似する。新しく定義されたシナリオに関して、第1は、27%のOrder−Statusおよび28%のStock−Levelトランザクションからなる(すなわち、クエリのみ)。第2のシナリオは、修正およびクエリ両方が等しく混合される。PaymentおよびDeliveryトランザクションの加重は27%であるが、他の2つのタイプのトランザクションは、連携して28%に加重される。
第1のシナリオ、クエリのみの場合、ビーが有効なPostgreSQLおよび普通のDBMSは、それぞれ、1分あたり3699および3135件のトランザクションを処理し、18%の向上だった。第2のシナリオ、修正およびクエリが等しく加重される場合、ビーが有効なPostgreSQLは、2220件のトランザクションを達成し、普通のバージョンは1998件を終了した。向上は11.1%であった。
プロファイルの結果、修正およびクエリ両方は、タプル値を抽出するために、slot_deform_tuple関数に依存することを示唆する。この関数は、GCLルーチンでマイクロ特化されるため、TPC−Cベンチマークの多様なシナリオで顕著な性能向上が達成される。さらに、この作業負荷のクエリは述語が関与するため、EVPルーチンは、向上したスループット、特にクエリ中心のシナリオにも寄与した。
8.3 コンパイラ最適化との比較
クエリ評価におけるマイクロ特化の性能効果は、コンパイラ最適化を併用した場合も研究された。調査された1つの重要な質問は、コンパイル時に(DBMSの)積極的な最適化を有効にすることが、マイクロ特化によって達成される性能向上を削減するかどうかである。すなわち、コンパイラ最適化は、マイクロ特化に対して同じ性能利点を十分に達成することができるのか、それによって、マイクロ特化が不要であることを示すだろうか?
TPC−Hベンチマークのクエリ6の性能が、スケールファクタを1に構成して研究された。具体的には、普通のPostgreSQLおよびビーが有効なPostgreSQLの両方は、3つのオプションを用いてコンパイルされた。これらのオプションは、−O0(最適化なし)、−O1(コードサイズおよび実行時間を削減)、および−O2(空間と速度のトレードオフを要求しない最適化全て)である[7]。
表8.4に、両方のDBMSに対するクエリ6の評価時間、およびマイクロ特化によって達成される対応する性能向上をまとめた。
この表が示すように、より積極的な最適化オプションが有効になると、マイクロ特化によって達成される全体的な性能向上がわずかに減少する。しかし、向上は全て多様なコンパイラ最適化に対して顕著であり、コンパイラ最適化だけによって達成される性能向上は、マイクロ特化によって達成される利点を十分に代用することができないことを示す。
その上、さらに積極的な最適化、フィードバック駆動最適化(FDO)が調査された。分岐条件チェックを削除する等、マイクロ特化によって実現される所定の最適化は、それぞれ、FDOおよび最新CPUによって採用される、分岐予想および命令パイプラインによって同等に達成することができると論じるかもしれない。理想的なFDO最適化によってPostgreSQL実行可能を生成するために、コンパイラのサンプル実行としてクエリ6だけが実行された。クエリ6は、FDO最適化を適用して、両方のDBMS上で再び実行された。普通のPostgreSQL上では、クエリ6の実行は1359ミリ秒かかった。ビーが有効なPostgreSQLは、このクエリを990ミリ秒で評価した。性能向上は27%であった。
上記の研究は、理想的な最適化を用いる場合であっても、マイクロ特化は、既存のコンパイラ最適化に加えて、顕著な向上を達成することが可能であることを示す。マイクロ特化の性能利点は、実行時命令の削減だけではない。実行されたコードサイズの削減は、クエリ評価中に命令キャッシュ圧力の低下を示す。
第9項:マイクロ特化の自動適用
マイクロ特化は、静的および動的プログラム解析を含む、複雑なタスクを必要とする。多様なマイクロ特化のタスクは、自動化された様式で実行することができる。
以下のステップを実行するために、多様なツールが提供される。これらのツールは、連携して、「高度に統合された開発環境(HIVE)」と呼ばれる。HIVEの実装は、第9.2項で簡単に検討する。HIVEは、関数バックエンドおよびEclipseプラグインベースのフロントエンドユーザインタフェースからなる。
9.1 マイクロ特化を適用するためのステップ
図27は、マイクロ特化の適用を自動化するための具体的なタスクにそれぞれ対応するステップのシーケンスを含むプロセスのフローチャート480を示す。
ステップ1(ブロック482)。クエリ評価ループを識別する。DBMSのかなり大きく複雑な実行可能コードからクエリ評価ループを表現するコード部分を正確に抽出するために、DBMS内部の基本ブロックの静的コールグラフが構築されてもよい。次いで、このグラフから強力に接続された構成要素が演算されてもよい。強力に接続された構成要素は、クエリ評価ループだけを表現する基本ブロックのセットを提供する。
例えば、この静的コールグラフ解析によって、リスト1に示されるslot_deform_tuple関数は、クエリ評価ループ内部にあると特定される。
ステップ2(ブロック484)。不変量を特定する。不変量を検出するには、動的解析が必要である。正確な実行時メモリアクセストレースを生成するために、VALGRIND等のプロファイルツールがクエリ評価と共に起動される。トレースは、(address,opcode,operand)の形式の三項のリストを含み、クエリ評価において値が不変量である変数を特定するために、以前に演算されたクエリ評価ループの基本ブロックセットおよび動的データフローグラフと組み合わせられる。クエリ評価中、特定のメモリ場所によって表される所定の変数が、(おそらく初期化中に代入によって)1回書き込まれてから、次いで、(クエリ評価ループ中に)複数回読み出される場合、このような変数は、可能性として実行時不変量として考えることができる。このようなメモリアクセスパターンは、実行トレースを解析することによって明らかにすることができる。
このような集中的な実行トレース等を収集することは、トレースレコードが命令あたりのベースで収集されるので、非効率的であり得ることに注意されたい。したがって、このようなトレースを解析することは、時間がかかるタスクである。マイクロ特化を適用することができる実行時不変量の1つの特徴は、これらの不変量は通常、分岐条件チェック文に関与することである。このため、cmp命令全てはクエリ評価中だけに収集することができ、これによって、実行トレースを収集するオーバーヘッドを相当削減することができる。
このステップは、VALGRINDの拡張が必要である。実行トレース生成を実現するために、代替のプロファイルツールも利用する(かつ拡張)ことができる。
ステップ3(ブロック486)。ソースコード内の不変量を究明する。不変量変数は次いで、ソースコードで定義されたデータ構造体にマッピングされる。特定された不変量は、オブジェクトコードでは、命令のオペランドとして表現される、メモリ場所である。これらの不変量の実際の参照および宣言を特定するために、命令をソースコードにトレースするために、debug_lineが利用される。
リスト1に示されるslot_deform_tuple関数内部のnattsおよびattalign等、実行時不変量変数を正確に示すため、プロファイル解析は静的プログラム解析と組み合わされなければならない。
ステップ4(ブロック488)。どのコードシーケンスがマイクロ特化されるべきかを決定する。次に、特に、各シーケンスの正確な境界を決定するために、特化されるターゲットコードシーケンスそれぞれが検証される。このために、静的データフロー解析に依存して、値が不変量であるコードシーケンスを特定する。これらのコードシーケンスは、コールグラフの早期またはそのリーフ近辺のいずれかに存在し得る。理想的な特化ターゲットは、短いコードシーケンス内部に比較的多数の使用を含む。
第2項で検討した事例研究の場合、slot_deform_tuple関数内部の属性値抽出ループが特化された。
ステップ5(ブロック490)。ビーの関連タスクをいつ実施するかを決定する。異なる種類のビーに対して、第3.4項で検討した関連タスクは、異なる時点で実行されることが必要である。開発者は、最初に、5つのタスクの各々が適切な時点で実行されるように、HREに組み入れることができるように、設計および作成されるビーの種類を決定することが必要である。
表3.1に従って、5つのタスクのコードがHREに実装された。
ステップ6(ブロック492)。ターゲットのソースコードは、ビールーチンを設計するために、スニペットに変換される。関係ビールーチンを検討する。このルーチンはおそらく、関係の属性すべてを処理する。属性の型に特化されるとする。実際の関係ビールーチンは、各々可能な型に対して1つのスニペットから構築されなければならず、スキーマに従ってステッチングされる。この特定の事例では、スニペットは、switch文から抽出される。別の例として、リスト1の11行目の属性のforループを検討する。そのループの本体からスニペットが作成される。
コードシーケンスに別の関数への呼び出しが含まれている場合、その呼び出しが、パラメータとして不変量値のうちの1つを渡すと、その呼び出された関数もまた、関数起動をインラインした後、このビールーチンの一部として特化される。(そうでなければ、ビーは関数呼び出しだけを保持する。)
第2項で検討したように、コードスニペットは、個別のコード分岐を利用することによって、関係およびタプルビーに対して設計され、各々が属性値抽出ループ内部の属性の所定の型を処理する。スキーマ定義時に、関係およびタプルビーのソースコードを形成するために、これらのコードスニペットが選択される。関係およびタプルビーソースコードの作成は、HRE内に実装されたプロシージャによって行われる。実行時にソースコード作成が必要な任意の新しい種類のビーに対して、このタスクを実行するコードは、そのような種類のビー各々に実装されることが必要である。
ステップ7(ブロック494)。ビー起動およびサポートコードをDBMSソースに追加する。特化されたコードは、ここでDBMSから削除され、対応するビールーチンへの呼び出しで置換される。
ビーを追加すると、DBMSの他の部分に影響を与える場合がある(希望的には、非常に限定された方式において)。第5項で検討したように、タプルビー内に記憶された属性はもはや関係そのものには記憶されない。TPC−Hのorders関係では、o_orderstatus、o_orderpriority、およびo_shippriorityという、3つの属性が特化され、これらは、小さい離散値ドメインを有する。これらの属性は、これらの値が各タプルにインスタンス化されたビーに記憶されるため、スキーマから削除される。この変更を有効にするために、コードをDBMSに追加しなければならない。
必要な関数呼び出しは、PostgreSQLに統合された。例えば、リスト3は、PostgreSQLに統合されたタプルビーを起動する、このような関数呼び出しを示す。
ステップ8(ブロック496)。確認実験性能解析を実行する。そのビーを利用すべきクエリ上に追加されたビー各々の性能利点を検証することが重要である。ビーが有効なDBMSおよび普通のバージョンを比較することによって性能を研究するためにベンチマークが利用される。詳細な研究は、クエリの実行時間、トランザクションのスループット、ならびに命令およびキャッシュ統計のプロファイルを含み、第8項に検討される。
第8項で詳細を記載するように、マイクロ特化の多くの利点を研究するために、包括的な性能解析を実施した。
これらの8つのステップは、本発明人がマイクロ特化を手動で適用した経験に基づいて、マイクロ特化を適用する際の既知の課題に対応するために提供される。これらのステップを実行する開発者を支援するために、マイクロ特化のプロセスを簡素化し、自動化することを目的とするツールセットが提供される。
9.2 HIVE:高度に統合された開発環境
HIVEは、連携して、各マイクロ特化を実現するために必要な関連タスクを実行する、一連のツールからなる。
図25は、HIVE442の上位アーキテクチャ430を表す。具体的には、この図に示される各ボックスは、8つのステップの中のいくつかのタスクを実施する施設を示す。直線は、制御フローを表し、破線は、データフローを表す。HIVE442は、ECLIPSEプラグインベースユーザインタフェース432を提供し、これによって、開発者は、マイクロ特化を適用するための8つのステップを実行するための一連のツールと相互作用することが可能になる。このユーザインタフェース432内部で、開発者は、実行可能プログラムの静的構造を研究するために、オブジェクトコードアナライザ434を起動することができる。具体的には、オブジェクトコードアナライザ434は、DBMS実行可能の静的コールグラフを解析し、クエリ評価内部ループを演算する(ステップ1)。この内部ループは、コールグラフとしても表現され、ユーザインタフェース432で視覚化することができる。オブジェクトコードアナライザ434は、動的実行解析を実施するために、プロファイルアナライザ436と連携して起動することができることに注意されたい。具体的には、オブジェクトコードアナライザ434によって生成される静的コールグラフは実行頻度によって増強され、実行されない関数が静的コールグラフから排除され、プログラムの実行時挙動のより正確な研究を可能にする(ステップ2)。
ユーザインタフェース432を使用することによって、開発者は、ソースコード内の不変量変数を識別する等、ソースコード研究を実施するためにソースコードアナライザ438を起動することができる。識別された変数は、ユーザインタフェース432を通じて視覚化される(ステップ3)。開発者は、ビーソースコードスニペットをHRE442に指定し、ビー起動文をDBMSに追加することによって、ビーを設計するためにソースコードアナライザ438を直接利用することもできる(ステップ4〜7)。
最後に、DBMS実行コントローラ440によって、DBMSは、ユーザインタフェース432内部から稼動することが可能になり、実験目的で、必要な構成パラメータをカスタマイズすることが可能になる(ステップ8)DBMS実行コントローラ440は、オブジェクトコードアナライザ434によって解析される同じ実行可能ファイルであり得る、DBMS実行可能444を起動する。さらに、プロファイルアナライザ436は、開発者が、クエリ評価中に実行された命令数および多様なキャッシュミスレートを調査するために、複数のプロファイル結果を比較させる施設も提供する。
これらの一連のツールは、ツールがユーザインタフェース432から独立するように、モデルビューコントローラ(MVC)[4]アーキテクチャでHIVEに統合される。事実、これらのツールのAPIは汎用定義され、このため、ECLIPSEプラグイン以外の他の種類のユーザインタフェースを開発することができる。
図26は、HIVEメインユーザインタフェースの画面例450で、コールグラフ452が右側454(RHS)に示される。具体的には、HIVEはECLIPSEにプラグインとして統合される。HIVEのユーザインタフェースは、左側456(LHS)のリソースリストおよびRHS454の表示領域からなる。具体的に、この画面例450では、postgres−stockという名前のHIVEプロジェクトは、LHSリスト456に表示される。このようなプロジェクトはそれぞれユーザによって作成される。各プロジェクトは、ディスク上に記憶される、個別のプロジェクトディレクトリに関連付けられる。
このようなプロジェクトそれぞれの下に、各々がプロジェクトのディレクトリ内部のディレクトリとして示される、4つのノードが関連付けられる。これらは、オブジェクトファイル(Object Files)、性能解析(Performance Analysis)、プロファイル結果(Profile Results)、およびソースファイル(Source Files)である。開発者は、各個別のノードを右クリックして、コンテキスト指向のポップアップメニューオプションを表示することができる。例えば、オブジェクトファイルノードのメニューオプションの1つは、オブジェクトファイルをロード(Load Object File)である。このオプションを選択すると、ファイルブラウザが表示され、開発者は、バイナリオブジェクトファイルをロードするよう選択することができる。図26が示すように、postgres.bin458という名前の実行可能ファイルがロードされた。オブジェクトファイルがロードされると、ロードされたファイルと同一のコピーが作成され、オブジェクトファイル(Object Files)ディレクトリ内部に記憶される。これは、インストールされたバイナリファイルで、直接操作されない。代わりに、バイナリオブジェクトコードのあらゆる調査は、オリジナルファイルの同一コピー上で実施されなければならない。
ソースファイル、この事例では、postgresql−8.4.2−stockソースファイルの場合、ロードされると、このディレクトリは、物理的にクローンされないことに注意されたい。そうではなく、ロードされたファイルはオリジナルのコピーで、直接修正することはできない。
性能解析ノードの下に、bee−enabled.perfファイルが示される。このファイルは、JDBC接続文字列、実験用データベース名、および結果ディレクトリ名等、DBMSを稼動し、実験を実施するための構成情報を含む。
プロファイル結果ノードは、VALGRINDによって収集されたプロファイル結果を含むことができる。これらのプロファイルは、関連の実行時コールグラフおよび命令カウントを研究するために、特定のオブジェクトファイルと組み合わせて選択することができる。この例では、既存のプロファイルを提供しない。
図26に示されるRHS454ビューは、マイクロ特化を適用する際の第1のステップに対応する。図示されるように、HIVEは、左側(LHS)456のツリービューおよびRHS454のパネルビューからなる。RHSは通常、関連情報を視覚化するためのメイン表示領域、および多様なタスクをトリガするためのボタンを含む操作パネルからなる。LHSツリーノードは、ソースコード、実行可能、プロファイル結果、および性能測定値等、解析対象項目に対応する。RHS上に示される内容は、LHS上で選択されたノードに依存する。例えば、図26では、実行可能ファイル、postgres.bin458が選択される。RHS454はすると、オブジェクトコード解析関連オプションを表示する。この特定の事例では、バイナリ解析(Analyze Binary)ボタン460が示される。このボタン460をクリックすると、HIVEは、選択されたオブジェクトファイルを解析し、静的コールグラフ452を演算する。HIVEは、コールグラフから強力に接続された構成要素を識別することによって、関数呼び出しを体系化し、これは特に、DBMS実行可能からクエリ評価内部ループを明確にするために有用である。図26に示されるように、強力に接続された構成要素は全て、強力に接続された構成要素(Strongly Connected Components)というHTMLタブ462に列挙される。タブ462内部で特定の強力に接続された構成要素を選択すると、この事例では、「component_42 Diagram」という名前の新しいタブ464内に視覚化されたコールグラフ452が作成される。
9.3 マイクロ特化を適用するためにHIVEを利用する
上記の図27を参照して、マイクロ特化が適用される8つのステップを検討する。以下の8つのステップの参照では、HIVEとマイクロ特化を適用する開発者との間の相互作用に重点を置く。
ステップ1(ブロック482)。クエリ評価ループを識別する。開発者は、LHS上のオブジェクトファイル(Object Files)ノードにDBMS実行可能バイナリをロードする。新しく追加されたオブジェクトファイルが選択されると、RHSビューは、実行可能に関する一般情報を表示する。さらに、図26に示されるように、バイナリ解析(Analyze Binary)というボタンがRHSビューの最上部に表示される。このボタンをクリックすると、オブジェクトコードアナライザが起動して、静的コールグラフを構築し、クエリ評価ループを表す強力に接続された構成要素を識別する。視覚化目的のために、図26は、クエリ評価ループを表さない、8つの関数を含む、単一の強力に接続された構成要素を示すことに注意されたい。
ステップ2(ブロック484)。実行トレースで不変量を識別する。不変量は、VALGRINDを利用することによって生成される、実行時プロファイルに依存する、動的解析で識別される。
ステップ3(ブロック486)。ソースコード内の不変量を究明する。このステップの目標は、実行時不変量を表す、ソースコード内の宣言をハイライトすることである。したがって、開発者は、LHS上のソースファイル(Source Files)ノードの下にノードとして表される、DBMSソースコードディレクトリにロードすることが必要である。不変量を識別し、ハイライトするためのオプションは、開発者がオブジェクトファイルノード(postgres.bin等)および関連のプロファイルノード(この場合、hive.trace.19051.profileファイル)を複数選択すると表示される。RHS表示は、いくつかのタブを示し、各々、不変量がハイライトされているソースコードファイルを表す。図28は、開発者によって選択され、このためRHS454のソースコードビューでハイライトされた不変量変数508を含むHIVEの画面例500を示す。この図では、11行目で定数値0x12345が代入されたint cという明らかな不変量変数508を含む、非常に単純なプログラムが示される。この変数508は次いで、15行目に示されるif文で利用される。ステップ3は中間ステップであることに注意されたい。不変量をハイライトすることは、検証目的に過ぎない。本番では、不変量を正しく識別することは、HIVEによって完全に管理されるべきである。このため、連続するステップでソースコードを明示的に注釈する必要性は存在しない。
この特定の不変量がどのように識別されるかをここで説明する。LHS上で、hive.trace.19051.profileという名前のプロファイルファイル516が選択される。このプロファイルは、VALGRINDによって生成される。具体的には、このファイルは、プログラムの実行中に各命令が実行される直前の命令のシーケンスおよび登録値を含む。この場合、プログラムは、オブジェクトファイル(Object Files)ノードの下に示されるように、simple.binと命名される。
プロファイルファイル516がLHS456上で選択されると、RHS454は、3つの列からなるインタフェースを表示する。図28に示されるように、操作(Operations)列504では、開発者は、まず、現在選択されたプロファイルファイル516が、実行可能プログラムを実行することによって生成されたことを示すために、オブジェクトファイルボタン512とのリンクをクリックすることが必要であり、これは、このボタンをクリックした後にファイルブラウザで選択される。HIVEは、実行可能ファイルに対してプロファイルデータを解析し、それらのタイプによって命令を分類する。マイクロ特化は分岐文に存在する実行時不変量を識別することを中心とすることから、CMP(比較)命令が現在サポートされる。上位命令取得(Get Top Instruction)ボタン514をクリックすると、100の最も頻繁に実行されたCMP命令(#instrボックスによって指定される)が下のリスト内部に表示される。この単純なプログラムの場合、合計で3つのCMP命令だけが識別される。リストの2番目のCMP命令を選択すると、RHS454のソースコード(中央の)列が、選択された命令を含むソースコードを表示する。さらに、選択された命令に対応する文がハイライトされる。一番右側の列には、選択されたCMP命令のオペランドの値が表示される。変数cには定数0x12345が代入され、他の比較オペランドも0x12345であるため、一番右の列に表示された両方の値が0x12345であることがわかる。最後に添付された値1000は、特定のオペランドと値の組み合わせが何回発生するかを示し、ソースコード内で、if文が1000回反復するforループ内部で実行されることを確認することができる。
識別された不変量が存在するソースファイルを視覚化するためのより直感的な代替方法は、各個別の不変量変数を個別の色に割り当て、さらに各ソースファイルに対してLHS上に注釈することで、色の帯の組み合わせが、含まれた不変量変数すべてを示す。
HIVEは通常、開発者によって不変量を識別する手動操作を必要とせず、実行時メモリアドレスによって表されるだけであることに注意されたい。そうではなく、これらの2つのステップは、動的および静的バイナリコード解析を使用して、HIVEによって全体的に自動化される。
ステップ4(ブロック488)。どのコードシーケンスがマイクロ特化されるべきかを決定する。HIVEは、不変量の参照を識別するために、データフロー解析を利用する。この解析が完了すると、HIVEは、特化される候補のコードシーケンスを示唆する。開発者は、次いで、予め選択されたコードシーケンスの境界を調整することが可能である。境界の選択は、2つの条件に依存することに注意されたい。まず、多量のビーは、特に多数が起動される場合、相当なキャッシュ圧力を導入することになるため、コードシーケンスはわずかでなければならない。第2に、特化されるコードセグメントは、関数呼び出しはビーインスタンス化中の複雑性を示すため、できる限り少ない関数呼び出しを含まなければならない。事実、特化されるコードセグメントの選択が分岐指向であることから、1つの直感的な選択条件は、分岐文によってコードセグメントの境界を決定することになる。
このステップは、フロントエンドのソースコードビュー内部で実施される。
ステップ5(ブロック490)。ビーの関連タスクをいつ実施するかを決定する。第3.3項で検討したように、異なる種類のビーに対する5つのタスクは多様なタイミングで実施される。たとえば、全てのバージョンの結合アルゴリズムおよび述語評価クエリプロトビーは、データベースが作成されるときに生成することができる。一方、関係プロトビーは、スキーマ定義時点のみに生成することができる。関係ビーは、スキーマ定義時にインスタンス化されるが、クエリビーは、クエリがDBMSによって受信された後のみにインスタンス化できる。開発者は最初にビールーチンを設計し、次に、HIVEに対し、設計された特化コードが存在すべきビーの種類、およびビールーチンが作成、生成、およびインスタンス化されるべきタイミングを記述する。上記のタスクをソースコードに記述するために、ソースコード注釈を採用できることに注意されたい。しかし、上記のタスクは、開発者が関与せず、HIVEによって自動的に実施されるべきである。
ステップ6(ブロック492)。ターゲットのソースコードは、ビールーチンを作成するために、スニペットに変換される。指定されたコードシーケンス内部から、HIVEは、示された分岐に従ってコードスニペットを自動的に抽出してもよい。開発者は、生成されたコードスニペットを手動で修正することができる。さらに、開発者は、関連およびタプルビーのため等、コードスニペットからビーソースコードを作成するために、関連関数を実装する。
ステップ7(ブロック494)。ビー起動およびサポートコードをDBMSソースに追加する。上記のステップが達成されると、現在のマイクロ特化の適用は、ビーソースコードを作成し、プロトビーを生成し、ビーをインスタンス化するために、適切な起動文をDBMSに埋め込むことによって、全てHREへの呼び出しとして、最終化されることが必要である。また、コードシーケンスが、特化されることが決定されると、開発者は、ステップ5に応じて、ビー作成、生成、およびインスタンス化を実施する関連コードをHREのビーメーカー構成要素に組み入れることが必要である。このステップは、ソースコードビューで実行されるべきである。
ステップ8(ブロック496)。確認実験性能解析を実行する。ビーが配置され、DBMSが再度コンパイルされた後、開発者は、性能を測定して研究するための実験を開始することができる。プロファイリングおよび実行時間測定の2つのオプションが提供される。開発者は、特化が適用されたDBMSおよび普通のバージョン両方でベンチマークを実行することができる。
図29は、プロファイル結果比較ビューを示す画面例530を提供する。この例では、LHS456上のハイライトされたプロファイルノードとして示される、gcl_evp_evj_07132011およびstock_07112011という2つの組のプロファイル532および534がそれぞれ選択される。デュアルプロファイルの選択によって、RHS454は、実行時間データの比較ビューを示し、開発者が比較を実施することを可能にする。この特定の画面例が表示するように、TPC−Hクエリの評価中に実行された命令の数(コンフィグ(config)ペイン540のIrチェックボックス536を選択することによって示される)が研究される。2つの組のプロファイルが比較される。1つの組のプロファイルは、普通のPostgreSQLで収集される。他の組は、ビーが有効なPostgreSQLで収集され、GCL、EVP、およびEVJビールーチンが有効であった。比較(Compare)ボタン542をクリックすると、結果テーブルは詳細統計データで更新される。
HIVEは、解析されるデータのグラフ視覚化を提供する。この特定の例では、開発者は、X軸をTPC−Hクエリ番号であるIDに、Y軸はIrに指定する。次に、開発者は、この表の選択された列を視覚化するバーチャートを生成するために、図を生成(Generate Figure)ボタンをクリックする。具体的には、開発者は、プロファイル収集アナライザ546の左下の図(Figure)タブ548をクリックする。生成されたバーチャート554は、図30に示されるように、このタブに表示される。加えて、生成されたチャートとともに統計要約558が表示される。図30に表示されるこのバーチャート554は、第8項の図12に対応することに注意されたい。
9.4 マイクロ特化を実際に適用する
この研究では、マイクロ特化を特定のDBMS、実際には特定のバージョン(PostgreSQL8.4.2)に適用することに重点が置かれた。しかし、現実には、DBMSコードレポジトリは頻繁に変化し、マイナーおよびメジャーリリースが定期的に発表される。このようなDBMSにおける頻繁な変更は、マイクロ特化に影響を与えかねない。究極的に、DBMSのソースコードが変更されると、ビーが最初に適用された不変量が変更されている可能性があるため、既存のビーが生成されることが必要な場合がある。すなわち、影響を受けるビーを更新するために、第9.1項で検討された8つのステップが再び実施されることが必要である。これらの8つのステップの複雑度から、このセクションでは、ステップ全体を再度実行することが必要かどうか、または再実行を簡素化することができるか、各ステップに関して検討が提供される。8つ全てのステップで、HIVEは、そのステップで生成された結果を記録することが必要であることに注意されたい。たとえば、第1のステップによって生成されるクエリ評価ループは、記憶されることが必要である。これらの結果は次いで、DBMSが変更された後、そのステップを高速化または実施を排除するために、使用されることが可能である。
クエリ評価ループを識別する第1のステップでは、DBMSソースコードが変更されると、変更がクエリ評価ループに影響を与えるかどうかを判定することが必要である。この解析は、クエリ評価中に起動される関数全てを提供する、以前に演算されたクエリ評価ループに依存する。このループ内部のいずれかの関数のソースコードが変更されると、このステップは再実行されることが必要で、次いで、次のステップに進むことができる。変更は、クエリ評価ループに多様な程度の影響を与える場合があることに注意されたい。たとえば、関数をループの内外に移動する等、ループのアーキテクチャを変更すると、その後の7つのステップ全てに全体的に再実行しなければならない。一方、変更が内部ループの以前の構造を無効にしない、わずかな関数に限定される場合、第2のステップは、ステップの残り全てを再実行させなくても、直ちに検討されるはずである。このステップの後、新しいクエリ評価ループが演算され、記憶される。
ソースコード内の変更が必ずしもそれまでに識別された不変量全てに影響を与えるわけではない場合があるので、不変量を識別する第2のステップは、完全に再実行される必要がなくてもよい。このステップは、収集される実行プロファイルが必要で、時間がかかりかねない。HIVEは、プロファイル結果がそれまでのプロファイルから異なるようになる、DBMSソースコードへの変更が所定の実行サンプルに影響を与える可能性があるかどうかを決定しなければならない。影響を受ける実行サンプルだけを再実行することが必要である。さらに、新しい関数をクエリ評価ループに組み込むとき、新しい不変量が発見されるかもしれない。この場合、DBMSの開発者は、新しく導入されたコードを網羅する実行サンプルを提供しなければならず、このため、新しく追加されたコードは、不変量の存在について解析することができる。
第3のステップは、ソースコード内の変数またはデータ構造に対する実行プロファイルで検出された不変量を識別する。DBMSソースコードへの変更によって影響を受ける不変量、ならびに出現する新しい不変量だけが、この第3のステップで識別される必要がある。
同様に、第4のステップでは、コードシーケンスがマイクロ特化に選択された場合、第3のステップで再識別された変数およびデータ構造を参照するコードシーケンスだけが再検討される必要がある。
汎用ソースコードをビーコードスニペットに変換し、ビールーチンを設計し、DBMSにビー起動を追加する、多様なビー関連タスクの割り当てを実施する第5〜7のステップはそれぞれ、影響を受ける不変量に関してのみ部分的に再実行される。
適用されたビーの性能を評価する、第8のステップは、特に他の既存のビーに対する影響について、新しく修正されたビーの性能利点を研究するために、概して再実行されなければならない。
一般に、HIVEは、各ステップに必要な作業を示し、予測するために、ユーザインタフェースを含まなければならない。特に、開発者が時間および予算制約にしたがって選択を行うことができるように、ステップ全体を再実行することに関する概算作業および時間と、ステップを部分的に再実行するための作業および時間との間の比較が所望される。
第10項:実行時値を可能にするようにテンプレート機構を拡張する
実行時コード特化技法を利用することによって、DBMS性能を向上する手法として、マイクロ特化が導入された。具体的には、実行時に実行可能コードを選択し、生成する際に重要なステップである、ビーのインスタンス化は、(C++またはJava(登録商標)のジェネリックスで使用される)テンプレートによって導入されるインスタンス化機構に非常に類似する。テンプレートインスタンス化機構によって、入力引数の型に特異的な関数等、コードを動的に選択する機能が、生成された実行可能コードに導入されることを可能にする。しかし、従来のテンプレートは、コンパイル時にインスタンス化をトリガする。このため、究極的にマイクロ特化によって実行時に利用される中心技法である、変数値ベースのインスタンス化は、従来のテンプレートによって直接影響され得ない。
10.1 動的テンプレート
実行時に値ベースのコード特化を適用可能にすることができる、テンプレート機構の拡張を検討する。第10.2項において、動的テンプレートをマイクロ特化でどのように使用できるかについての検討を提供する。PostgreSQLに実装されたExecEvalScalarVar関数は、実行時コード特化の状況で、テンプレート機構をどのように強化することができるかについて検討するために、例として利用され、リスト6に示される。
リスト16によって示されるように、従来のCで表現されるExecEvalScalarVar関数は、3つの分岐を含む。この関数は、PostgreSQLのソースコードから直接得られた。9行目の分岐文(switch)は、variable−>varno変数に記憶される、入力タプルの入力源を検証し、これに応じてどの分岐を実行する化を決定する。
ExecEvalScalarVar関数は、関数テンプレートに変換される。動的テンプレートベースの特化を示すために、対応するテンプレート定義およびインスタンス化の例をリスト17に示す。この関数テンプレート定義に示されるように、第1行目に示されるint varnoという、テンプレート引数が導入される。コードは、実行する特定の分岐を決定するために、このテンプレート引数を使用する。3つの個別のコード分岐だけが存在することから、ExecEvalScalarVar関数は、3つのバージョンにコンパイルすることができ、各々特定の分岐に具体的に対応する。
リスト18は、ExecEvalScalarVar関数の特化されたソースコードを示し、varnoテンプレート変数がINNER(分岐)値に割り当てられる。リスト17に示される25行目の関数呼び出し文が、テンプレート変数の値として、INNERで起動されると、リスト18に示される特化された関数が起動されることに注意されたい。
前の作業で、varnoの値は、コンパイル時定数であることが必要である。しかしながら、マイクロ特化内部では、変数は、テンプレートパラメータとして指定されることが可能である。動的テンプレートベースの特化は、実行時値によって特化されるルーチンの起動を許可するために、C++言語等のテンプレートを汎用化する。
実行時、リスト17の25行目に示されるように、関数起動文は、関数テンプレートパラメータとして、入力タプルの入力源を表す、varnoという名前の変数を提供する。第6.2項に記載されるように、varno変数の値、この場合、テンプレートパラメータは、ターゲットプロトビーバージョンにマッピングされる。このため、ビーの正しい分岐(バージョン)を起動することができる。
この動的テンプレートベース特化機構を採用することによって、実行時コード特化は、良好に構造化された様式で実現することができる。特化ソースコードの作成、オブジェクトコードの生成、起動、および他の操作は、コンパイラによって完全に管理される。
さらに、テンプレート機構のこのような汎用化は、最適化機会を導入する。例えば、コンパイラは、静的、動的、またはハイブリッド様式で、関数等、コードのストレッチに複数バージョンを生成するように選択することができる。
わずかなコード分岐だけが存在する場合、コンパイラは、コンパイル時に可能なバージョン全てを生成することができる。静的に生成されたコードは、次いで、実行時に直接起動することができる。しかし、多数の分岐が存在する場合、バージョン全てを生成すると、得られるコードを管理する空間の複雑度を顕著に増加する可能性がある。リスト17の第25行目の関数呼び出しは、varnoテンプレートパラメータの値から、テンプレート関数をコンパイルするためにコンパイラを動的に起動することができる。
このため、コンパイラは、特定の分岐が実行される場合、実行時にのみコードを動的にコンパイルすることを決定することができる。ハイブリッド手法では、プロファイルガイド解析は、所定の頻繁に実行されるコード分岐を識別することを補助することができる。コンパイラは、コンパイル時に頻繁に実行される分岐だけのオブジェクトコードを静的に生成することができる。このため、他の分岐は、実行時に動的にコンパイルされなければならない。ここで検討される動的なコード生成、起動、および必要な管理は、マイクロ特化手法に類似する実装を使用して実現することができる。具体的には、プロトビー管理の実装は、この動的テンプレート機構を実現するために、ここで直接利用することができる。唯一の違いは、変数ベースのテンプレートパラメータを示すために、動的テンプレートには構文注釈が必要である点である。
10.2 マイクロ特化に動的テンプレートを採用する
マイクロ特化の適用中の1つのタスクは、汎用ソースコードを特化コードスニペットに変換することである。変換は現在、事例ベースで手動で実行される。すなわち、それぞれの種類のビーに対して、ビーソースコードを作成するために、異なる手法が利用される。事例特異的手法は、それぞれの種類のビーが適切に作成できることを保証するが、この手法の複雑性は、このタスクの自動化を困難にする。したがって、ビー作成および生成を自動化する際に関与する高度化を活用するための技法が採用されてもよい。
第10.1項では、HREを利用することによって、テンプレート機構をどのように拡大できるかについて検討し、その結果、動的テンプレートとなった。ここでは、マイクロ特化を適用する際、特に、ビー作成および生成のステップにおいて、静的テンプレートの正確さを継承する、動的テンプレートをプログラム言語構成概念としてどのように適用することができるかについて検討する。
リスト17に示される例が示すように、動的テンプレートは、クエリビーの作成および生成タスクを実施するために利用することができる。具体的には、汎用コードセグメント内部の究極的には個別のコード分岐である、クエリプロトビーが、DBMS開発と共に設計および作成されることから、動的テンプレートは、汎用コードを特化クエリビーコードに手動で変換することなく、クエリビーを有効にするためにDBMSソースコードに組み入れることができる。動的テンプレートのコンパイル中、コンパイラは、動的テンプレートの全てのバージョンを生成し、各バージョンがプロトビーである。さらに、ホットスワップを実施することができるクエリビーに関して、このようなビーを作成および生成するために、動的テンプレートを採用することもできる。コンパイラは、実行のフローを制御する変数の値のシーケンスによって要求されるビーのバージョン全てを決定し、このため、それに応じてビーコード内のホットスワッピング文でビーを生成することができる。クエリビーはDBMS開発時に作成され、DBMSコンパイル中に生成されることから、これらのタスクを実現するために動的テンプレートを適用することができ、テンプレートの作成および生成は、クエリビーのタスクに整合される。
事実、ホットスワッピングを必要としない通常のクエリビーの場合、ビー起動によってインライン機能を利用することができ、ビーコードがコンパイル時に呼び出し元コードに埋め込むことができる。インライン機構の欠点は、リスト16に例示したようなコード分岐は全て、ビーにコンパイルされ、呼び出し元に組み込まれることである。これによって、生成されるDBMS実行可能コードのサイズが増加する。さらに、分岐条件は全て、特定の分岐だけが実際に実行される場合であってもまだ評価される。
関係ビーおよびタプルビーの場合、それらの作成および生成がDBMSコンパイルの後である、DBMS実行時であるという事実に起因して、どちらの種類のビーでもこれらのタスクを実施するために動的テンプレートを採用することはできない。すなわち、動的テンプレートのコード生成は、コンパイル時に要求される。しかしながら、関連およびタプルビーのコード生成は、実行時に要求される。
第11項:他の実施形態
ビーを並列実行する
マイクロ特化の重要な特徴は、特化実行可能コード、すなわち、ビーの実行時起動である。ビーは通常、相互に独立してタスクを実行する。例えば、特定の関係のタプルビーは全て、タプルから値を抽出し、これらのタプルで述語を評価するとき、独立的に並列に実行することができる。したがって、1度に1つのタプルクエリ評価モデルを採用する代わりに、多数のタプルをフェッチすることによって、属性値抽出および述語評価を並列に実行し、このような演算の効率を顕著に向上するために1度に全ての関連タプルビーを起動することができる。
マイクロ特化の利点は、DBMS内部の非常に下位で適用されることである。従来のDBMS内部でクエリ評価全体を並列に実行することは困難であるが、並列性は、マイクロ特化の柔軟性および微細な適用粒度によって達成することができる。
最新のCPUは通常、独立的に動作することができるマルチコアが装備されている。このようなアーキテクチャ上の発展は、ビーの並列実行を利用するマイクロ特化の適用に対する機会を提供する。さらに、現在のGPUは、非常に積極的な並列コード実行を促進することができる。ビーが小さいことから、GPUは、多数のビーを並列で実行させる理想的なプラットフォームを提供してもよい。
ビー効果をクエリオプティマイザのコストモデルに組み入れる
DBMSは、効率的なクエリ評価のために最適なクエリ評価計画を生成するためにクエリオプティマイザを採用する。クエリオプティマイザは通常、特定の計画の全体的なコストを概算することができるコストモデルに依存する。したがって、多様な候補計画の間でこのようなコストを比較することによって、実行するための最適計画を選択することができる。
コストモデルは通常、シーケンシャルスキャン、ハッシュ演算、またはソート操作を実施するオーバーヘッド等、所定の既知の定数に基づく。
ビーの利用が影響を受ける計画演算子の効率性に直接影響することから、ビーが有効なDBMSのコストモデルは、ビーの存在に合わせて調整することができる。いくつかの場合には、古い定数値を、関連ビーの影響を考慮する新しい値に置換する代わりに、ビーはさらなるコンパイルを導入されてもよい。例えば、ホットスワップするビーは、特定のビーが実行のためにスワップされるたびに、演算子に対して異なる性能影響を有する。ビーのこの動的な性質に対処するために、ビーを用いてクエリ計画生成を向上する際の機会は、ホットスワップが関与するクエリ評価中に、各ビーが何回実行されるか等、微細粒度の解析を処理するためにコストモデル内部で利用されてもよい。
マイクロ特化を積極的に活用する
タプルビーは、関係内の属性値を特化することによって作成される。究極的に、列全体が特化された関係から削除される。いくつかの実施形態において、タプルビーは、特化属性が制限値ドメインを有する場合にのみ利用される。より積極的にマイクロ特化を適用するために、この制限を緩和することができ、これらの属性のうちの一部に関するタプルビーを可能にすることができる。具体的には、属性の値の分散を利用することができ、高い発生率を有する値に関してのみ特化することができる。
加えて、修正およびページビー等、他の種類のビーをHREに組み入れてもよい。このような拡張は、HRE APIに追加の関数を導入してもよい。
さらに、マイクロ特化は、他の種類のDBMSに適用されてもよい。例えば、MonetDBおよびVoltDBは、アーキテクチャの点で、従来の関係DBMSとは非常に異なる。
バイナリコードからビーを生成する
現在、マイクロ特化は、ビーソースコードを作成するために、DBMSのソースコードに依存する。いくつかの実装において、DBMSのオブジェクトコードを直接修正することによって、ビーを生成し、ビーの起動を有効にするためにDBMSのバイナリ実行可能コードを直接利用することが有利な場合がある。バイナリコードからビーを生成する明らかな利点は、ソースコードの高価なコンパイルを回避できること、およびプロトビーの可能性があるバージョン全てをコンパイルすることを回避できることであり、コンパイルのコスト、および多数のバージョンのプロトビーを記憶するために必要な空間要件を削減する。
HIVEを用いてマイクロ特化の適用を自動化する
いくつかの実施形態において、HIVEツールセットによって、DBMS開発者は、DBMSにおけるビーを設計し、実装するために、必要な静的コード解析、動的なプロファイル収集および解析、および実行時性能解析を実施することが可能になる。いくつかの実装において、このような半自動化プロセスは、第9項に記載の8つのステップによって構造化され、ユーザが一切関与することなく、完全に自動化することができる。このような自動化は、多数の種類のDBMS内部にマイクロ特化を積極的に適用するために有用である。完全に自動化されたマイクロ特化は、導入されるビーの解析および調整に正確性も提供する。最後に、第5項および6項に検討したように、ビーを作成、生成、およびインスタンス化するコストは、特にこれらのタスクがDBMS実行時に必要である場合、マイクロ特化適用時に考慮することが必要である。マイクロ特化が所与の所定のシナリオで利点があるかどうかを自動的に決定することができるコストモデルをHIVEが導入してもよい。
引用参考文献(本明細書に参照により組み入れる)
[1]J.Bonwick.The Slab Allocator:An Object−Caching Kernel Memory Allocator.In Proceedings Usenix Technical Conference,pages 87−98,June 1994.
[2]B.Calder,P.Feller,and A.Eustace.Value
Profiling and Optimization.Journal of Instruction Level Parallelism,vol.1,March
1999.
[3]Valgrind Developers.Callgrind:A Call−Graph Generating Cache and Branch Prediction Profiler.
[4]R.Eckstein.Java SE Application Design
with MVC.
[5]R.Elmasri and S.Navathe.Fundamentals of Database Systems.Addison Wesley Publishing Company,sixth edition,April 2010.
[6]Linux Foundation.ELF and ABI standards.
[7]Inc.Free Software Foundation.Optimization Options−Using the GNU Compiler Collection (GCC).
[8]K.Krikellas,S.Viglas,and M.Cintra.Generating Code for Holistic Query Evaluation.In Proceedings of the IEEE International Conference on Data Engineering(ICDE),pages 613−624,2010.
[9]D.Lussier.BenchmarkSQL.
[10]R.Muth,S.Watterson,and S.K.Debray.Code Specialization Based on Value Profiles.In Proceedings International Static Analysis Symposium(SAS),pages 340−359,June
2000.
[11]PostgresSQL Global Development Group.PostgresSQL:Documentation 8.4:Resource Consumption.
[12]TPC.TPC Transaction Processing Performance Council −TPC−C.
[13]TPC.TPC Transaction Processing Performance Council−TPC−H.
本明細書に参照および/または本明細書のデータシートに挙げられた上記の米国特許、米国特許出願公開、米国特許出願、外国特許、外国特許出願、および特許以外の文献は、それらの全体を参照によって本明細書に組み入れる。
前述の記載の実施形態は、異なる他の構成要素内部に含まれた、または異なる他の構成要素と接続された異なる構成要素を表す。そのような表されたアーキテクチャは例示に過ぎず、事実、同じ機能を達成する、多数の他のアーキテクチャが実装できることを理解されたい。概念的には、同じ機能を達成するための構成要素の配置は、所望の機能が達成されるように、効果的に「関連付けられる」。したがって、特定の機能を達成するために組み合わされた本明細書の任意の2つの構成要素は、アーキテクチャまたは中間構成要素に無関係に、所望の機能が達成されるように相互に「関連付けられる」と考えることができる。同様に、そのように関連付けられた任意の2つの構成要素は、所望の機能を達成するために相互に「動作可能に接続」、または「動作可能に連結」すると考えることもできる。
本発明の特定の実施形態が示され、記載されるが、当業者には、本明細書の開示に基づいて、本発明およびそのより広義の態様から逸脱することなく変更および変形を行うことができること、したがって、添付の請求項は、そのような変更および変形全てが本発明の真の精神および範囲であるとして、それらの範囲内に及ぶことが明らかであろう。さらに、本発明は添付の請求項のみによって定義されることを理解されたい。一般的に、本明細書、および特に添付の請求項(すなわち、添付の請求項の本文)に使用される用語は、一般的に、「限定されない」用語(すなわち、「含んでいる」という用語は、「含んでいるがそれに限定されない」として解釈されるべきであり、「有する」という用語は、「少なくとも有する」として解釈されるべきであり、「含む」という用語は、「含むがそれに限定されない」として解釈されるべきである等)を意図することが当業者によって理解されるであろう。さらに、特定の数の導入された請求項の記述が意図される場合、そのような意図は請求項内で明示的に記述され、そのような記述が欠落する場合、そのような意図が全く存在しないことが当業者によって理解されるであろう。例えば、理解の支援として、以下の添付の請求項は、請求項の記述を導入するために、「少なくとも1つ」および「1つ以上」という導入語句の使用を含む場合がある。しかしながら、そのような語句の使用は、同じ請求項が「1つ以上」または「少なくとも1つ」という導入語句、および「1つ」(「a」または「an」)等の不定冠詞を含む場合であっても、「1つ」(「a」または「an」)という不定冠詞による請求項の記述の導入は、そのように導入された請求項の記述を含む任意の請求項が、1つだけのそのような記述を含む発明に限定することを暗示すると、解釈されてはならず(「a」および/または「an」は典型的に、「少なくとも1つ」または「1つ以上」と解釈される)、同じことは、請求項の記述を導入するために使用される定冠詞の使用にも言える。加えて、特定の数の導入された請求項の記載が明示的に記載される場合であっても、当業者は、そのような記載は典型的に、少なくとも記述された数を意味すると解釈されるべきである(すなわち、他の修飾語なく、「2つの記述」のみの記述は、典型的に、少なくとも2つの記述、または2つ以上の記述を意味する)。
したがって、本発明は、添付の請求項によることを除いて、限定されない。