毎回多数の命令を解釈しないようにするにはどうすればよいですか?

はじめに: ベクトル化エンジンにより、PolarDB-X式の計算のパフォーマンスが大幅に向上しました。

著者:ジュンカイ




前書き


PolarDB-Xは、Alibabaが自己開発したクラウドネイティブの分散データベースであり、コンピューティングノードが多数の式コンピューティングタスクを担当するコンピューティングストレージ分離アーキテクチャを使用しています。これらの式の計算にはSQL実行のすべての側面が含まれ、パフォーマンスに重要な影響を及ぼします。このため、PolarDB-Xはベクトル化された実行エンジンを導入し、式の計算で数十倍のパフォーマンス向上をもたらします。


従来のデータベースエグゼキュータの欠点


最新のデータベースシステムの実行エンジンは、ほとんどの場合、一度に1行のデータ(Tuple-at-a-time)を計算する処理方法を採用しており、複雑な式の構造に適応するには、実行時にデータ型を分析および判断する必要があります。これを「スカラー式」と呼びます。この方法は実装が簡単で構造が​​明確ですが、処理するデータの量が増えると重大な欠陥があります。


複雑な式の構造に適応するために、式の計算には多くの場合、多数の命令の導入が必要です。行の実行では、単一のデータを処理するには、演算子ツリーで命令の解釈を再度実行する必要があり、その結果、多数の命令が発生します。命令の解釈オーバーヘッド。論文「MonetDB / X100:ハイパーパイプラインクエリ実行」の統計によると、MySQLがTPC-HテストセットのQuery1を実行すると、実行時間の90%が命令の解釈に費やされます。


さらに、元のVolcano構造設計では、オペレーターの内部ロジックは分岐予測を回避しませんでした。間違った分岐予測では、CPUが現在のパイプラインを終了し、ELSEステートメントの命令をリロードする必要があります。このプロセスをパイプラインフラッシュまたはパイプラインブレークと呼びます。頻繁な分岐予測エラーは、データベースの実行パフォーマンスに深刻な影響を及ぼします。


ベクトル化された実行システム


データベースのベクトル化実行システムは、「MonetDB / X100:ハイパーパイプラインクエリ実行」という論文で最初に提案されました。主なポイントは次のとおりです。

  1. 一度にベクトルを実行するモードが採用されています。つまり、ベクトル(ベクトル)がデータ編成単位として使用されます。
  2. ベクトル化実行システム全体を構築するためのベクトル化演算子の基本単位として、ベクトル化プリミティブを使用します。プリミティブでの分岐予測は避けてください。
  3. コード生成テクノロジーを使用して、静的型付けによって引き起こされるコード爆発の問題を解決します。


ベクトル化エンジンにより、PolarDB-X式の計算のパフォーマンスが大幅に向上しました。次の図で、横軸はベクトルサイズ、縦軸はスループットです。さまざまなスカラー式とベクトル化された式のパフォーマンステストの比較結果は次のとおりです。


1.jpeg


症例式性能試験の比較結果は以下のとおりです。


2.jpeg




全体的なプロセス


PolarDB-Xでは、ベクトル化された式の実行は次の段階に分けられます。

  1. ユーザーSQLが解析された後、式の型情報を導出および変更するためにバリデーターで検証されます。この段階では、ベクトル化操作の正確で静的な型情報が提供されます。
  2. オプティマイザは実行プランを作成した後、式ツリーをバインドし、対応するベクトル化されたプリミティブをインスタンス化し、ランタイムメモリ割り当て用のベクトル添え字を割り当てる必要があります。
  3. 実行フェーズでは、Volcanoスタイルの構造に従って、ベクトル化されたプリミティブがトップダウントリガーで実行され、ベクトルがランタイムデータ構造として使用されます。


3.png



ランタイム構造


データ構造


PolarDB-Xベクトル化実行システムでは、次のデータ構造を使用してデータを格納します。


4.png


ベクトル化された式が実行されると、すべてのデータがバッチデータ構造に格納されます。バッチは、多くのベクトルと選択配列で構成されます。その中で、ベクトルには特定のタイプ(値)を格納する値のリストとnull値の位置を識別するnull配列が含まれており、それらはすべてメモリに継続的に格納されます。null配列のビットビットは、0と1を使用して、値リストの特定の位置がnull値であるかどうかを区別します。


vector(type、index)を使用して、バッチ内のベクトルを識別できます。各ベクトルには、バッチ内のベクトルの順序を示す特定のインデックス位置(index)があり、ベクトルのタイプを指定するタイプ情報(type)があります。ベクトル化された式を評価する前に、式ツリー全体を走査し、各式のオペランドと戻り値に従って添え字の位置を割り当て、最後に添え字の位置に従ってベクトルにメモリを割り当てる必要があります。


マテリアライズの遅延


選択配列の設計は、遅延マテリアライゼーションのアイデアを反映しています。「列指向DBMSのマテリアライゼーション戦略」を参照してください。いわゆる遅延マテリアライゼーションは、メモリアクセスによって引き起こされるオーバーヘッドを削減するために、マテリアライゼーション(マトリックス化)のプロセスを可能な限り延期することです。式の計算を実行する場合、多くの場合、データの一部がFilter式でフィルター処理され、フィルター処理されたデータが評価されます。各フィルターは、バッチ内のすべてのベクトルに影響します。例として上の図のバッチを取り上げます。vector(int、0)のデータの90%がフィルター条件を満たしていると仮定して、0番目のベクトルにvector(int、0)!= 1のフィルター条件を設定します。 (選択率の選択性= 0.9)、次に、バッチ内のすべてのベクトルのデータの90%を別のメモリブロックに再実体化する必要があります。また、フィルター条件を満たす場所のみを記録し、それらを選択配列に格納すると、この具体化プロセスを回避できます。同様に、後続のすべてのベクトル化評価プロセスでこの選択配列を参照する必要があります。


ベクトル化されたプリミティブ


ベクトル化されたプリミティブは、ベクトル化された実行システムの実行ユニットであり、実行中の自由度を最大限に制限します。プリミティブは、コンテキスト情報に注意を払う必要はなく、実行時に型分析や関数呼び出しを実行する必要もありません。着信ベクトルに注意を払うだけです。これはタイプ固有です。つまり、プリミティブのクラスは特定のタイプのみを処理できます。


ベクトル化プリミティブの本体は、Tight-Loopのコード構造です。ループ本体では、値と計算のみが必要であり、分岐計算と関数呼び出しは必要ありません。単純なベクトル化されたプリミティブ構造は次のとおりです。


map_plus_double_col_double_col(int n、double * __strict__
 res、
 double * __strict__ vector1、double * __strict__ vector2、int * __strict__
 selection)
 {
   if(selection){
         for(int j = 0; j <n; j ++){
             int i = selection [ j];
            res [i] = vector1 [i] + vector2 [i];
        } 
   } else {
         for(int i = 0; i <n; i ++)
           res [i] = vector1 [i] + vector2 [i];
  }   
 }

注:*読むには左右にスワイプしてください


次の図に示すように、計算プロセスでは、選択配列を使用して、ベクトルの値を徐々に取得、操作、および格納します。


5.png


ベクトル化プリミティブには、次の利点があります。

  1. タイプ固有およびタイトループの構造により、命令解釈のオーバーヘッドが大幅に削減されます。
  2. 分岐予測の失敗やCPUパイプラインでの仮想関数呼び出しの干渉を回避し、ループパイプラインの最適化にも役立ちます[論文引用]
  3. ベクターからデータにアクセスすると、キャッシュのプリフェッチがトリガーされ、キャッシュミスによって発生するオーバーヘッドが削減されます。


さまざまなスカラー式に対応するプリミティブ実装を提供し、スカラーからベクトル化への変換を完了します。たとえば、加算演算plus(Object、Object)は、plus(double、double)、plus(long、long)などのさまざまなオペランドタイプのプリミティブを生成します。


短絡評価


ベクトル化プリミティブに基づいて、分岐演算(制御フローとも呼ばれます)の短絡計算をさらに最適化して、式の計算のパフォーマンスを向上させることができます。


たとえば、case式は、nのwhen式、n-1のthen式、および1のelse式で構成されます。式について


             a> 1の場合はa * 2 、b> 1の場合はb * 2、            それ以外の場合はa * bの
場合を選択します


論理セマンティクスは次のとおりです。

  • a> 1を満たすベクトル位置の場合、a * 2を計算します。
  • a <= 1およびb> 1を満たすベクトル位置の場合、b * 2を計算します。
  • a <= 1およびb <= 1を満たすベクトル位置の場合、a * bを計算します。
  • すべての位置の値を組み合わせて、新しいベクトルと出力を形成します。


次のツリー構造があります。


6.png


スカラー式は火山構造に従って配置され、統一されたnext()インターフェイスを提供するため、case式はすべてのサブ式a> 1、a * 2、b> 1、b * 2、およびa *を実行する必要があります。すべての結果をまとめ、最後にケースセマンティック処理を実行します。この実行方法は、when式の処理結果に応じて計算処理を時間内に終了することはできませんが、すべての部分式を無差別に実行します。


ベクトル化されたエグゼキュータを導入した後、この問題を最適化するための短絡評価を設計できます。各部分式に適切な選択配列を指定して、列内の適切な位置がベクトル操作に対して正しく選択されるようにする必要があります。条件式がの場合にi番目に受け入れられる選択要素セットS.pngであり、出力選択要素セットがR.png、つまりi番目の条件式に受け入れられる選択要素セットであるとします。次に、元の選択配列の添え字のセットでDENG.pngあるが満たさ S.pngれます。選択要素のセットを取得するステップを減算選択と呼び、ケース操作のプロセス全体を次の図に示します。


7.png



総括する


PolarDB-Xベクトル化エンジンは、プリミティブを使用して式を作成し、実行時データ構造としてベクトルを使用します。各プリミティブは特定のタイプのみを提供するため、命令の総数が削減されます。プリミティブのタイトループ構造は、CPUパイプラインに非常に適しているだけでなく、CPUがデータのプリフェッチを実行して分岐予測を回避できるようにします。さらに、遅延実体化や短絡評価などのいくつかの最適化により、式評価のパフォーマンスがさらに向上します。


ただし、ユーザーSQLとベクトル化された実行の間には大きなギャップがあります。次の重要な問題を解決する必要があります。


1.式の入力タイプと出力タイプを決定し、SQLの式に適切なプリミティブを割り当てる方法は?2.各プリミティブは、入力と出力に異なるベクトルを使用する必要があります。ベクトルをプリミティブに正しく割り当てるにはどうすればよいですか?3.各プリミティブは特定のタイプのみを提供するため、さまざまなデータ型に適応するために、式に多数の異なるプリミティブを装備する必要があります。プリミティブの数の急増にどのように対処しますか?


次の記事では、上記の問題の解決策を詳しく紹介します。




【関連資料】

HTAP用PolarDB-Xハイブリッドアクチュエータ

PolarDB-XHTAP用CBOオプティマイザー

BMW 3シリーズや5シリーズなど:PolarDB-XとDRDSは密接に関連しています

PolarDB-Xストレージアーキテクチャ「Paxosに基づくベストプロダクションプラクティス」

PolarDB-Xプライベートプロトコル:クラスターのパフォーマンスと安定性を向上させます

技術的解釈| PolarDB-X分散トランザクションの実装

技術的解釈| PolarDB-X非常に一貫性のある分散トランザクション

PolarDB-Xコンセンサスコンセンサスプロトコル(X-Paxos)

元のリンク:https //developer.aliyun.com/article/782166?

著作権表示: この記事内容は、Alibaba Cloudの実名登録ユーザーによって自発的に提供されています。著作権は元の作成者に帰属します。AlibabaCloud開発者コミュニティはその著作権を所有せず、対応する法的責任を負いません。具体的なルールについては、「Alibaba Cloud DeveloperCommunityユーザーサービス契約」および「AlibabaCloudDeveloperCommunity知的財産保護ガイドライン」を参照してください。このコミュニティで盗用の疑いがある場合は、侵害の申し立てフォームに記入して報告してください。確認されると、コミュニティは侵害の疑いのあるコンテンツをすぐに削除します。

おすすめ

転載: blog.csdn.net/alitech2017/article/details/114937527