netpoll
Go の netpoll は、Go ランタイム(runtime)が実装するコア I/O 多重化メカニズムで、Go が簡潔な同期プログラミングモデル(接続ごとに 1 つの Goroutine)で高性能なネットワークサービスを実現できるようにします。その本質は、オペレーティングシステムの基盤となる I/O 多重化システムコール(Linux の epoll など)を Go の Goroutine スケジューラと深く統合することです。
Netpoll とネイティブ Go Net の比較
Goroutine の数
- Go Net:1 つの Goroutine に 1 つの接続
- NetPoll:1 つの Goroutine に複数の接続
Goroutine コンテキストスイッチングスケジューリングのプレッシャー
- Go Net:高並行時、スイッチングプレッシャーが大きい。GoNet は 1 接続につき 1 goroutine に対応するため、goroutine 数が多いとスイッチングが頻繁になります
- NetPoll:高並行時、スイッチングプレッシャーが小さい。NetPoll は複数の接続が 1 つの goroutine を共有するため、スイッチング回数が少なくなります
メモリ消費
- Go Net:メモリ消費は接続数に正比例し、高並行時に Goroutine 数の爆発によりメモリプレッシャーが発生する可能性があります
- NetPoll:事前にバッファを割り当て、バッファプールを作成します
トリガー方式
- Go Net:エッジトリガー ET を使用。1 回でデータを読み切り、実装はシンプルです
- NetPoll:レベルトリガー LT を使用。データバッファとして十分なサイズのメモリを事前に割り当てる必要がありません
カーネルとユーザー層のバッファプール共有、1 回のコピー削減のサポート
- Go Net:サポートしません
- NetPoll:サポートします。Buffer プールを管理してユーザーに直接渡し、1 回のコピーを削減します
適用シーン
- Go Net:低並行のシンプルなシーン
- NetPoll:高並行低遅延のシーン
netpoll の設計思路
基本アーキテクチャ ネイティブネットワークライブラリは epoll lt モードに基づいて開発されており、基本アーキテクチャは下図の通りです。

goroutine の使用 poll オブジェクトプールが存在し、各 poll オブジェクトには 1 つの epoll と、個別に対応する 1 つの goroutine があります。goroutine の数は poll オブジェクトの数と一致し、各 poll オブジェクトは複数のファイルディスクリプタ fd を監視できます。
IO 読み書きロジック
- 各 poll オブジェクトは 1 つの goroutine を開始して、現在の epoll 上の読み取り可能・書き込み可能などのイベントを継続的にポーリングします
- 各 epoll は複数の fd に関連付けられ、各 fd は 1 つの Buffer に関連付けられます
- fd から読み取り可能イベントが監視されると、データを Buffer に読み込みます
- Buffer のデータを継続的にポーリングして処理し、データの読み取りが完了したことを検出すると、後続の処理ロジックのゴルーチンプール GoPoll に実行を通知します
- カーネルとのシステムコールのやり取りは完全に netpoll によって制御され、ユーザー層の Conn に対する読み書きは共用の Buffer を操作するだけです
優劣点 優点:より高い高並行サポート能力
- (1) ポーリングにより Buffer のデータを処理するため、データが処理されない状況は発生しません
- (2) 1 つの goroutine が複数の接続に対応するため、接続が非常に多くても、リソーススケジューリングとスイッチングのオーバーヘッドは大きくありません
- (3) ユーザーとカーネルのバッファ共有をサポートし、データコピー操作を 1 回削減して効率を向上させます
劣点:より多くのメモリを占有する必要があります
