10GbE NICで3Gbpsくらいの常時データをUDPで受信する事があったので、
その際に必要だったことについてまとめた。
1.ノンブロッキングソケットに設定する
2.受信バッファの拡張
3.ドライバ側のパラメータ設定
4.スレッドのプライオリティを高くする
ノンブロッキングソケットに設定する
ブロッキングソケットで受信する場合、以下のように連続受信すると、recvfromでほぼ毎回(多くの場合、パケットバッファは空なので)、スレッドがパケット到着まで待機状態になる。そのため、オーバヘッドが大きくなり、パケットを取りこぼすことがあるようだ。
while(1){
len = recvfrom(...); //パケットを受信
if(len!=sizeof(buf)){
continue; //パケットがなかった場合
}
copy_pkt(...);//パケットを保存
}
ノンブロッキングソケットで受信すれば、基本的にrecvfromでスレッドが待機状態にならないので、パケット取りこぼしがなくなった。
*その代り、CPU負荷は高くなった。
ioctlsocketでブロッキング/ノンブロッキングを切り替え可能
以下MSDNのサンプル抜粋
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms738573(v=vs.85).aspx
u_long iMode = 1; //iMode==0:blocking, iMode!=0:non-blocking
int iResult = ioctlsocket(m_socket, FIONBIO, &iMode);
if (iResult != NO_ERROR)
printf("ioctlsocket failed with error: %ld\n", iResult);
}
受信バッファの拡張
受信バッファが少ないと、OSによるスレッド切り替えが行われて受信できない間に、バッファが溢れる可能性が高くなるため、setsocketoptでネットワーク受信バッファを拡張する。
int buffSize = (1024 * 1024) * 512; //512MB
ret = ::setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (const char *)&buffSize, sizeof(int));
if (ret != 0) {
TRACE(_T("割り当て失敗"));
}
ドライバ側パラメータ設定
NICのパラメータを調整する。
ネットワークアダプタを右クリックして、起動したダイアログの構成ボタンをクリックする。
さらにダイアログが起動するので、詳細設定タブを選択すると、受信バッファ等の値を変更できるので、受信バッファを最大にする。
スレッドのプライオリティを高くする。
受信スレッドを、THREAD_PRIORITY_HIGHEST等に設定して,OSにより実行中にスレッドを切り替えられにくくする。