# Understanding Host Network Stack Overheads
> [!abstract] 概要
> 従来のエンドホストネットワークスタックは、データセンターのアクセスリンク帯域が急速に増加する一方で CPU オーバーヘッドが持続不可能になりつつある。
> この状況を受けて、Linux カーネルの最適化、部分的なハードウェアオフロード、ゼロから設計したユーザー空間スタック、専用ホストネットワークハードウェアなど、将来のネットワークスタックに向けた多様な解決策が検討されている。
> これらの設計空間を評価するには、既存ネットワークスタックの CPU 非効率性を詳細に理解する必要がある。
> 本稿は、100 Gbps のアクセスリンクにおける Linux カーネルネットワークスタックの性能を測定し、その知見を示す。
> このような高帯域リンクと、コア速度・コア数・キャッシュ容量・NIC バッファ容量など他のホスト資源の比較的停滞した技術動向の組み合わせは、ホストネットワークスタックのボトルネックに根本的な変化をもたらす。
> 例えば、単一コアはもはや線速でパケットを処理できず、受信側でのカーネルバッファからアプリケーションバッファへのデータコピーが主要な性能ボトルネックになる。
> また、帯域遅延積の増加がキャッシュ容量の増加を上回ることで、NIC と CPU の DMA パイプラインが非効率になる。
> 最後に、従来のネットワークスタックと CPU スケジューラの疎結合設計が、複数コアへネットワークスタック性能を拡張する際の制約になる。
## 論文情報
- 著者: [[Qizhe Cai]]、[[Shubham Chaudhary]]、[[Midhul Vuppalapati]]、[[Jaehyun Hwang]]、[[Rachit Agarwal]]
- 所属: [[Cornell University]]
- 媒体: *ACM SIGCOMM 2021*
- 発表日: 2021-08-23〜2021-08-27
- DOI: https://doi.org/10.1145/3452296.3472888
- 再現用コード: [[terabit-network-stack-profiling]](https://github.com/Terabit-Ethernet/terabit-network-stack-profiling)
## 概要
本稿の中心的な観察は、100 Gbps 級のリンクではネットワークのボトルネックがネットワークコアからホストへ移り、従来の「プロトコル処理を減らす」だけでは不十分になるという点である。
最適化を有効にした Linux でも、単一の長フローに対するスループット毎コアは約 42 Gbps に留まり、受信側 CPU の最大の消費要因はカーネルバッファからアプリケーションバッファへのデータコピーであった。
長フロー・短フロー・複数フローの混在では、キャッシュ、NUMA、パケット集約、メモリ管理、スケジューリングが相互作用するため、同じ固定的な処理経路をすべての接続に適用する設計が限界になる。
(Source: [[.raw/papers/Understanding_host_network_stack_overheads.txt]] Abstract, §1, §3)
## 問題設定
データセンターのアクセスリンクは過去数年で 4〜10 倍に高速化した一方、コア速度・コア数・キャッシュ容量・NIC バッファ容量などの伸びは比較的小さい。
既存研究には短フローや低帯域リンクの分析が多いが、データセンターでは長フローが大半のデータ量を占め、実際のワークロードは短フローと長フロー、複数の通信パターンの混合になる。
したがって、単一のフロー種別だけを測定しても、ホストネットワークスタックの CPU 非効率性を完全には説明できない。
本稿は、TCP/IP 処理、データコピー、`skb` 管理、メモリ確保・解放、ロック、スケジューリング、その他の処理を CPU サイクルの分類軸にし、単一フロー、one-to-one、incast、outcast、all-to-all の5通信パターンを比較する。
## 測定方法
### 実験環境
2 台のサーバーをスイッチなしで 100 Gbps リンクに直結し、ネットワークコアではなくホストスタックがボトルネックになる構成にした。
- CPU: Intel Xeon Gold 6128、3.4 GHz、4 ソケット NUMA、各ソケット 6 コア
- キャッシュ: L1 32 KB、L2 1 MB、L3 20 MB
- メモリ: 256 GB
- NIC: Mellanox ConnectX-5 Ex、100 Gbps
- ソフトウェア: Ubuntu 16.04、Linux kernel 5.4.43
- 既定条件: DDIO 有効、ハイパースレッディング無効、IOMMU 無効
- 長フロー: `iPerf`
- 短フロー: `netperf` による ping-pong 型 RPC
- 輻輳制御: 既定は TCP CUBIC。BBR と DCTCP も比較
スループット、全コアの CPU 使用率、ボトルネック側のスループット毎コアを測定し、サンプリング型 CPU プロファイリングで約 95% の CPU 使用率を占める関数を分類した。
再現用の Linux 計装コード、スクリプト、ドキュメントは [[terabit-network-stack-profiling]] として公開されている。
(Source: [[.raw/papers/Understanding_host_network_stack_overheads.txt]] §2.2)
## Linux ネットワークスタックのデータ経路
送信側では、アプリケーションの `write` により `skb` が作られ、ユーザー空間からカーネルバッファへデータがコピーされる。
その後、TCP/IP 層、ネットワークサブシステム、NIC ドライバを通り、GSO/TSO により大きな `skb` のセグメント化を NIC 側へ委ねて送信キューへ入れる。
受信側では、NIC が DMA でフレームを書き込み、IRQ と NAPI ポーリングを起点に `skb` を作り、GRO/LRO による集約、RPS/RFS/aRFS による CPU 選択、TCP/IP 処理を経て、アプリケーションが `read` でデータをコピーする。
![[_attachments/Understanding_host_network_stack_overheads/fig01-linux-network-stack-data-path.png]]
(Figure 1。送信側と受信側のアプリケーション、ソケット、TCP/IP、ネットワークサブシステム、ドライバ、NIC のデータ経路を示す。受信側では IRQ、NAPI、GRO、RPS/RFS を経てソケットへ到達する。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 1.)
**Table 1: CPU 使用率の分類**
| 分類 | 内容 |
|---|---|
| Data copy | ユーザー空間とカーネル空間の間のコピー |
| TCP/IP | TCP/IP 層の全パケット処理 |
| Netdevice subsystem | NAPI、GSO/GRO、qdisc などの netdevice・NIC ドライバ処理 |
| skb management | `skb` の構築、分割、解放 |
| Memory de-/alloc | `skb` の確保・解放とページ関連処理 |
| Lock/unlock | スピンロックなどのロック操作 |
| Scheduling | スレッド間のスケジューリングとコンテキスト切り替え |
| Others | IRQ 処理など上記以外 |
(Table 1. CPU usage taxonomy。Figure 1 の各層へ対応する CPU 使用率の分類。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Table 1.)
5 つの通信パターンは、単一フロー、one-to-one、incast、outcast、all-to-all である。
![[_attachments/Understanding_host_network_stack_overheads/fig02-traffic-patterns.png]]
(Figure 2。送信コアと受信コアの組み合わせを、単一フロー、one-to-one、incast、outcast、all-to-all の5パターンで示す。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 2.)
**Table 2: 受信側のフロー振り分け**
| 方式 | 説明 |
|---|---|
| RPS (Receive Packet Steering) | 4-tuple ハッシュで処理コアを選ぶ |
| RFS (Receive Flow Steering) | アプリケーションが動作するコアを探す |
| RSS (Receive Side Scaling) | NIC が提供する RPS のハードウェア版 |
| aRFS (accelerated RFS) | NIC が提供する RFS のハードウェア版 |
(Table 2. Receiver-side flow steering techniques。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Table 2.)
## 実験結果
### 単一の長フロー
10〜40 Gbps のリンクでは単一スレッドで線速に到達できたが、100 Gbps では最適化をすべて有効にしてもスループット毎コアは約 42 Gbps となり、単一コアでは線速に届かない。
NIC の TSO/GRO、ジャンボフレーム、aRFS を順に有効化すると、`skb` あたりの処理量を増やし CPU 効率を改善できる。
送信側では TSO が TCP/IP と netdevice の処理を減らし、受信側では GRO が `skb` の数を減らす。
ただし受信側は、NIC から DMA されたデータのコピーと `skb` の確保・解放が残るため、常に送信側より CPU 使用率が高い。
![[_attachments/Understanding_host_network_stack_overheads/fig03-single-flow-performance.png]]
(Figure 3。単一フローでの最適化、送受信 CPU 使用率、CPU サイクル内訳、キャッシュミス率、TCP 受信バッファサイズと NAPI からコピー開始までの遅延を示す。受信側ではデータコピーが最大の消費要因になる。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 3.)
全最適化後の受信側では、データコピーが CPU 使用率の約 49% を占める。
一方、aRFS を無効にすると NIC からリモート NUMA メモリへ DMA され、データコピーのコストが増える。
aRFS はアプリケーションを NIC に近い NUMA ノードへ寄せるが、単一フローでもキャッシュミス率は約 49% に達した。
NIC の受信ディスクリプタ数または TCP 受信バッファを増やすとキャッシュミス率が上がり、スループットが下がる。
実験環境では DCA が利用できるキャッシュ容量は L3 の約 18%、約 3 MB であり、TCP 受信バッファが 1600 KB を超えると NAPI 処理からコピー開始までの遅延が急増した。
これは、増大した帯域遅延積のデータをアプリケーションがコピーする前に、NIC による後続 DMA がキャッシュ上のデータを追い出すためである。
NIC からリモート NUMA ノードで動作するアプリケーションへデータを渡すと、スループット毎コアは NIC ローカル NUMA の場合より約 20%低下し、キャッシュミス率も増加する。
![[_attachments/Understanding_host_network_stack_overheads/fig04-numa-locality.png]]
(Figure 4。NIC ローカル NUMA とリモート NUMA の比較。リモート NUMA ではスループット毎コアが約 20%低下し、受信側キャッシュミス率が増える。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 4.)
### 複数フローによる資源競合
one-to-one では、フロー数が 8 でリンクが飽和するにもかかわらず、フロー数を 24 まで増やすとスループット毎コアは約 64%低下して約 15 Gbps になる。
受信側では、ネットワーク飽和によるアプリケーションスレッドの待機と再開がスケジューリングオーバーヘッドを増やす一方、ページを再利用しやすくなるためメモリ管理オーバーヘッドは低下する。
![[_attachments/Understanding_host_network_stack_overheads/fig05-one-to-one-performance.png]]
(Figure 5。one-to-one 通信でフロー数を増やした場合のスループット毎コア、送信側 CPU 内訳、受信側 CPU 内訳を示す。リンク飽和後もフロー数増加によりスループット毎コアが下がる。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 5.)
incast では、複数の送信コアから一つの受信コアへ通信を集中させる。
フロー数を 1 から 8 に増やすと、同じ L3 キャッシュを共有するアプリケーション間の競合によりキャッシュミス率が 48% から 78% へ上昇し、スループット毎コアが約 19%低下する。
CPU 内訳の構成自体はフロー数に対して大きく変わらず、データコピーの 1 バイトあたりコストの増加が性能低下を説明する。
![[_attachments/Understanding_host_network_stack_overheads/fig06-incast-performance.png]]
(Figure 6。incast 通信でフロー数を増やした場合のスループット毎コア、受信側 CPU 内訳、L3 キャッシュミス率を示す。フロー数の増加に伴いキャッシュミス率が上がり、スループット毎コアが下がる。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 6.)
TCP は送信側主導のプロトコルであるため、受信側は一つのコアで処理するアクティブフロー数を制御できない。
著者らは、受信側主導のトランスポートプロトコルが、受信側の CPU とキャッシュ競合を調整する余地を持つと論じる。
### 送信側競合と全対全通信
outcast では、一つの送信コアが複数の受信コアへフローを送る。
送信側はフロー数が 1 から 8 へ増えると、TSO と aRFS の効果でスループット毎送信コアが上がり、最大約 89 Gbps に達する。
これは incast の受信側ボトルネックに対するスループット毎コアの約 2.1 倍であり、送信側処理経路が受信側より CPU 効率に優れることを示す。
![[_attachments/Understanding_host_network_stack_overheads/fig07-outcast-performance.png]]
(Figure 7。outcast 通信でフロー数を増やした場合の送信コアあたりスループット、送信側 CPU 内訳、CPU 使用率を示す。送信側スループットは 8 フロー付近で最大になり、その後は低下する。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 7.)
all-to-all では、最大 24×24 フローが全送信コアと全受信コアの間で通信する。
1×1 から 24×24 へ増やすと、スループット毎コアは約 67%低下する。
多数のフローが一つの資源を共有すると、GRO が一つのフローから十分なパケットを集約できず、受信側へ渡される `skb` が小さくなる。
その結果、TCP/IP 処理の 1 バイトあたりオーバーヘッドが増加し、スケジューリングオーバーヘッドも増える。
![[_attachments/Understanding_host_network_stack_overheads/fig08-all-to-all-performance.png]]
(Figure 8。all-to-all 通信でフロー数を増やした場合のスループット毎コア、受信側 CPU 内訳、GRO 後の `skb` サイズ分布を示す。フロー数増加により 64 KB の `skb` が減少する。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 8.)
### パケット損失とフローサイズ
単一フローにランダムなパケット損失を導入すると、損失率 0 から 0.015 でスループット毎コアは約 24%低下する。
受信側では重複 ACK の生成により ACK 処理の CPU 割合が 1.52% から 7.4%へ約 4.87 倍になる。
送信側では ACK 処理と再送、輻輳制御により CPU オーバーヘッドが増え、損失の影響は受信側より大きく現れる。
![[_attachments/Understanding_host_network_stack_overheads/fig09-packet-drop-impact.png]]
(Figure 9。パケット損失率を増やした場合のスループット毎コア、CPU 使用率、送受信側 CPU 内訳を示す。損失率の増加により TCP/IP と netdevice の処理が増える。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 9.)
短フローでは 16:1 incast を使い、RPC サイズを 4〜64 KB へ変化させた。
4 KB のような極端に短いフローでは、GRO の集約機会が少ないため TCP/IP 処理とスケジューリングが相対的に大きく、データコピーはまだ支配的ではない。
一方、16 KB 程度へ増えるとデータコピーが主要な消費要因になり、64 KB では長フローと似た CPU 内訳になる。
短フローでは NIC リモート NUMA に置いても長フローほどスループットが低下せず、長フローを NIC ローカル NUMA へ、短フローをリモート NUMA へ配置する資源配分の余地がある。
![[_attachments/Understanding_host_network_stack_overheads/fig10-short-flow-performance.png]]
(Figure 10。短フローの RPC サイズを変化させた場合のスループット毎コア、サーバー CPU 内訳、NIC リモート NUMA の影響を示す。小さい RPC では GRO の効果が限定的である。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 10.)
長フローと短フローを同じコアへ混在させると、短フロー数を 0 から 16 へ増やしたとき、全体のスループット毎コアは約 43%低下する。
長フローは単独時の約 42 Gbps から混在時の約 20 Gbpsへ、短フローは単独時の約 6.15 Gbps から約 2.6 Gbpsへ低下した。
長短フローは異なる CPU ボトルネックを持つにもかかわらず同じ固定処理経路を共有するため、同じコアへ混在させないことが CPU 効率向上につながる。
![[_attachments/Understanding_host_network_stack_overheads/fig11-mixed-flow-performance.png]]
(Figure 11。1 本の長フローへ短フローを同じコアで追加した場合のスループット毎コアとサーバー CPU 内訳を示す。短フローの増加によりデータコピー以外に TCP/IP 処理とスケジューリングの割合も増える。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 11.)
### DCA、IOMMU、輻輳制御
DDIO に代表される DCA を無効にすると、単一フローのスループット毎コアは既定条件から 19%低下し、aRFS の効果は約 50%減少する。
IOMMU を有効にするとスループット毎コアは 26%低下し、NIC DMA 用ページのデバイスページテーブルへの登録と DMA 後のアンマップにより、受信側のメモリ管理が CPU サイクルの 30%を占める。
![[_attachments/Understanding_host_network_stack_overheads/fig12-dca-iommu-impact.png]]
(Figure 12。DCA を無効化した場合と IOMMU を有効化した場合のスループット毎コア、送信側 CPU 内訳、受信側 CPU 内訳を比較する。IOMMU はメモリ管理オーバーヘッドを増やす。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 12.)
TCP CUBIC、BBR、DCTCP の選択は、受信側 CPU がボトルネックになるこの実験ではスループット毎コアに大きな差を生じさせなかった。
3 方式はいずれも送信側主導であり、受信側処理の差が小さいためである。
ただし BBR は qdisc によるペーシングと繰り返しのスレッド起床により、送信側のスケジューリングオーバーヘッドが高かった。
![[_attachments/Understanding_host_network_stack_overheads/fig13-congestion-control-impact.png]]
(Figure 13。CUBIC、BBR、DCTCP のスループット毎コアと送受信側 CPU 内訳を比較する。スループットはほぼ同じだが、BBR は送信側スケジューリングの割合が高い。Source: [[.raw/papers/Understanding_host_network_stack_overheads.pdf]], Figure 13.)
## 設計上の含意
### ゼロコピーの受信側への拡張
送信側では `MSG_ZEROCOPY` がアプリケーションバッファを pin し、NIC が DMA read で直接取得できる。
受信側では TCP ソケットの `mmap` 拡張により、NIC が DMA した物理アドレスへ対応する仮想アドレスをアプリケーションへ渡せる。
ただし受信側ゼロコピーはメモリ管理の意味論を変えるため、アプリケーションの変更が必要になる。
AF_XDP はゼロコピーを提供するが、ユーザー空間でネットワーク・トランスポートプロトコルを再実装する必要がある。
したがって、既存の TCP/IP スタックを保ったままアプリケーション変更を最小化する受信側ゼロコピーが重要な未解決課題になる。
(Source: [[.raw/papers/Understanding_host_network_stack_overheads.txt]] §4)
### ホスト資源を認識するトランスポート
従来のトランスポート設計は、レイテンシーやスループットを目的にネットワーク資源を調整してきた。
高帯域リンクでは、利用可能なコア数、キャッシュ容量、DCA 能力、NUMA の位置も同時に調整すべき資源になる。
受信側主導プロトコルは、受信側のアクティブフロー数や処理量を制御し、ホスト資源を含むフロー制御へ拡張できる可能性がある。
### 動的かつアプリケーション認識型のスタック
既存スタックでは、バッファ、プロトコル処理、ホスト資源の割り当てがソケット作成時に決まり、接続の寿命中はほぼ変わらない。
しかし、長フローと短フローでは支配的なボトルネックが異なり、新しいフローの到着によって競合も変化する。
将来のスタックは、接続単位でホスト資源を動的に拡張し、アプリケーション特性と同一ホスト上の他接続を認識して処理経路を選ぶ必要がある。
CPU スケジューラもネットワーク層から独立して設計するのではなく、長フローを NIC ローカル NUMA ノードへ配置し、長短フローを別コアへ分離するなど、ネットワーク認識型の協調設計が求められる。
## 強み / 弱点・課題
### 強み
- 100 Gbps リンクで、単一フローだけでなく 5 通信パターン、長短フロー混在、パケット損失、DCA、IOMMU、輻輳制御まで同じ CPU 分類で比較した点
- 受信側のデータコピー、帯域遅延積と DCA キャッシュ容量の不釣り合い、フロー多重化による GRO 集約機会の減少を、CPU 内訳・キャッシュミス率・スループット毎コアで結び付けた点
- Linux の各層に対する計装コードと再現用スクリプトを公開し、測定の再現可能性を確保した点
- 将来技術の優劣を断定せず、カーネル最適化、ゼロコピー、ユーザー空間スタック、ハードウェアオフロード、スケジューラ協調を同じボトルネック分析から位置づけた点
### 弱点・課題
- Linux kernel 5.4.43、特定の Intel Xeon Gold 6128、ConnectX-5 Ex、100 Gbps 直結という一つのスタック・ハードウェア構成に依存する
- 主な評価指標は CPU 使用率とスループットであり、ネットワークトポロジ、スイッチ輻輳、エンドツーエンドレイテンシーのボトルネックは対象外である
- Linux カーネルは急速に進化するため、後続版で同じボトルネックがどの程度残るかは版ごとの再測定が必要である
- `iPerf` と `netperf` はアプリケーション処理を最小化しており、複雑な実アプリケーションの計算・メモリ・I/O との相互作用までは評価していない
## 横断的な位置づけ
本稿は、[[netmap]] やユーザー空間 TCP スタックのようにカーネルを迂回する方式そのものを提案するのではなく、成熟した Linux カーネルスタックを高帯域リンクで測定し、どのオーバーヘッドが次のボトルネックになるかを示す基準線である。
[[ゼロコピーネットワーキング]] が整理するカーネル・ユーザー空間コピーの削減は、本稿の受信側データコピー約 49%という観察に直接対応する。
[[ユーザーレベルTCPスタック]] や [[カーネルバイパスネットワーキング]] は TCP/IP 処理とシステムコールの削減に強いが、本稿が示す受信側キャッシュ競合、NUMA 配置、長短フロー混在への対処には、ゼロコピー以外の資源オーケストレーションも必要になる。
[[TCP IPスタック統合|TCP/IPスタック統合]] の観点では、iip が処理ループと CPU コア割り当てを統合側へ開放した動機を、高帯域 Linux スタックの測定結果から裏づける。
(Source: [[.raw/papers/Understanding_host_network_stack_overheads.txt]] §3–§5)
## 出典
- [[.raw/papers/Understanding_host_network_stack_overheads.pdf]]
- [[.raw/papers/Understanding_host_network_stack_overheads.txt]]
- [ACM DOI](https://doi.org/10.1145/3452296.3472888)
- [再現用コード](https://github.com/Terabit-Ethernet/terabit-network-stack-profiling)