MacとNASを10GBase-TでつないだのにQuickTimeムービーが途切れる件について

この時期、ここ近年は夏休みなんて関係ないぜだったんですが、今年は諸般の事情により夏休みになりました。
みなさんは夏休みいかがお過ごしでしょうか。

で、夏休みに入る前にトラブルを片付けておくということで、お客さんのところでNASとMacを10GBase-Tでつないだのに、NAS上のQuickTimeムービーを再生したらコマ落ちするから性能出てないんじゃないかというお話が来てたのを片付けに。

状況確認

状況を確認したところ、QuickTimeムービーを再生すると確かにコマ落ちするんですが、ファイル転送はそんなに遅くないし、ベンチマークソフトでNASへのディスクアクセスを計測しても軽く500MByte/sec以上とか出るので一見全く問題なさそう。

変だなーと思ってパケットキャプチャしてみたところ、4パケットごとにACKがMacから返してるんだけど、それが約100msぐらい遅れてる。

んー。これなんか昔に見たよなーと思い調べたところ、Delayed ACKとNagleアルゴリズムの組み合わせによる問題。
あーそういえばそんなのあったあった。

Delayed ACKとNagleアルゴリズムの組み合わせによる問題

どういう問題かを簡単に説明しときましょう。

まず、前提知識。
Delayed ACKとNagleアルゴリズムはいずれもTCPのプロトコルオーバーヘッドを削減するための実装です。

Delayed ACKは受信側でACK応答を遅らせ、受信ウィンドウの更新や応答データをまとめて送ります。遅延を解除し、送信する条件は以下のとおり。
  1. ACK以外に応答するデータがある場合
  2. 一定量以上のデータを受け取った場合
  3. 一定時間経過した場合
一方Nagleアルゴリズムはデータの送信を遅らせ、複数の小さなパケットをまとめて1つのパケットで送信するものです。こちらの送信する条件は以下のとおり。
  1. 未送信データが最大セグメントサイズ(MSS)以上になった場合
  2. 送信済みのパケットのACKを全て受け取った場合
  3. 一定時間が経過した場合
Delayed ACKが受信側、Nagleアルゴリズムが送信側で有効になっていると、以下の様な状況が起こることがあります。
  1. 送信側:データを送信
  2. 受信側:データを受信するが、データ量が一定量以下なのでACKはまだ返さない
  3. 送信側:送信済みデータのACKが来ないので、次の送信データを貯めこむ
  4. 受信側:送信側からデータが来ないので、さらにACKを待つ
このようになるとどちらも送信をせず、お見合い状態になってしまいます。
転送が再開されるのは、Delayed ACKまたはNagleアルゴリズムのそれぞれの3番目の条件、「一定時間が経過」しない限りデータ転送が再開されません。
WANのように回線帯域に余裕が無い場合は輻輳するよりもこの方がマシという場合もあるでしょうが、10Gといった高速回線上では無駄な待ち時間にほかなりません。

解決されたはずだけど…

この問題、昔からあって最近見かけなかったから対策されたんじゃないのかなーと思ってたら、以下のようなBlog記事を発見。

Performance Tuning the Network Stack on Mac OS X Part 2

これのExplanation of Configuration Optionsの7番目にDelayed ACKとNagleアルゴリズムの話が書いてあるんですが、どうやらAppleはGigabit Ethernetまでは対策したけど、10Gには対策がまだ出来てないようだとのこと。

上記Blogを参考に、sysctlを使用してカーネルパラメータを修正し、Delayed ACKを無効化。
$ sudo sysctl -w net.inet.tcp.delayed_ack=0
本来ならばNAS側の方でNagleアルゴリズムを無効化すべきなんでしょうが、ちょっと今回は見送り。というかNASはQNAPなんですが、中身SambaなんだしsocketオプションにNO_TCPDELAY入ってないのかなぁ…。

とりあえずMac側でDelayed ACKを無効化することで問題の解消が確認できたので、設定を永続化するため、/etc/sysctl.confを編集し、以下の行を追加。
net.inet.tcp.delayed_ack=0
うーん。久しぶりにネットワークエンジニアっぽい仕事をした気分…。

というところで今回はここまで。


ちなみにお客さんが使ってたMac用10GEthernetアダプタはこれ↓

コメント