物理的 AI アプリケーション向け NVIDIA GPU における BEV ポーリングの高速化
NVIDIA は物理的 AI アプリケーション向けに BEV ポーリング処理を GPU で加速する手法を発表し、自動運転やロボティクスのリアルタイム性能向上を実現した。
キーポイント
BEV ポーリングの GPU 最適化
物理的 AI アプリケーションにおいて、従来のボトルネックとなっていた BEV(Bird's Eye View)ポーリング処理を NVIDIA GPU で効率的に実行する手法を開発した。
リアルタイム性能の向上
この最適化により、自動運転やロボティクス分野におけるセンサーデータ処理速度が大幅に向上し、より高度なリアルタイム判断が可能になる。
物理的 AI への応用拡大
計算コストの削減と処理速度の向上は、複雑な環境認識を要する物理的 AI システムの実用化とスケーラビリティを後押しする。
影響分析・編集コメントを表示
影響分析
この発表は、自動運転や自律型ロボットといった物理的 AI システムのボトルネックである視覚処理の効率化に寄与し、より複雑な環境下でのリアルタイム動作を可能にする。結果として、産業応用における導入ハードルが低下し、次世代の知能システム開発スピードが加速すると予想される。
編集コメント
物理的 AI の実用化において、視覚処理のボトルネック解消は極めて重要な課題であり、今回の GPU 最適化手法はその解決策として注目すべき進展です。
自律走行車(AVs)、ロボット、空間 AI システムにおける設計パターンとして、鳥瞰図(BEV)知覚がますます一般的になっています。BEV モデルはマルチカメラの画像特徴を共有された上からのグリッドに投影し、車線、車両、歩行者、空き領域について推論するための共通の空間レイアウトを、下流の知覚および計画モジュールに提供します。
このパイプラインにおける重要な操作は*BEV ポーリング(pooling)*であり、これは画像特徴を集約し、深度情報で重み付けを行った上で、BEV グリッドセルへ散乱・集約(scatter-reduce)する処理です。開発者にとって BEV 知覚の実践的価値は、多数のカメラ固有のビューを、シーンに対する空間的に一貫した表現に変換することにあります。各カメラ画像ごとに個別に推論を行うのではなく、下流モジュールは車両やロボットの周囲の世界に整合された統一された上からの特徴マップ上で動作できます。BEV ポーリングは、この表現を実時間で利用可能にするステップであり、検出、占有状況予測、軌道予測、マッピング、計画などのワークロードに供給できるコンパクトな BEV テンソルへ、深度を認識した画像特徴を変換します。
概念的にはこれは単純です。しかし、実装においては、不規則なメモリアクセス、インデックスの繰り返し読み取り、散乱・集約動作、および GPU 固有のキャッシュ効果を組み込むため、BEV ポーリングがレイテンシのボトルネックとなる可能性があります。
本記事では、BEVPoolV3 を事例として、NVIDIA GPU 向けに BEV ポーリングや gather/scatter 演算を多く含む他のオペレーターを最適化する手法について解説します。ここでは、実務で適用可能なワークフローを順を追って説明します:まずメモリーレジームを分類し、冗長な scatter トラフィックを除去し、カーネル実装を対象 GPU にマッピングし、NVIDIA Nsight Compute を用いて実際のボトルネックを検証するという流れです。得られたパフォーマンス結果は、このワークフローがなぜ重要かを示しています:同じ BEV ポーリングオペレーターでも、動作セットが DRAM バウンド状態にあるか、L2 キャッシュに主に残っているかによって、最適な最適化戦略は異なります。
NVIDIA RTX GPU 上で BEVPoolV3 はどのようにして BEV ポーリングのレイテンシを削減するのか?
先行研究において重要な進展が既に達成されています。本記事では V2 と呼ばれる BEVPoolV2 は、BEVDet スタイルモデル向けに効率的なデプロイ指向の BEV ポーリング定式化を導入しました。CUDA-BEVFusion には、bevpool_half_pack10_kernel というコンポーネントが含まれており、ここでは V2+DO と呼んでいます。これは深度外側走査(depth-outer traversal)を用いて、V2 で繰り返されていたタイル外側のインデックス読み込みの多くを排除しています。
BEVPoolV3 はこの最適化方向を引き継ぎつつ、4 つの追加変更を加えています:重複する深度読み込みの削減、5 配列からなる INT32 スキャッターマップ、ランタイムでの整数除算を不要にする事前計算済みインデックス、そしてインターバル固有の出力書き込みです。
本記事では、BEV ポーリングおよび gather または scatter を多用する他の演算子を NVIDIA GPU 上で最適化する方法について、BEVPoolV3 を事例として解説します。ここでは、BEV ポーリングのワークロードをメモリーレジーム別に分類し、冗長な scatter トラフィックを特定し、カーネル実装を対象 GPU にマッピングし、Nsight Compute を用いてアクティブなボトルネックを検証する方法を学びます。2 つの NVIDIA RTX GPU におけるパフォーマンス結果は、このワークフローがなぜ重要であるかを示しています:同じ BEV ポーリングアルゴリズムでも、ある GPU では DRAM バウンドとなり、別の GPU では主に L2 キャッシュに収まるため、異なる最適化選択が必要となります。
本評価では、異なるメモリーレジームを代表する 2 つの NVIDIA RTX GPU を比較します。1 つ目は、6 MB の L2 キャッシュを持ちネイティブ FP8 ISA を持たない NVIDIA Ampere SM86 GPU である NVIDIA RTX A6000 です。もう 1 つは、128 MB の L2 キャッシュを持ちネイティブ FP8 サポートを備えた NVIDIA Blackwell SM120 GPU である NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition です。ここで用いる標準的な設定は、実際の nuScenes サンプルから導き出されたものであり、約 209K の scatter ポイント、80 の特徴量チャネル、および 49 MB の BEV ポーリングワーキングセットを含んでいます。このワーキングセットは RTX A6000 の L2 キャッシュを超えていますが、RTX PRO 6000 Blackwell Max-Q の L2 キャッシュには収まります。その結果、初期ロード後は RTX A6000 が DRAM バウンドとなり、RTX PRO 6000 Blackwell Max-Q は主に L2 に残存することになります。
image*図 1. BEV pooling は、マルチカメラ画像の特徴量に深度情報を付与し、散乱・集約(scatter-reduce)して検出、占有予測、計画のための共有されたトップダウン表現に変換します*
標準的な設定では、V2 スタイルの NVIDIA TensorRT プラグインパスは RTX PRO 6000 Blackwell Max-Q で 274.0 µs を要します。一方、BEVPoolV3 は FP16 で 17.3 µs、FP8 で 16.4 µs に短縮します。RTX A6000 では、DRAM 対応の BEVPoolV3 FP16 パスが 90.0 µs に達します。速度向上だけでなく、この投稿では散乱・集約カーネルを最適化するための反復可能なワークフローを示しています:作業セットの分類、冗長なメモリアクセスの除去、ターゲット GPU に合わせた起動形状のマッチング、そして Nsight Compute による結果検証です。
image*図 2. V2 FP16 に対する標準 TensorRT プラグインパスの速度向上。RTX A6000 では V3 FP16 が V2 より 19.31 倍、RTX PRO 6000 Blackwell Max-Q では V3 FP16 が V2 より 15.84 倍、V3 FP8 が V2 より 16.71 倍の速度を達成*
前提条件
本投稿では、BEV pooling の文脈における CUDA カーネルの動作、TensorRT プラグインの統合、GPU プロファイリングについて解説します。有益な前提知識として以下が挙げられます:
- ワープスケジューリング、アトミック操作、ベクトル化されたグローバルロード、DRAM/L2/L1 キャッシュの動作といった CUDA カーネルの基本概念
- TensorRT プラグインの統合、特に IPluginV3 インターフェース
- メモリ動作、オキュパンシー(占有率)、命令発行のボトルネックを検証するための Nsight Compute プロファイリング
- CUDA-BEVFusion における BEV-pooling カーネルは、先行する深度外側参照実装として機能します
関連する背景情報については、CUDA C++ Programming Guide、TensorRT プラグインドキュメント、TensorRT サンプル、および Nsight Compute プロファイリングガイド を参照してください。
メモリレジームの分類
最初のステップは、BEV-pooling のワーキングセットが L2 に収まるかどうかを分類することです。標準的な設定では、主要な配列の合計は約 49 MB で、特徴量データと出力が支配的です。この単一の数値がメモリレジームを決定します:これは RTX A6000 の 6 MB L2 キャッシュよりも大きいですが、RTX PRO 6000 Blackwell Max-Q の 128 MB L2 キャッシュよりは小さいです。
image*図 3. L2 キャパシティによる標準的な BEV ポーリングのワーキングセットの分類。実世界の nuScenes サンプルから導出された標準設定では、ワーキングセットは約 49 MB です。これは RTX A6000 の 6 MB の L2 キャッシュを超えているため、カーネルは DRAM バウンドパスをたどります。一方、同じワーキングセットは RTX PRO 6000 Blackwell Max-Q の 128 MB の L2 キャッシュに収まるため、初期充填後はカーネルが主に L2 に残存します。なお、この図は概念的なものであり、正確な縮尺で描かれているわけではありません*。
この適合/不適合の判断は最適化目標を変更します。RTX A6000 では、特徴量の収集と出力トラフィックが L2 を超えてスピンアウトするため、小規模 L2 パスはバイト数の削減とキャッシュストリーミングによる出力ストアを優先します。一方、RTX PRO 6000 Blackwell Max-Q では標準的なワーキングセットが L2 に収まるため、大規模 L2 パスは命令効率、オーカパンシー(occupancy)、事前計算されたインデックス、ベクトル化ロード、および FP8 専門化へとシフトします。
冗長なスキャッタートラフィックの除去
BEV スキャッター・リデュースは以下のように要約できます:
out[ranks_bev[t], c] += depth[ranks_depth[t]] * feat[ranks_feat[t], c];
BEVPoolV2 は散乱ループの外側でチャネルタイルを反復処理します。C=80 で 8 チャネルのタイルの場合、同じ散乱インデックスが 10 回読み込まれます。これにより、インデックスは一度だけ読み込む場合に 2.51 MB で済むところ、約 25.1 MB のインデックストラフィックが発生してしまいます。深さ-外側ループ順序(depth-outer loop order)を採用することで、この問題のほとんどが解決します。これは各 BEV インターバルを先に反復処理し、そのインターバルに対するすべてのチャネルを一度のパスで累積する方式です。
BEVPoolV3 は、CUDA-BEVFusion の bevpool_half_pack10_kernel で使用されている深さ-外側最適化の方向性を拡張したもので、ここでは V2+DO と呼んでいます。V2+DO は有用なベースラインです。なぜなら、それは BEVPoolV2 における繰り返しのタイル外側インデックス読み込みをすでに排除しており、インターバルベースの走査の価値を実証しているからです。BEVPoolV3 はこの方向性を維持しつつ、GPU メモリレジーム全体で移植性とパフォーマンスを向上させる 4 つの実装変更を追加しました。具体的には、各インターバル内の重複する深さ読み込みの削減、ranks_depth、ranks_feat、ranks_bev、interval_starts、interval_lengths の 5 つの配列を用いた INT32 散乱マップ(scatter map)の使用、ランタイムでの整数除算を不要にする事前計算された明示的インデックス、そして V2 スタイルパスに対するアトミック操作を回避するインターバル所有型の出力書き込みです。
image*図 4. V2+DO は冗長なインデストラフィックのほとんどを除去します。V3 FP16 はさらに整列された散乱マップのオーバーヘッドと命令圧力を削減します。V3 FP8 は特徴量と出力のバイト数を半減させ、これはワークセットが L2 に残っている場合に最も効果的です*
5 つの配列からなる散乱マップは、大規模 L2 GPU において特に重要です。(ranks_depth, ranks_feat, ranks_bev) を int3 配列にパッキングすると 12 バイトのレコードになります。このレイアウトはアライメントされたメモリアクセスには不向きであり、16 バイトの LDG.128 ロードにもきれいにマッピングされません。INT32 配列を別々に用意することで、隣接するスレッドがアライメントされたロードを統合でき、フィールド間の結合を回避できます。論理的なバイト数は似て見えるかもしれませんが、命令ストリームははるかにクリーンになります。
インターバル所有型の散乱リダクションの実装
本番環境では BEVPoolV3 は複数の特殊化されたカーネルを使用しますが、その中核となる実装の考え方は、小さなロジックのスケッチとして理解するとより容易です。散乱マップは事前に用意され、各 BEV インターバルには 1 つの所有者が割り当てられます。所有者はそのインターバル内のポイントを走査し、関連する特徴チャネルを累積して、出力を一度だけ書き込みます。
この構造により、散乱マップが単一のレコードにパッキングされた際に発生する内部ループでのデコーディング作業が不要になります。ランタイムでインデックスを再構築する代わりに、カーネルは ranks_depth, ranks_feat, ranks_bev, interval_starts, interval_lengths といった明示的な配列を読み取ります。
// 1. 5 つの事前計算済み散乱配列を使用する。
// 2. ランタイムでのインデックス分割なしで、明示的なインデックスを直接読み取る。
// 3. 各インターバルの所有者が出力セルを累積させる。
// 4. 所有者ループ内の各散乱ポイントに対して、深度値を一度だけロードする。
for each interval iv in parallel:
start = interval_starts[iv]
length = interval_lengths[iv]
bev = ranks_bev[start]
acc[channel_tile] = 0
for offset in 0 .. length - 1:
t = start + offset
d = depth[ranks_depth[t]]
feat_row = ranks_feat[t]
for c in channel_tile:
acc[c] += d * feat[feat_row, c]
out[bev, channel_tile] = acc
このコードのスケッチは、一般的な BEVPoolV3 の構造を捉えています。散乱マップ(scatter map)が明示的に定義され、ランタイム時のインデックスデコードが削除され、深度データはインターバル所有者ループ内で読み込まれ、各出力セルは局所的な集積処理後に一度だけ書き込まれます。
本番環境用のカーネルは、この構造をターゲットとなるメモリレジームに合わせて最適化しています。L2 キャッシュ容量が小さい RTX A6000 などの GPU では、実装はバイト数の削減、FP16 half2 形式での積算、およびキャッシュストリーミング出力ストアを優先し、出力テンソルが L2 から有用なインデックスデータを追い出さないようにしています。一方、L2 キャッシュ容量が大きい RTX PRO 6000 Blackwell Max-Q などの GPU では、実装はまず高稼働率の起動エンベロープに適合させ、その後、事前計算されたインデックス、ベクトル化されたインデックス読み込み、およびワークセットが L2 に常駐する FP8 特化型内部ループを用いて命令オーバーヘッドを削減します。
アルゴリズムの不変条件は同じです。インターバルの所有、ランタイム時のインデックスデコードの回避、局所的な集積処理、そして一度だけの書き込みです。アーキテクチャ固有の作業内容は、この不変条件がどのように実装されるかを変えるだけであり、BEV ポーリング演算子が計算する内容そのものを変えるものではありません。
image*図 5. RTX PRO 6000 Blackwell Max-Q における TensorRT のレイテンシを 6 つの BEV pooling 構成で比較。V3 FP8 がすべての構成で最速であり、チャネル数が増えるほどその差が拡大*
RTX PRO 6000 Blackwell Max-Q における絶対的なレイテンシ結果は、異なるポイント数とチャネル幅にわたって大規模 L2 パスがどのように動作するかを示しています。同じ最適化パターンは、V2 FP16 ベースラインに対する速度向上として測定した場合、RTX A6000 の DRAM バウンドパスでも成立します。RTX A6000 において、DRAM に適応させた V3 FP16 パスは、テストされたすべての構成で V2 に対して 11 倍から 22 倍の速度向上を達成しました。一方、RTX PRO 6000 Blackwell Max-Q では、V3 FP8 が V2 に対して 11 倍から 42 倍の速度向上を示し、特にポイント数が多い場合やチャネル構成が広い場合に最大の効果が現れます。
image*図 6. V2 FP16 に対する GPU 横断的な速度向上。RTX A6000 の V3 FP16 はテスト構成全体で 11–22 倍、RTX PRO 6000 Blackwell Max-Q の V3 FP8 はポイント数とチャネル幅に応じて 11–42 倍*
TensorRT プラグインの展開と検証
BEVPoolV3 は、TensorRT IPluginV3 オペレーターとして公開されています。このプラグインは、5 つの配列からなる散乱マップ(scatter map)に加え、深度データと特徴量(feat)を受け取り、GPU クラスおよびデータ型(dtype)に応じて適切なカーネルをディスパッチします。ベンチマークパスでは、ONNX から TensorRT へのビルドと、trtexec を用いた CUDA Graph の再生が使用されました。
検証のため、FP64 の参照値または既存の信頼できる V2 パスと比較してください。RTX A6000 向けに DRAM に適応させたカーネルは、6 つの設定すべてにおいて atol=1e-2 でテストされたすべての出力要素をパスし、観測された最大誤差は 0.0065 でした。RTX PRO 6000 Blackwell Max-Q では、V2 と V3 がテスト対象の設定で同一の出力を生成しており、最適化された scatter-map(散乱マップ)および起動変更が参照パスの数値動作を維持していることを示しています。
アルゴリズムをハードウェアにマッピングする
BEVPoolV3 の 4 つのアルゴリズム変更は移植可能ですが、本番用のカーネルはアクティブな GPU のボトルネックに合わせる必要があります。重要な判断基準は、BEV ポーリングのワーキングセットが L2 キャッシュに収まるかどうかです。
RTX A6000 では、標準的なワーキングセットが L2 を超えるため、カーネルはランダム・ギャザ(random-gather)による DRAM トラフィックによって制限されます。そのため FP16 パスはバイト数の削減とキャッシュの維持を優先します。TILE_C を 8 から 16 に増やすことで、C=80 のタイルパス数を 10 から 5 に減らし、ループオーバーヘッドと繰り返されるスカラー演算を削減します。__half2 による蓄積に __hfma2(半精度浮動小数点乗算加算命令)を使用することで、不要な FP16 から FP32 への拡張およびパッキングを防ぎます。キャッシュ・ストリーミング出力ストアにより、12.8 MB の出力テンソルがより小さな L2 常駐インデックス配列を追い出すのを防ぎます。これらの変更後、RTX A6000 パスは標準設定で 90.0 µs を達成し、V2 FP16 の 1,738.0 µs と比較して大幅な改善が見られます。
RTX PRO 6000 Blackwell Max-Q では、標準的なワーキングセットが L2 キャッシュに収まるため、ボトルネックは命令発行、オキュパンシー(稼働率)、および依存関係の遅延へとシフトします。本番環境用のカーネルはまず、高オキュパンシーを実現する V2+DO スタイルの実行エンベロープに合致し、その後、5 次元配列による散乱マップと事前計算されたインデックスを用いて内部ループのオーバーヘッドを削減します。これにより、ランタイムにおける整数除算が回避され、散乱マップへの負荷圧力が軽減されます。標準構成では、V3 FP16 は 17.3 µs を達成するのに対し、V2+DO FP16 は 37.8 µs かかり、同じデータ型(dtype)で 2.18 倍の高速化が実現されています。
FP8 パスはさらに大規模 L2 キャッシュケースに特化しています。特徴量および出力データが L2 から供給されるため、これらのデータ型のビット幅を削減することで、実際のレイテンシ短縮につながります。本番環境用の FP8 パスでは、チャンネルごとのカウントに基づくエントリーポイント、C=80 向けの LDG.64 インデックスパッキング、そして C=128 および C=256 向けに広範な特徴量ロードを採用しています。パッキング済みインデックスパスの上にループ展開を追加するなど、より積極的な組み合わせも検討されましたが、レジスタ圧力とスパイル(溢れ)トラフィックが増加したため、きれいに統合できませんでした。
精度の階段には実用的な目的地があり、私たちの NVFP4 評価は各フォーマットがどこで輝くかを明確にします。具体的には、カメラ特徴量を E2M1 で保存し、深度と出力を FP8 に保ちつつ、16 要素ごとの E4M3 マイクロブロックスケールを持つ NVFP4 パスをテストしました。__half2 packed accumulators(半精度 2 要素パック型加算器)、融合されたスケール–深度係数、および半精度 LUT を備えた攻撃的に最適化された実装であっても、デコードのオーバーヘッドにより、このパスは FP8 ベースラインよりも明らかに低速で動作します。
Nsight Compute によるプロファイリングでは、カーネルが L2 キャッシュに完全に常駐し、DRAM バンド幅の利用率が低く、smsp__issue_active(スレッドブロックの実行アクティブ度)がピークスループットを大きく下回っていることが示されました。一方、ALU パイプラインは FMA パイプラインよりも大幅に多くの命令を処理しています。
これは、この散乱・集約レジームが FP8 において利用可能なバイト効率の恩恵をすでに捉えていることを示唆しており、NVFP4 の追加的な要素ごとのニブル抽出、値デコード、およびマイクロブロックごとのスケール折りたたみは、FP8 パスが単一のスカラー FP8 から半精度への変換を通じて回避する内ループ処理を導入します。その結果、明確なワークロード配置のストーリーが浮かび上がります:NVFP4 は Tensor Cores を通じて MMA.kind::nvfp4(行列乗算演算)を介して流れる計算バウンド型の行列乗算形状に対して極めて強力な適合性を示しますが、L2 常駐型の散乱・集約ワークロードにおいては、dtype ラダー上では FP8 が最適です。
同様の分析は BEV ポーリング以外でも適用されます。スパース埋め込み、ボクセル化、ヒストグラム、セグメント化された集約処理、およびその他の gather または scatter 演算が中心となるオペレーターにおいては、まずメモリレジームを分類し、その後 Nsight Compute を用いてアクティブなボトルネックが帯域幅、命令発行、またはオーカパンシーのいずれであるかを特定してください。
表 1 は、RTX PRO 6000 Blackwell Max-Q の TensorRT プラグインパス遅延を要約したものであり、100 回の反復における中央値遅延として報告されています。
設定C-次元V2 FP16V2+DO FP16V3 FP16V3 FP8V3 FP8 / V2
small80137.8 µs31.5 µs12.7 µs12.6 µs10.94x
canonical80274.0 µs37.8 µs17.3 µs16.4 µs16.71x
large80749.9 µs48.0 µs27.3 µs24.9 µs30.12x
xlarge801,675.0 µs61.9 µs48.0 µs39.8 µs42.09x
wide_c128128457.3 µs54.2 µs21.4 µs14.8 µs30.90x
wide_c256256880.9 µs152.3 µs33.4 µs22.0 µs40.04x
*表 1. 複数のモデル設定における RTX PRO 6000 Blackwell Max-Q の TensorRT プラグインパス遅延。値は、V2 FP16、V2+DO FP16、V3 FP16、および V3 FP8 に対する各ベンチマーク推論/プラグイン呼び出しの 100 回反復における中央値遅延(マイクロ秒単位)を示しています。最終列は、遅延削減から計算された V3 FP8 の V2 FP16 に対する速度向上倍率を示します*
エッジクラスプラットフォームにおける考慮事項
同様の分析は、NVIDIA DRIVE AGX Thor を含むエッジクラス NVIDIA プラットフォームにも拡張できます。初期のエッジ指向実験では、FP16 の BEVPoolV3 パスが良好に継承されます。なぜなら中核的な改善点である冗長な散乱トラフィックの除去、ランタイムインデックス復号の回避、およびインターバル所有書き込みの使用は、アーキテクチャに依存しないためです。
一方、FP8 の高速化が自動的に実現されるわけではありません。エッジクラスターゲットでは、問題サイズの小ささ、メモリアーキテクチャの動作、レジスタ圧力、および FP8 変換オーバーヘッドが、理論的なデータ型帯域幅の恩恵を制限または相殺する可能性があります。これにより、FP8 は FP16 の代替として即座に差し替えることができるものではなく、カーネルおよびアーキテクチャ固有の最適化となります。
BEV ポーリング最適化の開始方法
BEVPoolV3 ワークフローを独自の BEV 知覚や gather/scatter(集積/散乱)処理が重いワークロードに適用するには、まず演算子を単独でプロファイリングすることから始めます。特徴量、深度、散乱インデックス、出力テンソルのサイズを測定し、総作業セット wi
原文を表示
An increasingly common design pattern for autonomous vehicles (AVs), robotics, and spatial AI systems is bird’s-eye-view (BEV) perception. BEV models project multicamera image features into a shared top-down grid, providing downstream perception and planning modules with a common spatial layout for reasoning about lanes, vehicles, pedestrians, and free space.
A key operation in this pipeline is *BEV pooling*, which gathers image features, weights them with depth information, and scatter-reduces them into BEV grid cells. For developers, the practical value of BEV perception is that it converts many camera-specific views into one spatially consistent representation of the scene. Instead of reasoning separately over each camera image, downstream modules can operate on a unified top-down feature map aligned to the world around the vehicle or robot. BEV pooling is the step that makes this representation usable in real time: it turns depth-aware image features into a compact BEV tensor that can feed detection, occupancy, trajectory prediction, mapping, and planning workloads.
Conceptually, this is simple. In deployment, however, BEV pooling can become a latency bottleneck because it combines irregular memory access, repeated index reads, scatter-reduce behavior, and GPU-specific cache effects.
This post uses BEVPoolV3 as a case study in optimizing BEV pooling and other gather- or scatter-heavy operators for NVIDIA GPUs. It walks through a practical workflow you can apply to your workloads: classify the memory regime, remove redundant scatter traffic, map the kernel implementation to the target GPU, and validate the active bottleneck with NVIDIA Nsight Compute. The performance results show why this workflow matters: the same BEV pooling operator can require different optimization strategies depending on whether the working set is DRAM-bound or largely L2-resident.
How does BEVPoolV3 reduce BEV pooling latency on NVIDIA RTX GPUs?
Prior work has already made important progress. BEVPoolV2, referred to as V2 in this post, introduced an efficient deployment-oriented BEV pooling formulation for BEVDet-style models. CUDA-BEVFusion includes bevpool_half_pack10_kernel, referred to here as V2+DO, which uses depth-outer traversal to remove much of the V2 repeated tile-outer index loading.
BEVPoolV3 continues this optimization direction with four additional changes: reduced duplicate depth loads, a five-array INT32 scatter map, precomputed indices that remove runtime integer division, and interval-owned output writes.
This post uses BEVPoolV3 as a case study in how to optimize BEV pooling and other gather- or scatter-heavy operators for NVIDIA GPUs. You will learn how to classify a BEV pooling workload by memory regime, identify redundant scatter traffic, map the kernel implementation to the target GPU, and validate the active bottleneck with Nsight Compute. The performance results on two NVIDIA RTX GPUs show why this workflow matters: the same BEV pooling algorithm can be DRAM-bound on one GPU and largely L2-resident on another, requiring different optimization choices.
The evaluation compares two NVIDIA RTX GPUs that represent different memory regimes: NVIDIA RTX A6000, an NVIDIA Ampere SM86 GPU with a 6 MB L2 cache and no native FP8 ISA, and NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition, an NVIDIA Blackwell SM120 GPU with a 128 MB L2 cache and native FP8 support. The canonical config used here is derived from real nuScenes samples and contains about 209K scatter points, 80 feature channels, and a 49 MB BEV pooling working set. That working set exceeds RTX A6000 L2 cache but fits inside RTX PRO 6000 Blackwell Max-Q L2 cache, making RTX A6000 DRAM-bound and RTX PRO 6000 Blackwell Max-Q largely L2-resident after the initial fill.

In the canonical config, the V2-style NVIDIA TensorRT plugin path takes 274.0 µs on RTX PRO 6000 Blackwell Max-Q. BEVPoolV3 reduces that to 17.3 µs in FP16 and 16.4 µs in FP8. On RTX A6000, the DRAM-adapted BEVPoolV3 FP16 path reaches 90.0 µs. Beyond the speedup, this post shows a repeatable workflow for optimizing scatter-reduce kernels: classify the working set, remove redundant memory traffic, match the launch shape to the target GPU, and validate the result with Nsight Compute.

Prerequisites
This post discusses CUDA kernel behavior, TensorRT plugin integration, and GPU profiling in the context of BEV pooling. Helpful prerequisites include:
- CUDA kernel concepts such as warp scheduling, atomics, vectorized global loads, and DRAM/L2/L1 cache behavior
- TensorRT plugin integration, especially the IPluginV3 interface
- Nsight Compute profiling for validating memory behavior, occupancy, and instruction-issue bottlenecks
- The BEV-pooling kernel in CUDA-BEVFusion as the prior depth-outer reference implementation
For related background information, see the CUDA C++ Programming Guide, TensorRT plugin documentation, TensorRT samples, and Nsight Compute Profiling Guide.
Classify the memory regime
The first step is to classify whether the BEV-pooling working set fits in L2. In the canonical config, the main arrays total about 49 MB, dominated by feature data and output. That single number determines the memory regime: it is larger than the RTX A6000 6 MB L2 cache, but smaller than RTX PRO 6000 Blackwell Max-Q 128 MB L2 cache.

This fit/no-fit decision changes the optimization target. On RTX A6000, feature gathers and output traffic spill beyond L2, so the small-L2 path prioritizes byte reduction and cache-streaming output stores. On RTX PRO 6000 Blackwell Max-Q, the canonical working set fits in L2, so the large-L2 path shifts toward instruction efficiency, occupancy, precomputed indices, vectorized loads, and FP8 specialization.
Remove redundant scatter traffic
The BEV scatter-reduce can be summarized as:
out[ranks_bev[t], c] += depth[ranks_depth[t]] * feat[ranks_feat[t], c];
BEVPoolV2 iterates over channel tiles outside the scatter loop. For C=80 and an 8-channel tile, the same scatter indices are loaded 10 times. That produces roughly 25.1 MB of index traffic for indices that only need 2.51 MB when read once. A depth-outer loop order fixes most of that problem by iterating over each BEV interval first and accumulating all channels for that interval in one pass.
BEVPoolV3 extends the depth-outer optimization direction used in CUDA-BEVFusion bevpool_half_pack10_kernel, referred to here as V2+DO. V2+DO is a useful baseline because it already removes the repeated tile-outer index loads in BEVPoolV2 and demonstrates the value of interval-based traversal. BEVPoolV3 keeps that direction and adds four implementation changes that improve portability and performance across GPU memory regimes: reduced duplicate depth loads within each interval; a five-array INT32 scatter map µsing ranks_depth, ranks_feat, ranks_bev, interval_starts, and interval_lengths; precomputed explicit indices that remove runtime integer division; and interval-owned output writes that avoid atomics relative to the V2-style path.

The five-array scatter map is especially important on large-L2 GPUs. Packing (ranks_depth, ranks_feat, ranks_bev) into an int3 array gives a 12-byte record. That layout is inconvenient for aligned memory transactions and does not map cleanly to a 16-byte LDG.128 load. Separate INT32 arrays let adjacent threads merge aligned loads and avoid field coupling. The total logical bytes may look similar, but the instruction stream is much cleaner.
Implement interval-owned scatter-reduce
In production, BEVPoolV3 uses multiple specialized kernels, but the core implementation idea is easier to understand as a small logic sketch. The scatter map is prepared ahead of time, each BEV interval is assigned to one owner, the owner walks the points in that interval, accumulates the relevant feature channels, and writes the output once.
This structure removes the inner-loop decoding work that appears when the scatter map is packed into a single record. Instead of reconstructing indices at runtime, the kernel reads explicit arrays such as ranks_depth, ranks_feat, ranks_bev, interval_starts, and interval_lengths.
// 1. Use five precomputed scatter arrays.
// 2. Read explicit indices directly, with no runtime index division.
// 3. Let one interval owner accumulate the output cell.
// 4. Load each depth value once per scatter point in the owner loop.
for each interval iv in parallel:
start = interval_starts[iv]
length = interval_lengths[iv]
bev = ranks_bev[start]
acc[channel_tile] = 0
for offset in 0 .. length - 1:
t = start + offset
d = depth[ranks_depth[t]]
feat_row = ranks_feat[t]
for c in channel_tile:
acc[c] += d * feat[feat_row, c]
out[bev, channel_tile] = acc
This code sketch captures the common BEVPoolV3 structure: the scatter map is explicit, runtime index decoding is removed, depth is loaded in the interval owner loop, and each output cell is written once after local accumulation.
The production kernels specialize this structure for the target memory regime. On small-L2 GPUs such as RTX A6000, the implementation prioritizes byte reduction, FP16 half2 accumulation, and cache-streaming output stores so the output tensor does not evict useful index data from L2. On large-L2 GPUs such as RTX PRO 6000 Blackwell Max-Q, the implementation first matches a high-occupancy launch envelope, then reduces instruction overhead with precomputed indices, vectorized index loads, and FP8-specialized inner loops where the working set is L2-resident.
The algorithmic invariant stays the same: own the interval, avoid runtime index decoding, accumulate locally, and write once. The architecture-specific work changes how that invariant is implemented, not what the BEV-pooling operator computes.

The absolute latency results on RTX PRO 6000 Blackwell Max-Q show how the large-L2 path behaves across different point counts and channel widths. The same optimization pattern also holds on the RTX A6000 DRAM-bound path when measured as speedup over the V2 FP16 baseline. On RTX A6000, the DRAM-adapted V3 FP16 path reaches speedups of 11s to 22x over V2 across the tested configurations. On RTX PRO 6000 Blackwell Max-Q, V3 FP8 reaches speedups of 11x to 42x over V2, with the largest gains appearing at larger point counts and wider channel configurations.

Deploy and validate the TensorRT plugin
BEVPoolV3 is exposed as a TensorRT IPluginV3 operator. The plugin accepts the five-array scatter map plus depth and feat, then dispatches the appropriate kernel for the GPU class and dtype. The benchmark path used ONNX-to-TensorRT builds and CUDA Graph replay with trtexec.
For validation, compare against an FP64 reference or an existing trusted V2 path. The RTX A6000 DRAM-adapted kernel passed all tested output elements across the six configurations at atol=1e-2, with maximum observed error of 0.0065. On RTX PRO 6000 Blackwell Max-Q, V2 and V3 produced identical outputs for the tested configurations, indicating that the optimized scatter-map and launch changes preserved the numerical behavior of the reference path.
Map the algorithm onto the hardware
The four BEVPoolV3 algorithmic changes are portable, but the production kernel must match the active GPU bottleneck. The key decision is whether the BEV-pooling working set fits in L2.
On RTX A6000, the canonical working set exceeds L2, so the kernel is limited by random-gather DRAM traffic. The FP16 path therefore prioritizes byte reduction and cache preservation. Increasing TILE_C from 8 to 16 cuts the C=80 tile passes from 10 to 5, reducing loop overhead and repeated scalar work. Using __half2 accumulation with __hfma2 avoids unnecessary FP16-to-FP32 widening and packing. Cache-streaming output stores prevent the 12.8 MB output tensor from evicting the smaller L2-resident index arrays. After these changes, the RTX A6000 path reaches 90.0 µs in the canonical config, compared with 1,738.0 µs for V2 FP16.
On RTX PRO 6000 Blackwell Max-Q, the canonical working set fits in L2, so the limiting factors shift toward instruction issue, occupancy, and dependency latency. The production kernel first matches the high-occupancy V2+DO-style launch envelope, then removes inner-loop overhead with the five-array scatter map and precomputed indices. This avoids runtime integer division and reduces scatter-map load pressure. In the canonical config, V3 FP16 reaches 17.3 µs versus 37.8 µs for V2+DO FP16, a 2.18x speedup at the same dtype.
The FP8 path further specializes in the large-L2 case. Because feature and output data are served from L2, reducing their dtype can translate into real latency gains. The production FP8 path uses per-channel-count entry points, LDG.64 index packing for C=80, and wider feature loads for C=128 and C=256. More aggressive combinations, such as adding loop unrolling on top of the packed-index path, did not compose cleanly because they increased register pressure and spill traffic.
The precision ladder has a practical destination, and our NVFP4 evaluation helps clarify exactly where each format shines: we tested an NVFP4 path that stores camera features in E2M1 with per-16-element E4M3 microblock scales while keeping depth and output in FP8, and even with an aggressively optimized implementation featuring __half2 packed accumulators, fused scale–depth coefficients, and a half-precision LUT, the decode overhead causes it to run notably slower than the FP8 baseline.
Profiling with Nsight Compute shows the kernel is fully resident in L2 cache, with low DRAM bandwidth utilization and smsp__issue_active hovering well below peak throughput, while the ALU pipeline carries significantly more instructions than the FMA pipeline.
This indicates that this scatter-reduce regime has already captured the available byte-efficiency benefits at FP8, while the NVFP4 additional per-element nibble extraction, value decode, and per-microblock scale fold introduce inner-loop work that the FP8 path avoids through a single scalar FP8 to half conversion. The result is a crisp workload-placement story: NVFP4 remains an incredibly powerful fit for compute-bound matrix multiplication shapes flowing through Tensor Cores through MMA.kind::nvfp4, while for L2-resident scatter-reduce workloads, FP8 is ideal on the dtype ladder.
The same analysis applies beyond BEV pooling. For sparse embeddings, voxelization, histograms, segmented reductions, and other gather- or scatter-heavy operators, first classify the memory regime, then use Nsight Compute to determine whether the active ceiling is bandwidth, instruction issue, or occupancy.
Table 1 summarizes RTX PRO 6000 Blackwell Max-Q TensorRT plugin-path latency, reported as 100-iteration median latency.
Considerations for edge-class platforms
The same analysis can extend to edge-class NVIDIA platforms, including NVIDIA DRIVE AGX Thor. In early edge-oriented experiments, the FP16 BEVPoolV3 path carries over well because the core improvements—removing redundant scatter traffic, avoiding runtime index decoding, and using interval-owned writes—are architecture-independent.
FP8 speedup, however, is not automatic. On edge-class targets, smaller problem sizes, memory hierarchy behavior, register pressure, and FP8 conversion overhead can limit or offset the theoretical dtype bandwidth benefit. This makes FP8 a kernel- and architecture-specific optimization rather than a guaranteed drop-in replacement for FP16.
Get started with BEV pooling optimization
To apply the BEVPoolV3 workflow to your own BEV perception or gather/scatter-heavy workload, start by profiling the operator in isolation. Measure the feature, depth, scatter-index, and output tensor sizes, then compare the total working set wi
関連記事
アジリティ・ロボティクス、SPAC を通じた上場を計画し 25 億ドル規模の取引へ
ロボット開発企業のアジリティ・ロボティクスは、特殊目的買収会社(SPAC)を利用した株式市場への新規上場を発表し、評価額約 25 億ドルの取引を進める方針を示している。
NVIDIA と AWS が大規模な AI の実用化に向けて協力
NVIDIA と Amazon Web Services(AWS)が、AI を大規模に生産環境で運用するための協力を開始した。両社はインフラと技術の統合により、企業による AI の実装を加速させる方針を示している。
フルスタック推論・学習最適化による AI ファクトリのエネルギー効率最大化
NVIDIA は、AI ファクトリ全体のエネルギー効率を向上させるため、推論と学習の両面で最適化手法を提案している。
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み