注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
Velox ↗ を使用してネイティブアクセラレーションを有効にすることで、Spark のパフォーマンスを向上させることができます。
ネイティブアクセラレーションは、バッチジョブのパフォーマンスを向上させるために低レベルのハードウェア最適化を利用する技術です。これらのパフォーマンス向上は、Java仮想マシン (JVM) 言語からC++のようなネイティブ言語にコンピュートを移行することによって達成されます。これらの言語は機械語にコンパイルされ、マシンのハードウェア上で直接実行されます。プラットフォーム固有の機能を使用することで、ネイティブアクセラレーションは大規模なデータワークロードを処理するために必要な時間を大幅に短縮し、ジョブの実行を高速化し、リソースの利用効率を改善することを目指しています。
ネイティブアクセラレーションは、Pythonトランスフォーム と Pipeline Builder で利用可能です。
ネイティブアクセラレーションされたビルドの基本的な分析は、Spark Details ページで行うことができます。Query Plan タブの下で、Physical Plan を選択すると、次のようなものが表示されます。
// 物理プランの構造を示しています
== Physical Plan ==
AdaptiveSparkPlan // 適応型のSparkプラン
+- == Final Plan ==
Execute InsertIntoHadoopFsRelationCommand // Hadoopファイルシステムへの挿入コマンドを実行
+- WriteFiles // ファイル書き込み操作
+- CollectMetrics // メトリクスの収集
+- VeloxColumnarToRowExec // Veloxのカラム形式から行形式への変換
+- ^ ProjectExecTransformer // プロジェクト実行トランスフォーマー
+- ^ InputIteratorTransformer // 入力イテレーターのトランスフォーマー
+- ^ InputAdapter // 入力アダプター
+- ^ RowToVeloxColumnar // 行形式からVeloxカラム形式への変換
+- ^ HashAggregate // ハッシュ集計操作
+- ^ VeloxColumnarToRowExec // Veloxのカラム形式から行形式への変換
+- ^ AQEShuffleRead // AQE(Adaptive Query Execution)のシャッフル読み取り
+- ^ ShuffleQueryStage // シャッフルクエリステージ
+- ColumnarExchange // カラム形式のデータ交換
+- ^ ProjectExecTransformer // プロジェクト実行トランスフォーマー
+- ^ RowToVeloxColumnar // 行形式からVeloxカラム形式への変換
+- * ColumnarToRow // カラム形式から行形式への変換
+- BatchScan parquet // Parquet形式のバッチスキャン
このコードは、Spark SQLの物理プランを示しています。各ノードはデータ処理のステップを表しており、データ形式の変換やシャッフル操作などが含まれています。
通常のSparkクエリプランに似ていますが、いくつかの重要な違いがあります。ProjectExec
ノードの代わりに、ProjectExecTransformer
があります。これにより、操作はVeloxクエリエンジンでネイティブに実行されることを意味します。クエリプランのすべてのオフロードされたノードは、ツリー内で^
記号でマークされます。ネイティブ実行のブロックは、RowToVeloxColumnar
とVeloxColumnarToRowExec
に挟まれています。これらのノードは、SparkデータセットをArrow DataFramesに変換し、逆も行います。このシリアル化/デシリアル化にはかなりのコストがかかります。
ネイティブアクセラレーションのパフォーマンスが低いことを示す一般的なパターンは2つあります:
^
記号で示されるように、ネイティブに実行されるノードの割合が小さい。RowToVeloxColumnar
とVeloxColumnarToRowExec
ノードがあり、高いシリアル化オーバーヘッドをもたらす。この分析は、パフォーマンスが期待どおりでない場合に役立ちます。パイプラインに小さな変更を加えることで、オフロードされる計算量に大きな影響を与えることができます。チェックポイントのような機能を使用して、ネイティブにすべて実行できるビルドのチャンクを手動でグループ化することができます。
Foundryのネイティブアクセラレーションの実装は、Apache Gluten ↗プロジェクトに基づいています。Foundryのネイティブアクセラレーションは、Velox ↗クエリエンジンを利用して、実行時にSparkジョブを高速化します。VeloxはC++で書かれており、データベースアクセラレーションを念頭に置いて設計されています ↗。Arrow DataFrames上でオペレーターレベルの操作を実行するための開発者APIを提供しています。Glutenは、SparkランタイムとVeloxを結びつけるために必要な「グルー」を提供します。
このセットアップでは、パイプラインは最初に通常のビルド(ネイティブアクセラレーションなし)と同様にSparkクエリプランを生成します。その後、追加の最適化ルールがプランに適用され、クエリの一部がVeloxで実行できるかどうかが識別されます。この決定は、Veloxに同等の実装があるかどうか、Glutenに実装のマッピングが存在するかどうかに基づいています。クエリはオペレーターレベルでオフロードできます。これは、SELECT
、FILTER
、JOIN
のようなSQLステートメントに大まかに対応します。オフロード可能なクエリプランの部分は、この段階でマークされます。
プランニングステップが完了すると、クエリは通常のSparkエンジンを通じて実行されます。これは、すべてのタスクスケジューリング、エグゼキュータオーケストレーション、およびライフサイクル管理が通常どおり進行することを意味します。違いは、エグゼキュータがネイティブ実行用にマークされたクエリプランの部分に到達したときに生じます。この場合、Sparkのデフォルトの実装を呼び出す代わりに、Veloxの実装が呼び出されます。
このアーキテクチャは、すべての計算がVeloxで行えないクエリをサポートするため、特に有利です。これは、オフロードの決定がプラン全体ではなくオペレーターレベルで行われるためです。サポートされるオペレーターの数は絶えず増加していますが、UDFのようなユーザー作成のコードは、ネイティブ実装が存在しないため、オフロードされることはありません。
SparkはScalaで書かれたJVM言語であり、そのパフォーマンスを向上させるためにコード生成 ↗などの多くの最適化を含んでいます。さらに、JVM自体には、できるだけ多くのプラットフォーム固有の機能を活用することを目的としたC2コンパイラ ↗などの最適化が含まれています。しかし、C++のようなネイティブ言語は、3つの基本的な理由で引き続き優れたパフォーマンスを提供します。
Foundryでネイティブアクセラレーションを使用してSparkを実行するには、通常のバッチパイプラインとは少し異なる設定が必要です。Sparkは、いくつかの操作をオフヒープメモリ ↗で実行することをサポートしています。オフヒープメモリはJVMによって管理されないメモリであり、GCのオーバーヘッドを排除し、パフォーマンスを向上させます。デフォルトでは、Foundryではオフヒープメモリを有効にしていません。これを有効にすると、パイプラインの追加のメンテナンスコストが発生する可能性があるためです。ネイティブアクセラレーションにはオフヒープメモリが必要です。Veloxによって変更されたDataFrameは、ネイティブプロセスによってアクセス可能であるためにオフヒープでなければなりません。Foundryは、Veloxのデータトランスフォーメーションを除くすべてのために十分なオンヒープメモリを必要とします(たとえば、オーケストレーション、スケジューリング、およびビルド管理コードは依然としてJVMで実行されます)が、理想的にはほとんどの作業がオフヒープで実行されることになります。ネイティブアクセラレーションを使用するようにパイプラインを設定すると、オンヒープメモリとオフヒープメモリのバランスを取る上で追加のメンテナンスコストが発生します。