none
プログラムでのTCP遅延ACKの無効化について RRS feed

  • 質問

  • Windows7,Visual Studio2015でソケット通信のサーバー側のプログラミングをしてます。
    実行時間を早くするために、TCP遅延ACKを無効化したいです。
    ネットにある情報では以下のレジストリを1にする方法が紹介されてます。

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{Interface GUID}\TcpAckFrequency

    ただ、この方法だと再起動が必要な上に無線アダプタごとに設定しなければならなず、ユーザーから見て使用性が低くなるので、極力レジストリを変更せずにTCP遅延ACKをなくす方法を探しています。
    プログラムの中でソケットオプションでTCP_NODELAYを設定する方法がありましたが、これでは遅延ACKはなくなりませんでした。
    TCP_QUICKACKとうのもあるらしいですが、Windowsでは定義されてませんでした。

    何か、プログラム上でTCP遅延ACKを無効化する方法はないでしょうか?

    2017年3月13日 0:20

回答

  • 回答ではなく一般論になってしまいます。ご容赦ください。

    TCP_NODELAYオプションはNagleを無効化するもので、DelayedACKとの相乗的な遅延に対する
    ある程度の効果が期待できますが、それ自体を無効化するものではありません。
    当たり前ですがNagleアルゴリズム(RFC896)とDelayedACK(RFC1122)は別物です。

    次に、これらは標準的に実装されていて、回避することは一般的にはできません。
    しかも、OS自体もこれを前提として実装されていると予測できるため、
    これらを外した場合、OSの動作が不安定になる可能性を覚悟しなければなりません。
    従ってピアツーピア接続などの限定的な場合を除いて現実的な選択肢にはなりません。

    さて、両者の悪い影響を少なくするための傾向と対策的な手法は色々とあるようです。
    まずは、それらを行ってみるべきではないでしょうか。
    前提として、これらのアルゴリズムは、接続された両者間の登り下りの通信量が
    ほぼ同じ(対称)であることを前提に設計されています。

    最後に、DelayedACKに対する対策としては、

    1.受信に対して、即座に返信(送信)する。

    くらいしかありません。Nagleと合わせた対策をとらないと、あまり改善は期待できないかもしれません。

    2017年3月13日 1:49
  • TcpAckFrequencyのレジストリを1に変更するとこの200msecの遅延はなくなるので、TCP遅延ACKが原因と推測してます。

    1.~4.の「ステップの合計時間を見てい」るということは「1.~4.の合計時間」が早くしたいことでしょうか?

    遅延ACKが原因で後続データが送信されず、送信時間が増加してしまうのであれば、単純に考えれば、受信側の受信ウィンドウサイズを拡大すればいいように思いますが、その点については対策済みでしょうか? (SO_RCVBUFは正確には受信バッファサイズを指定するものですが、受信ウィンドウサイズも連動するそうです。)

    2017年3月13日 3:55
  • >この方法を具体的に教えていただけないでしょうか。
    それほど凝ったことではありません。

    1.連続通信開始を意味する通信ブロックは、Nagleを回避できるサイズを即座にやり取りする。

    だけです。これで連続通信開始時に特有のNagleとDeleydACKの影響を、ある程度回避できます。
    サーバーのrecv()を行っているスレッドで受信中のブロックの「意味=連続通信開始」が判明したら、
    即座に似たようなブロックをクライアントにsend()するだけです。
    Nagleを回避できるサイズとはイーサーネット上のIPV4では概ね1460Byteと計算されます。
    もう一つのNagle回避として、

    2.(連続通信中は)必要がなくても100[ms]程度以内に1回以上の通信を行い、NagleやDeleydACKの
     開始の条件が成立しないようにする(通信間隔をあけない)。

    ことですかね。

    2017年3月13日 5:43

すべての返信

  • TCP遅延ACK無効化の方法についてと同件でしょうか。質問から1ヶ月経過し誰も回答していないようなので、マルチポストとは少々状況が異なるとは思いますが、提示すべきとは思います。

    状況を整理したく思います。

    TCP ACKとは受信側が正常に受信完了した旨を送信側に通知する機能です。SOでのコメントによるとread()にて実行時間を測定されたとのことですが、read()時間とACK応答タイミングの違いがよくわかりませんでした。送信側へACKを応答しようがしなかろうが受信側の受信時間は変わらないのではありませんか?

    ネットワークカードにはTCPオフロード機能を有しているものもあり、OS側で明示的にACK応答パケットを生成しなくても自動的にACK応答してくれるものもある(かなぁ)と思います。

    また今回「無線アダプタ」という新たな情報が加えられています。有線通信と異なり無線通信では送受信エラーが多く正確な測定ができないはずですがその点はどうでしょうか? 更に無線アダプタでは送受信エラーが多い点を踏まえたSACK; Selective Acknowledgmentを備えたものもあると聞きます。この辺りはどうでしょうか?

    2017年3月13日 1:36
  • 回答ではなく一般論になってしまいます。ご容赦ください。

    TCP_NODELAYオプションはNagleを無効化するもので、DelayedACKとの相乗的な遅延に対する
    ある程度の効果が期待できますが、それ自体を無効化するものではありません。
    当たり前ですがNagleアルゴリズム(RFC896)とDelayedACK(RFC1122)は別物です。

    次に、これらは標準的に実装されていて、回避することは一般的にはできません。
    しかも、OS自体もこれを前提として実装されていると予測できるため、
    これらを外した場合、OSの動作が不安定になる可能性を覚悟しなければなりません。
    従ってピアツーピア接続などの限定的な場合を除いて現実的な選択肢にはなりません。

    さて、両者の悪い影響を少なくするための傾向と対策的な手法は色々とあるようです。
    まずは、それらを行ってみるべきではないでしょうか。
    前提として、これらのアルゴリズムは、接続された両者間の登り下りの通信量が
    ほぼ同じ(対称)であることを前提に設計されています。

    最後に、DelayedACKに対する対策としては、

    1.受信に対して、即座に返信(送信)する。

    くらいしかありません。Nagleと合わせた対策をとらないと、あまり改善は期待できないかもしれません。

    2017年3月13日 1:49
  • >> 佐祐理様

    ご返信ありがとうございます。

    >Oでのコメントによるとread()にて実行時間を測定されたとのことですが、read()時間とACK応答タイミングの違いがよくわかりませんでした。送信側へACKを応答しようがしなかろうが受信側の受信時間は変わらないのではありませんか?

    説明が不足していたので補足します。まずネットワーク構成ですが、以下の3つのデバイスがあります。

    • クライアントPC
    • サーバーPC
    • ルータ

    ここで、クライアントPCとルータは有線、ルータとサーバーPCが無線で通信しています。

    ソケット通信の実行時間は以下のステップの合計時間を見ています。

    1. クライアントPCでサーバーPCのIPアドレスを指定してソケットオープン
    2. サーバーPCにデータ送信
    3. サーバーPCからデータ受信
    4. ソケットクローズ

    ここで、3のデータ受信において、ソフト上では120KBのデータをread()一回で受信してますが、プロトコルアナライザで中身を解析したところ、いくつか分割されたパケットを受信しており、パケット複数個(不特定)に対しサーバーPCにACKを返しています。プロトコルアナライザそれぞれの時間解析をしたところ、パケット受け取ってからACKを返すまでの時間はほとんどは数百usecかかっているのに対し、最初だけは約200msecと非常に遅くなってます。

    TcpAckFrequencyのレジストリを1に変更するとこの200msecの遅延はなくなるので、TCP遅延ACKが原因と推測してます。

    >有線通信と異なり無線通信では送受信エラーが多く正確な測定ができないはずですがその点はどうでしょうか? 更に無線アダプタでは送受信エラーが多い点を踏まえたSACK; Selective Acknowledgmentを備えたものもあると聞きます。この辺りはどうでしょうか?

    実行時間の測定は20回測定して最小、最大、平均を求めてます。確かに無線だと短期、長期で非常にばらつきが大きいです。周りの電波状態に左右され、昼間と夕方で大きく実行時間の差が出ることもあります。ですが、短時間で複数回測定してれば最小値は安定して測定できますし、いくら良い条件でも遅延ACK無効化した実行時間には絶対到達しないので無線による実験系のばらつきは別に考慮されています。SACKについては知りませんでした。


    2017年3月13日 3:02
  • TcpAckFrequencyのレジストリを1に変更するとこの200msecの遅延はなくなるので、TCP遅延ACKが原因と推測してます。

    1.~4.の「ステップの合計時間を見てい」るということは「1.~4.の合計時間」が早くしたいことでしょうか?

    遅延ACKが原因で後続データが送信されず、送信時間が増加してしまうのであれば、単純に考えれば、受信側の受信ウィンドウサイズを拡大すればいいように思いますが、その点については対策済みでしょうか? (SO_RCVBUFは正確には受信バッファサイズを指定するものですが、受信ウィンドウサイズも連動するそうです。)

    2017年3月13日 3:55
  • >>中澤様

    ご返信ありがとうございます。

    >次に、これらは標準的に実装されていて、回避することは一般的にはできません。
    しかも、OS自体もこれを前提として実装されていると予測できるため、
    これらを外した場合、OSの動作が不安定になる可能性を覚悟しなければなりません。
    従ってピアツーピア接続などの限定的な場合を除いて現実的な選択肢にはなりません。

    やっぱりそうなんですね。レジストリ変更もすごく抵抗があってなるべくやりたくなかったのですが・・

    >最後に、DelayedACKに対する対策としては、

    1.受信に対して、即座に返信(送信)する。

    この方法を具体的に教えていただけないでしょうか。

    read処理はソフトから見れば1つの関数なのですが、その処理中で複数パケットの受信に対しACKしている部分で遅延が起きているので、ソフトでどうにも手が出せないです。何か使い方で解決できるのであればそうしたいのですが。

    それとももっとTCPプロトコルのもっと低レベル層をプログラミングできれば解決するのでしょうか。(できるかどうかわからないのですが・・)

    2017年3月13日 4:02
  • >1.~4.の「ステップの合計時間を見てい」るということは「1.~4.の合計時間」が早くしたいことでしょうか?

    はい。そうです。

    >遅延ACKが原因で後続データが送信されず、送信時間が増加してしまうのであれば、単純に考えれば、受信側の受信ウィンドウサイズを拡大すればいいように思いますが、その点については対策済みでしょうか?

    試してみます。ただ、どこかでACKは返さないといけないので、受信データをひとつにまとめたとしても、その後に早くACKを返せなければやはり遅くはなると思います。

    2017年3月13日 4:47
  • >この方法を具体的に教えていただけないでしょうか。
    それほど凝ったことではありません。

    1.連続通信開始を意味する通信ブロックは、Nagleを回避できるサイズを即座にやり取りする。

    だけです。これで連続通信開始時に特有のNagleとDeleydACKの影響を、ある程度回避できます。
    サーバーのrecv()を行っているスレッドで受信中のブロックの「意味=連続通信開始」が判明したら、
    即座に似たようなブロックをクライアントにsend()するだけです。
    Nagleを回避できるサイズとはイーサーネット上のIPV4では概ね1460Byteと計算されます。
    もう一つのNagle回避として、

    2.(連続通信中は)必要がなくても100[ms]程度以内に1回以上の通信を行い、NagleやDeleydACKの
     開始の条件が成立しないようにする(通信間隔をあけない)。

    ことですかね。

    2017年3月13日 5:43
  • ただ、どこかでACKは返さないといけないので、受信データをひとつにまとめたとしても、その後に早くACKを返せなければやはり遅くはなると思います。

    ですから何が遅くなり、何を早くしたいのですか? SOからずっと、質問者さんの「早くしたいこと」と「遅延ACK応答」とが結び付いていないように見受けられます。

    ACKは何か送信するパケットがあればそのついでとして送信できます。遅延ACK応答とは無条件に送信を遅らせるものではなく、何か送信するパケットがないかを一定時間待つ機能です。1.~4.であれば3.の受信ACKは4.のクローズに付加されるだけのことではありませんか?

    2017年3月13日 5:48
  • >1.~4.であれば3.の受信ACKは4.のクローズに付加されるだけのことではありませんか?

    ようやく理解できました。ACKはread処理内で閉じてるのだと思い込んでました。

    SO_RCVBUFの方法を試してみます。

    2017年3月13日 7:57