Linuxネットワーク性能 究極ガイド
(ntk148v.github.io)- Linuxのネットワーク性能チューニングは、パケットが NICリングバッファ、IRQ、NAPI、softIRQ、qdisc、TCPバッファを経てアプリケーションソケットに到達する流れを、ボトルネック基準で解釈する作業である
- 受信経路では、NICがDMAでパケットをRAMに書き込み、HardIRQを発生させ、ドライバがNAPIを予約して
NET_RX_SOFTIRQでリングバッファを空にした後、IP/TCP層とソケット受信バッファへ渡す ethtool、/proc/net/softnet_stat、ss、netstat、sysctlは観察と調整の出発点であり、割り込み統合、IRQ affinity、RSS/RPS/RFS/aRFS、netdev_budget、netdev_max_backlog、txqueuelen、TCP read/writeバッファが主要な軸である- すべてのシステムに通用する単一の設定はなく、リングバッファの増加はドロップを減らせる一方で遅延を増やし、割り込み統合はCPU使用率とHardIRQを減らす代わりに遅延コストを生む可能性がある
- 高性能パケット処理は
PACKET_MMAP、DPDK、PF_RING、XDP/AF_XDP のような選択肢へ拡張できるが、カーネルバイパス、ゼロコピー、カーネル内高速パスごとにハードウェア依存性、CPU占有、カーネルバージョン要件が異なる
Linux受信経路: NICからソケットまで
- ネットワークデバイスはパケット到着を知らせるために IRQ を発生させ、LinuxのIRQマッピングは
/proc/interruptsに保存される - IRQハンドラは非常に高い優先度で実行され、追加のIRQ発生を防ぐ場合があるため、ドライバは長時間かかる作業をIRQコンテキスト外へ先送りする
- この遅延処理には softIRQ が使われ、ネットワーク受信処理ではCPUごとに
ksoftirqd/<cpu-number>カーネルスレッド、softnet_data、poll_listが作られる net_dev_initはNET_RX_SOFTIRQをsoftIRQシステムに登録し、そのハンドラはnet_rx_actionである-
パケット到着とNAPI処理
- NICはネットワークから受け取ったデータを DMA でRAM上のリングバッファに記録する
- 一部のNICは複数のリングバッファを持つ multiqueue NICである
- NICがHardIRQを発生させると、ドライバのIRQハンドラが実行される
- ドライバはNICのIRQをクリアし、
napi_scheduleを呼び出してNAPI softIRQ pollループを開始する napi_scheduleはドライバのNAPI poll構造体を現在のCPUのpoll_listに追加し、softIRQ pending bitを設定するksoftirqdが__do_softirqを呼び出すと、pending状態のNET_RX_SOFTIRQのハンドラnet_rx_actionが実行される
-
GROとプロトコルスタックへの進入
net_rx_actionはNAPI poll listを確認し、budgetと経過時間を検査してsoftIRQがCPUを独占しないようにする- ドライバの
poll関数はRAM上のリングバッファからパケットを回収する - パケットは
napi_gro_receiveに渡される - GRO(Generic Receive Offloading) は小さなパケットを大きなパケットへ再構成し、アプリケーションが処理すべきパケット数を減らすソフトウェアベースのオフロード技術である
- GROがパケットを保留しない場合、
netif_receive_skbを通じてプロトコルスタック上位へ進む
-
RPS有効化の有無による分岐
- RPS が無効な場合:
netif_receive_skbがデータを__netif_receive_coreに渡す__netif_receive_coreはtapと登録済みプロトコル層ハンドラへデータを渡す
- RPS が有効な場合:
netif_receive_skbがデータをenqueue_to_backlogに渡す- パケットはCPUごとのinput queueに入る
- リモートCPUのNAPI構造体がそのCPUの
poll_listに追加され、IPIがキューイングされてリモートCPUのsoftIRQスレッドを起こす - リモートCPUの
ksoftirqdはprocess_backlogpoll関数でCPU input queueからパケットを回収する
- RPS が無効な場合:
-
IP、TCP、ソケット受信バッファ
- パケットはIPv4層で
ip_rcvにより受信され、netfilterとルーティング最適化を経る - 現在のシステム宛てのデータはUDPやTCPのような上位プロトコル層へ渡される
- TCP受信経路では、
tcp_v4_rcv、TCP finite state machine、ソケット探索を経て受信バッファに入る - 受信バッファサイズは
tcp_rmemのルールに従う tcp_moderate_rcvbufが有効なら、カーネルが受信バッファを自動調整するtcp_rmemはTCPソケット受信バッファの最小値、デフォルト値、最大値を含むSO_RCVBUFを使うと、そのソケットの受信バッファ自動調整は無効化されるnet.core.rmem_maxはTCP受信バッファサイズの上限であり、より大きなウィンドウはACK送信前により多くのデータを送れるため、遅延を減らしスループットを高められる
- パケットはIPv4層で
Linux送信経路: アプリケーションからNICまで
- 送信経路は受信より単純だが、qdisc、TCP write buffer、DMA、IRQが関与する
- アプリケーションが
sendmsgのような呼び出しでメッセージを送ると、TCP送信経路がskb_buffを割り当てる - パケットは
tcp_wmemサイズのソケットwrite bufferに入るtcp_wmemはTCPソケット送信バッファの最小値、デフォルト値、最大値を含む- カーネルはTCP送信バッファサイズを最小値と最大値の間で動的に調整する
SO_SNDBUFを使うと、そのソケットの送信バッファ自動調整は無効化されるnet.core.wmem_maxはTCP送信バッファサイズの上限である
- TCPヘッダとIPヘッダが作られ、
LOCAL_OUT、ルーティング、POST_ROUTING、fragmentationを経た後、dev_queue_xmitでL2送信関数が呼び出される - 出力qdiscは
txqueuelenの長さとdefault_qdiscアルゴリズムを使用する - ドライバはパケットをTXリングバッファに入れ、
tx-usecstimeout またはtx-framesの後にNET_TX_SOFTIRQを実行する - NICはDMAでRAMからパケットを取得して送信し、送信完了後にHardIRQを発生させる
- ドライバはこのIRQを処理し、NAPI pollシステムを予約してRAMを解放する
観測ツールと基本的な確認ポイント
-
/proc/net/softnet_stat/proc/net/softnet_statの各行は1つの CPU コアを表し、CPU0 から始まる- 各カラムの統計は 16進数 で提供される
- 1番目のカラムは割り込みハンドラが受信したフレーム数である
- 2番目のカラムは
netdev_max_backlog超過によりドロップされたフレーム数である - 3番目のカラムは処理すべき作業が残っているにもかかわらず、
ksoftirqdがnetdev_budgetまたは CPU 時間を使い切った回数である - 残りのカラムは Linux のバージョンによって異なる場合がある
-
/proc/net/sockstatとss/proc/net/sockstatではmemフィールドを確認する- この値はすべてのソケットの
sk_buff->truesizeを合算して計算される ssはソケット統計をダンプするツールで、netstatと似た情報に加え、より多くの TCP および状態情報を表示できるss -tmは TCP ソケットのメモリ使用量確認に使うrmem_alloc: 受信パケットに割り当てられたメモリrcv_buf: 受信パケットに割り当て可能な総メモリwmem_alloc: レイヤー 3 にすでに渡された送信パケットに使われたメモリsnd_buf: 送信パケットに割り当て可能な総メモリwmem_queued: まだレイヤー 3 に渡されていない送信パケットに割り当てられたメモリsock_drop: ソケットへ demultiplex される前にドロップされたパケット数
-
netstatとsysctlnetstatは開いているネットワーク接続とプロトコルスタック統計を出力するコマンドラインツールであり、/proc/net/ファイルシステムから情報を取得する/proc/net/dev: デバイス情報/proc/net/tcp: TCP ソケット情報/proc/net/unix: Unix domain socket 情報sysctlは/procファイルシステムに直接値を書き込む代わりに、システム・ネットワーク設定を変更するコマンドであるsysctl -w variable=valueは一時的な変更に使い、永続的な変更は/etc/sysctl.confを修正した後、sysctl -pで適用する
NIC リングバッファと割り込み調整
-
NIC リングバッファ
- RX リングバッファは RAM 上にある固定サイズの FIFO 循環バッファ である
- リングバッファ自体はパケットデータを保持せず、DMA により RAM に入った
skbを指す descriptor を保持する - ドロップや overrun が見られる場合はキューサイズを増やせるが、副作用として遅延が増える可能性がある
- 多くの場合、受信バッファサイズを増やすだけでパケットドロップを防げ、カーネルがバッファを空にする時間をもう少し確保できる
- 確認と変更は
ethtoolで行う ethtool -g eth3: 現在の RX/TX リングサイズと最大値を確認ethtool -G eth3 rx 8192 tx 8192: RX/TX バッファを最大値まで増加ethtool -S eth3とerr,drop,over,miss,timeout,reset,collisなどのカウンタで監視する
-
ハードウェア割り込みコアレッシング
- NIC は
rx-usecstimeout またはrx-frames条件まで RX リングバッファにパケット reference を蓄積した後に HardIRQ を発生させることがあり、これを Interrupt coalescence と呼ぶ - 早すぎる頻度で割り込むと、カーネルが実行中の作業を頻繁に中断するためシステム性能が悪化する
- 遅すぎる頻度で割り込むと、NIC からトラフィックを十分な速さで吐き出せず、上書きやトラフィック損失が発生する可能性がある
- 割り込みコアレッシングの調整は CPU 使用量と HardIRQ を減らし、スループットを高められるが、遅延コストが発生する場合がある
ethtool -c eth3で coalesce パラメータを確認し、ethtool -C eth3 adaptive-rx off rx-usecs 0 rx-frames 0のように変更できる- adaptive mode では、カードがトラフィックパターンとカーネルの受信パターンを見て coalescing 設定を動的に推定する
- NIC は
IRQ affinity と CPU 間の負荷分散
-
IRQ affinity
- IRQ には、その IRQ の ISR を実行できる CPU コアを定義する
smp_affinity属性がある - 割り込み affinity とアプリケーションスレッド affinity を特定の CPU コアに合わせると、キャッシュライン共有によりアプリケーション性能が向上することがある
- デフォルトでは
irqbalanceデーモンが制御する - 手動調整の前には
irqbalanceを停止する必要がある /proc/irq/<IRQ_NUMBER>/smp_affinityには CPU コアを表す 16進数のビットマスクが保存される- 4 コアサーバーでのデフォルト値
fは、すべての CPU で IRQ を処理できることを意味する echo 1 > /proc/irq/32/smp_affinityは CPU0 のみを使うようにする- 32 コアを超えるシステムでは、comma で 32-bit group を区切る
- IRQ affinity は非常に限定的な構成と事前定義されたワークロードでのみ性能を高められ、諸刃の剣 になり得る
- IRQ には、その IRQ の ISR を実行できる CPU コアを定義する
-
RSS
- 高速な NIC で単一キューと単一 CPU だけでパケットを受信すると、1 つのコアがデータ処理の責任をすべて負い、ほかのコアがアイドル状態になることがある
- RSS(Receive-side scaling) は、NIC が複数の送受信キューにトラフィックを分散するための NIC 技術である
- NIC は source/destination IP と TCP/UDP source/destination port に基づいてハッシュを計算し、同じ flow のパケットを単一キューに割り当てつつ、flow を各キューへ均等に分散する
- RSS はマルチプロセッシング環境で並列受信処理の利点を提供する
- Linux kernel のドキュメントによれば、RSS は遅延が重要であるか、受信割り込み処理がボトルネックである場合に有効化すべきであり、低遅延ネットワーキングではシステムの CPU 数だけキューを割り当てる設定が最適である
-
RPS, RFS, aRFS
- RPS(Receive Packet Steering) は RSS のソフトウェア実装に近い
- RSS がハードウェア割り込みハンドラを実行するキューと CPU を選ぶのに対し、RPS は割り込みハンドラの上位でプロトコル処理を実行する CPU を選ぶ
CONFIG_RPSが必要で、SMP ではデフォルトで有効化されている- 設定は
/sys/class/net/<dev>/queues/rx-<n>/rps_cpusの CPU bitmap で行う - RSS があれば不要な場合もあるが、CPU 数がキュー数より多い場合には有用なことがある
- RFS(Receive Flow Steering) は RPS をアプリケーション locality まで拡張したものだ
- RPS は flow ベースでパケットを分散するが、ユーザー空間アプリケーションがどの CPU で実行されているかは考慮しない
- RFS は global flow-to-CPU table である
rps_sock_flow_tableを維持する net.core.rps_sock_flow_entriesでテーブルサイズを調整できる- per-queue
rps_dev_flow_tableは、スケジューラがアプリケーションを新しい CPU に移した際に、残っているパケットのせいで順序が乱れる問題を減らすために使われる - aRFS(Accelerated RFS) は RFS 向けのハードウェアアクセラレーション負荷分散メカニズムである
- パケットをデータを消費するスレッドに近い CPU に直接送るため、RFS より高い性能を出せる可能性がある
- NIC の
ndo_rx_flow_steer、ntuplefiltering、CONFIG_RFS_ACCELが必要である - CPU とキューのマッピングは各受信キューの IRQ affinity から自動的に導出されるため、追加設定は不要である
softIRQ、qdisc、TCPバッファのチューニング
-
softIRQ の予算
- NAPI polling ルーチンは
netdev_budget_usecs、netdev_budget、dev_weightによって制限され、softIRQ が CPU を占有しないようにしている net.core.netdev_budgetのデフォルト値は 300 で、softIRQ プロセスが CPU から降りる前に NIC から 300 個のメッセージを空にすることを意味するnet.core.netdev_budget_usecsは 1 回の NAPI polling cycle の最大マイクロ秒数であるnet.core.dev_weightは NAPI interrupt において、カーネルが処理できる CPU ごとの最大パケット数である/proc/net/softnet_statで 1 列目以外の列が増加している場合、budget の変更が必要な可能性があり、小さな増加は正常なこともある
- NAPI polling ルーチンは
-
ingress qdisc と
netdev_max_backlognetdev_max_backlogは、NIC で受信された後、IP/TCP のようなプロトコルスタックで処理される前のトラフィックが保存されるカーネル内部キューである- CPU コアごとに 1 つの backlog queue がある
- インターフェースがカーネルが処理できる速度よりも速くパケットを受信すると、INPUT 側キューが
netdev_max_backlogまで埋まり、超過するとドロップされる - デフォルト値は 1000 で、複数の 1Gbps インターフェースや単一の 10Gbps インターフェースでは不足する場合がある
/proc/net/softnet_statの 2 列目は、backlog queue overflow によって増加するカウンタである- 値の変更は
sysctl -w net.core.netdev_max_backlog <value>で行う
-
egress qdisc、
txqueuelen、default_qdisctxqueuelenは、ネットワークインターフェースデバイスのカーネル transmit queue に許可されるパケット数を設定する- デフォルト値は、インターフェースドライバによっては 1000 の場合がある
ifconfig <interface> txqueuelen valueで変更し、ip -s linkで RX/TX dropped を確認するdefault_qdiscは、ネットワークデバイスに使用するデフォルトの queuing discipline であるpfifo_fastの代わりにsfq、codel、fq_codelのような代替を指定できるtc -s qdisc ls dev <interface>で dropped、overlimits、requeues のような指標を確認する
-
TCP read/write buffer と接続キュー
tcp_rmemとtcp_wmemは、それぞれ TCP 受信・送信バッファの最小値、デフォルト値、最大値を定義する- 変更は次のように行う
sysctl -w net.ipv4.tcp_rmem="min default max"sysctl -w net.ipv4.tcp_wmem="min default max"
/proc/net/sockstatでバッファ使用状況を確認する- Accept queue と SYN queue は
net.core.somaxconnとnet.ipv4.tcp_max_syn_backlogの影響を受ける net.core.somaxconnはlisten()の backlog パラメータの上限であり、この値を変更する場合はアプリケーション側も互換性のある値に変更する必要があるnet.ipv4.tcp_syncookiesは、SYN flood 攻撃の防御に有用な SYN cookie を有効または無効にするnet.ipv4.tcp_congestion_controlは、新しい接続に使用する congestion control アルゴリズムを設定する
NUMA とネットワーク性能
- NUMA(Non-uniform memory access) は、プロセッサが non-local memory よりも local memory により高速にアクセスできるメモリアーキテクチャである
- ネットワーク処理では CPU がリングバッファメモリにアクセスする必要があるため、NUMA locality はネットワーク性能に影響する可能性がある
- NUMA は CPU、メモリ、デバイスを複数の node に分割し、高速 interconnect と共通 OS を備えた複数の小さなコンピュータのように動作する
- NUMA システムでは、デバイスが属する単一 node の CPU コアにそのデバイスの割り込みを集めることがチューニング目標となる
- ただし NUMA システムはリアルタイムアプリケーションと相性が悪く、予期しないイベント遅延を引き起こすことがある
- NUMA node は
/sys/devices/system/node/node*で確認する - デバイス locality は
/sys/class/net/<interface>/device/numa_nodeで確認する-1は、ハードウェアプラットフォームが実際には NUMA でないか、カーネルが NUMA をエミュレートしている場合、またはデバイスに NUMA locality がない場合を意味する
- Linux kernel は 2.5 から NUMA をサポートしており、RedHat 系および Debian 系ディストリビューションは
numactl、numadを提供している numadは、システム topology とリソース使用量を監視し、十分に大きなメモリおよび CPU 負荷を持つプロセスを効率的な NUMA locality に配置しようとする
より高速なパケット処理の選択肢
-
AF_PACKETv4 とPACKET_MMAPAF_PACKET v4は Linux の高速なパケットインターフェースで、データパスから system call をなくし、基本的に copy-mode を使用するPACKET_ZEROCOPYを使うと、DMA packet buffer をユーザー空間にマッピングする true zero-copy モードを利用できる- 一般的な file read 後の socket send 経路では、user mode と kernel mode の間の context switch と複数回のデータ copy が必要になる
- zero-copy は重複したデータ copy を排除して性能を高める
PACKET_MMAPは高速な packet sniffing のための Linux API である- ユーザー空間とカーネルが共有する mmapped ring buffer を提供する
- 送受信パケットにおいて system call とユーザー空間・カーネル間の copy を減らす
-
DPDK
- DPDK(Data Plane Development Kit) は、高速なパケット処理のためのユーザー空間ライブラリとドライバーフレームワークである
- 目的は、NIC とユーザーアプリケーションの間でネットワークパケットを native speed でやり取りすることである
- 10Gb または 40Gb NIC を対象としており、速度が最も重要な基準である
- ネットワークスタックではなく、パケット forwarding に焦点を当てる
- DPDK が NIC を制御すると、すべてのトラフィックはカーネルを迂回し、その NIC はカーネルから見えなくなる
- ポートは Linux カーネルドライバーから unbind され、
vfio_pci、igb_uio、uio_pci_genericのようなドライバーで管理される - その後、アプリケーションと NIC の通信は DPDK PMD が担当する
- DPDK では大きなメモリ chunk を割り当てるために hugepages の設定が必要である
- 主な構成要素:
- EAL: 環境差異を隠蔽する generic interface
librte_ring: lockless multi-producer, multi-consumer FIFO APIlibrte_mempool: メモリオブジェクト pool の割り当てlibrte_mbuf: ネットワークパケットを格納する buffer の生成・操作librte_timer: 非同期関数実行のための timer service- PMD: interrupt の代わりに CPU が NIC を継続的に poll するドライバー
- 制約:
- ハードウェア依存性が大きい
- PMD 実行のために CPU core を専用に割り当てる必要があり、100% CPU を使用する
-
PF_RINGPF_RINGは、高速にパケットを処理し、パケット処理アプリケーションに一貫した API を提供する Linux kernel module と user-space framework であるPF_RINGは Linux NAPI により NIC からパケットを poll する- NAPI が NIC から
PF_RINGcircular buffer へパケットをコピーし、ユーザー空間アプリケーションが ring からパケットを読み取る - この構造にはアプリケーションと NAPI という 2 つの poller があり、polling に CPU cycle が使われる
- 利点は、incoming packet を複数の ring に同時に分散できる点である
- モジュール型の構造により、ZC module、FPGA-based card module、Stack module、Timeline module、Sysdig module などの追加コンポーネントを利用できる
PF_RINGはパケット capture と userland forwarding のコストを削減したが、カーネルが NIC から ring にコピーし、userland が ring から読み取って処理する 2 アクター構造のため、最適性能には限界があるPF_RINGは 7.5 からAF_XDPadapter のサポートを含む
-
XDP と
AF_XDP- XDP(eXpress Data Path) は、Linux ネットワークデータパスでパケットを早い段階で横取りする eBPF 実装である
- XDP は SKB 割り当て前、device driver の RX function 内部で RX packet page を直接処理する
- eBPF はカーネルで実行されるユーザー定義の sandboxed bytecode である
- 64-bit register 11 個と 512-byte stack を使用する
- LLVM backend により、C、Lua、Go、P4、Rust などから eBPF へコンパイルできる
- in-kernel verifier と JIT compiler を提供し、map、tail call、helper などの機能をサポートする
- XDP の利用例:
- DoS 緩和のための pre-stack filtering
- forwarding と load balancing
- GRO のような batching 手法
- flow sampling と monitoring
- ULP processing
- XDP は kernel bypass ではなく、カーネルスタック内の fast path であり、TCP/IP stack を置き換えるものではない
- 専用ハードウェアは不要だが、multi-queue NIC、TX/RX checksum offload、RSS、TSO などの要件がある
- DPDK と比べた XDP の利点:
- busy polling または interrupt driven networking を選択できる
- huge pages が不要
- 特殊なハードウェア要件がない
- 専用 CPU は必須ではない
- 3rd party userspace application からカーネルへパケットを再注入する必要がない
- ネットワークハードウェアアクセスのための新たな security model が不要
- 3rd party code/licensing が不要
AF_XDPは Linux 4.18 で導入された新しい socket タイプである- カーネルを完全に迂回せず、カーネル機能を活用して DPDK や
AF_PACKETと似た構造を作れる - XDP program が eBPF によって frame をユーザー空間 memory buffer へ redirect できる
- DMA transfer はユーザー空間メモリを使って zero-copy をサポートする
AF_PACKETと比べて 3〜20 倍の性能向上を達成できる- 制約:
- 比較的新しいプロジェクトである
- 完全なサポートには Linux kernel 5.4 以上が必要である
1件のコメント
Hacker Newsのコメント
数週間前に読めていたら本当に役立ったはずの資料。
データセンター間でL2リンク暗号化を行うため、複数ベンダーからハードウェアアプライアンスの見積もりを取ったが、高すぎると感じたので自作してみた。
汎用ハードウェアで、WireGuardオーバーレイネットワーク上にイーサネットフレームを載せて10Gbps出せるよう構成し、10日ほどの作業の末、最安の提案より約70%、最高額の提案より約95%安く実装できたが、細かいドキュメントの読み込みと多くの実験が必要だった。
この記事の内容で自分の理解が正しいか検証してみたいし、ざっと見ただけでも有望で包括的に思える。
調整可能な値がこれほど多いなら、自動チューニングソフトウェアを作る価値があるのではと思う。
ホワイトリストにあるパラメータをランダムに選び、許容範囲内で少し上げ下げして、しばらく性能を測定し、悪化したら戻し、改善したらさらに調整する、という勾配降下法に近いアプローチができそう。
興味深いが、ソフトウェアエンジニアとしては記事に出てくるコマンドを実際に実行する機会はほとんどない。
システムの大半は何らかのLinuxの縮小版コンテナで動いており、本番システムにはシェルアクセス権がなく、開発やQA環境は負荷などの面で本番と違いすぎるため、バグ再現もたいていあまり役に立たない。
結局、記事のコマンドを試せるのは個人のシステムを触るときくらいで、プラットフォームエンジニアとして働いているなら有用そう。
Kubernetesであまり気に入っていない点の一つがネットワーキングモデル。ネットワークカードが1枚しかないと想定し、アプリケーションはその下の層について知る必要がないほど単純だと前提している。
ネットワーキングモデル全体は、単純化と改善のために2020年代らしく大きく見直す余地がありそうだ。
net.core.wmem_maxはTCP送信バッファサイズの上限とされていて、net.ipv4.tcp_wmemもあるので、2つ気になることがある。net.core.wmem_maxと何が違うのか知りたい。net.core.wmem_maxは名前の通り最大値。net.ipv4.tcp_wmemは最小、デフォルト、最大の3つの値を持つトリプル値で、ここで指定する最大値は前者のnet.core.wmem_maxを超えられない。TCPはIPv4上で運ばれてもIPv6上で運ばれても同じであるべきプロトコル。
例: https://docs.redhat.com/en/documentation/red_hat_data_grid/7...
ここで少し欠けているのは、100Gbps超のスループットに対するデバッグとチューニング。
その規模でHTTPを提供すると、最初のボトルネックはたいていメモリ帯域幅で、kTLSが必要になることが多い。
AMD μProfのようなツールはデバッグに非常に有用で、カーネル空間とユーザー空間で正確に何が起きているかを理解するには、eBPFベースの継続的プロファイリングも役立つ。
かなり良さそう。これまでの自分のキャリアでは、性能が必要になるたびに、たいていカーネルバイパスから始めていた。