【Linux】RPS,RFSでCPU負荷を分散してネットワークパフォーマンスを向上する方法

Linuxには「Receive Side Scaling (RSS)」、「Receive Packet Steering (RPS)」、「Receive Flow Steering (RFS)」と呼ばれるネットワーク処理のCPU負荷を分散する仕組みがあります。

RSSは高級なネットワークインターフェースカード(NIC)に搭載されているマルチキューと呼ばれるもので、各キューをそれぞれCPUに割り当ててハードウェア的に負荷を分散する方法です。

RPSはNICのハードウェアに依存せずにソフトウェア割り込みの処理を各CPUに分散処理させてソフトウェア的にカーネルの仕組みでRSSのように負荷分散を行う方法です。

RFSは同一フローでパケットキューとアプリケーションが同じCPUを共有しCPUキャッシュなど分散処理の効率が良くなるようにRPSを拡張したものになります。

RPSやRFSを設定してCPU負荷を分散する方法を書いていこうと思います。

RPSの設定

RPSを有効にするためには以下の場所に値を書き込みます。

# 受信キュー用
/sys/class/net/ethX/queues/rx-N/rps_cpus
# 送信キュー用
/sys/class/net/ethX/queues/tx-N/xps_cpus

ethXの部分は対象のインターフェース名です。

rx-Nやtx-NのNは単一キューの場合は通常0ですが、複数キューを持ったNICの場合は0に加えて1以上のものが存在するかもしれません。

rps_cpusやxps_cpusに書き込む値は下位ビットからCPU0~にそれぞれ対応したビット列のフラグになっています。

CPU0とCPU1に割り当てたい場合は2進数で11となりますので、値は3を書き込みます。

CPU1だけに割り当てたい場合は10で値は2となります。

# eth0の受信処理をCPU0 eth1をCPU1に割り当てる例
echo 1 > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 2 > /sys/class/net/eth1/queues/rx-0/rps_cpus
# eth0の受信処理をCPU0,CPU1,CPU2,CPU3に割り当てて分散処理する方法
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
# eth0の受信処理をCPU0,CPU2 eth1の受信処理をCPU1,CPU3に割り当てて分散処理する方法
echo 5 > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo a > /sys/class/net/eth1/queues/rx-0/rps_cpus

CPU数以上の設定になる値は書き込めないようになっています。

例えばCPUが2つしか認識されていない環境だと4以上の値を書き込もうとすると「Value too large for defined data type」のようなエラーとなり、3までの値しか書き込めないようになっています。

RFSの設定

RFSを有効にするためには以下の場所に値を書き込みます。

# システム全体でRFSに許される最大フロー数のカーネル制限値
/proc/sys/net/core/rps_sock_flow_entries
# 指定インターフェースの指定キューに対する最大フロー数の制限値
/sys/class/net/ethX/queues/rx-N/rps_flow_cnt

ethXとrx-Nの部分の意味はRPSと同じです。

設定するNIC(インターフェース)の各キューの値(rps_flow_cnt)の合計値がシステム全体の制限値(rps_sock_flow_entries)を超えてはいけないようです。

→ RedHatのRFS設定の説明を参照

実際に設定する値はサーバの使用状況やネットワーク環境、使用しているアプリケーションなどによって変わってくると思います。

設定方針としてはrps_sock_flow_entriesは予想される最大のアクティブな接続数とし、rps_flow_cntはrps_sock_flow_entriesをNICのキューの数で割った値とするのが良いようです。

一般的なサーバーの適度な負荷に対する推奨値はrps_sock_flow_entriesが32768、rps_flow_cntはNICが単一キューであれば同じ32768、NICに4キューあればそれぞれ8192となります。

# 一般的な負荷環境のrps_sock_flow_entries設定
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
# eth0のNICが単一キューの場合のrps_flow_cnt設定
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
# eth0のNICが2キューの場合のrps_flow_cnt設定
echo 16384 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 16384 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt

システム全体の制限値であるrps_sock_flow_entriesの設定に関してはsysctlでも行えますので、sysctlコマンドを使用するか/etc/sysctl.confに記述することもできます。

# sysctlコマンドで設定
sysctl -w net.core.rps_sock_flow_entries=32768
# /etc/sysctl.confに以下を追加
net.core.rps_sock_flow_entries = 32768

最後に

※ 色々なサイトや文献を見る限り4キューのNICが2つある環境でもrps_flow_cntはrps_sock_flow_entriesを8で割らずに4で割ってNIC単体で計算して良いように見えます。(確信はなし、訂正する可能性あり)

※ 最終的にはネット上の情報はあてにせず、カーネルのソースから確認した方が確実だと思います。