100GbE+ラージMTU環境でのQUICのパフォーマンス改善

Jun-ya Kato
nttlabs
Published in
14 min readMay 19, 2021

トランスポートプロトコルQUICの仕様を定めた四つのRFCが、2021年5月31日にRFC8999 ~ RFC9002として発行されました。HTTP/3の仕様を決める2つのインターネットドラフトは最終調整の段階です。

  1. RFC8999, “Version-Independent Properties of QUIC”
  2. RFC9000, “QUIC: A UDP-Based Multiplexed and Secure Transport”
  3. RFC9001, “Using TLS to Secure QUIC”
  4. RFC9002, “QUIC Loss Detection and Congestion Control”
  5. “Hypertext Transfer Protocol Version 3 (HTTP/3)”
  6. “QPACK: Header Compression for HTTP/3”

QUICは、2013年からWeb体験を向上させることを目的にGoogleが開発を始めたプロトコルです。インターネット上でのコンテンツのデリバリを高速化するとされ、例えばGoogleからはYouTubeでバッファリング時間が短くなる視聴体験の向上の効果が報告されています。

この記事では、QUICがTCPと比べてどの程度の性能差があるか比較と分析を行います。プロトコルスタック内部の詳細分析はVA Linux Systems Japan 株式会社様の協力を得て実施しました。

QUICの概要と普及状況

Googleが自社のサーバとChromeブラウザなどのクライアントにQUICを実装することでデプロイを始め、同社のサービス普及とともにデファクト化していきました。その後、2015年からはIETFで標準化の議論が開始されています。QUICは、HTTPの最新バージョンであるHTTP/3と密接に結びついており、HTTP/3はトランスポートとしてQUICを使います。RFC9000となる見込みの仕様 (QUIC: A UDP-Based Multiplexed and Secure Transport) はタイトルの通り、UDPを利用したトランスポートプロトコルであり、かつ、TCPと同様な信頼性確保や輻輳制御のしくみとTLSv1.3をベースとしたセキュリティ機能を内包しています。HTTP/3はTCPを使いませんので、もしHTTP/2からの移行が進めば、TCPトラフィックは減り、UDPトラフィックが増えることが見込まれます。

現在、HTTP/3, QUICはGoogleだけでなくコンテンツプロバイダやCDN事業者による採用が進んでおり、YouTubeを含むGoogleの大多数のサービスや、facebookやInstagramなどのサービスが使用しており、ユーザは知らない間にQUICを使っていることも珍しくありません。2021年5月18日時点でのQ-successの調査は、Webサイトの19.4%がHTTP/3に対応していると報告しています。特に2021年2月から急増する様子が見てとれます。対応率が20%に迫る数値は大きすぎるとも感じましたが、少なくとも普及が進む状況は間違いなさそうです。

なお、Q-successの調査の興味深い点は、著名な大規模Webサイトを対象としたTop 1,000の普及率よりも、小規模を含めて様々なWebサイトが混在するTop 1,000,000 で測った普及率のほうが大きい数値を示していることです。curl開発者のDaniel Stenberg氏のブログ “Where is HTTP/3 right now?” では、大手CDN Cloudflareの無料プランのHTTP/3対応が要因のひとつと推測しています。

ユーザ空間に実装されるプロトコルスタック

性能測定の前に、TCPとQUICの実装形態の比較をしておきます。

一般に、TCPスタックはカーネル内に実装されるのに対して、QUICスタックの多くはユーザ空間にアプリケーションとして実装されます。アプリケーションとして実装することでデプロイを容易にしつつ、カーネルアップデートの問題に対応できます。

Linux, BSDなどのOSカーネルのTCPスタックは日々進化していますが、新しい機能を使うためにはカーネルを更新する必要があります。例えば、Linuxカーネルを使うAndoridスマートフォンでは、アプリケーションはGoogle Playから、簡単かつ頻繁にアップデート可能な一方、カーネルの更新はAndroid OS本体のバージョンアップが必要となります。しかしスマートフォンベンダから提供される機会は限られているのが実情で、世界中で古いバージョンの多くのAndorid OSが使われ続けています。このような状況では、アプリケーション提供者はLinuxカーネルの最新機能を前提に開発することが出来ません。

AndroidスマートフォンやLinuxカーネルだけの問題ではなく、世界中に広く配布されたデバイスに搭載されるOSソフトウェアでも同じ問題が起こります。カーネル機能は基本的なものだけを利用し、高度な機能はアプリケーションに組み込んで実装する方法は合理的です。

パフォーマンス (スループット)測定

TCPと比べてプロトコルスタックの実装形態が大きく違いますが、性能差はどの程度でしょうか?

実際に、2つのQUIC実装を使って1GBのコンテンツを取得(GET)する性能テストを行いました。下記の2つはHTTP/2 + TLSv1.3 + TCP にも対応しているソフトウェアですので、HTTPのバージョンだけを変えて同じコンテンツをリクエストします。

  1. クライアントは ngtcp2 とそれを使ったWeb負荷試験ツール h2load (quicブランチ)
  2. サーバは Nginx (QUIC開発ブランチ)

テスト環境は、100GbE NICを装着した2つのエンドポイントを、MTU 9001のジャンボフレームが透過するEthernet接続で結びます(図1)。

図1: スループットの測定環境

両エンドポイントのサーバは72 vCPUを搭載しており、エンドポイントA上でクライアントを大量発生させ、サーバに対して、1GBのファイルをダウンロードのGET要求を出しスループットを測定します。TCP, QUICそれぞれのコマンドは下記です。なお、いずれもTLSのアルゴリズムは同一のものを使用します。

  1. HTTP/2 + TLSv1.3 + TCP over 100GbE
# h2load -c 360 -t 36 -D 10s --npn-list h2 https://server/fsize-1GB.dat

2. HTTP/3 + QUIC(TLS) + UDP over 100GbE

# h2load -c 360 -t 36 -D 10s --npn-list h3-29 https://server/fsize-1GB.dat

実際に走らせて見ると、

HTTP/2 + TLSv1.3 + TCP ………..… 80.2Gbps
HTTP/3 + QUIC(TLS) + UDP ……… 32.8Gbps

となりました。なぜかQUICの性能はTCPのそれと比べて40%程度しか得られない結果となりました。

以降の節で性能が得られなかった理由と改善策を考察します。

コネクションハンドシェイクと送信UDPパケットサイズの決定

TCPでは3ウェイハンドシェイクと呼ばれる3つの信号を交換して、エンドポイント間でコネクションを確立します。セキュアな通信路が必要な場合は、さらにTLSのネゴシエーションが行われ、合計で2往復(2-RTT)の信号交換が必要です。一方、QUICではコネクション確立のネゴシーエションにTLSも重畳させて、1-RTTで完結させます。

エンドポイントが、送信パケットの最大サイズを決定する方法に注目します。QUICではネゴシエーションの際に交換するトランスポートパラメータの中に、受信可能なUDPパケットの最大サイズ (max_udp_payload_size) 含めて相手に通知します。TCPのハンドシェイクにおけるMSS (max segment size) オプションに似た概念です。

自分が受信可能なパケットサイズを相手のエンドポイントに通知するだけでは不十分です。図2のようにエンドポイントA,B間の経路は非対称であることを前提としなければなりません。エンドポイントAがmax_udp_payload_size として1452バイト(IPv6の場合)を通知し、実際にエンドポイントBが1500バイトのIPv6パケットを受信できたとしても、エンドポイントBはAに向けて1500バイトのIPv6パケットを必ずしも送れるとは限りません。図2の環境ではエンドポイントBから見たAへのPath MTUは1280だからです。

図2: 非対称経路とPath MTU

そこで、TCPでもQUICでも、エンドポイントはPath MTU Discovery で、Path MTUを求めます。エンドポイントBにおいてPath MTU Discoveryが正しく機能すれば、Aに至るまでのPath MTUが1280であることが発見でき、送信パケットサイズの最適設定が可能です。

しかし、特にインターネットで困ったことは、Path MTU Discoveryが必ずしも機能するとは限らないことです。IPレイヤのPath MTU DiscoveryではICMPエラーメッセージによりMTUを通知しますが、過剰なフィルタリングが起因してICMPエラーメッセージが届かないことがあります。TCPを使った通信ではPath MTUが発見できなくても、IPレイヤのフラグメントが機能して通信が成立することがあります。フラグメントによる性能劣化が発生していますが、ユーザからは気づかないこともあるでしょう。

しかし、QUICの仕様ではIPレイヤでのUDP/IPパケットのフラグメントを禁止しているため、IPフラグメントに頼ることはできません。QUIC仕様では、Path MTU DiscoveryによってPath MTUを発見した上で、IPフラグメントが生じないようにUDPパケットの送信サイズを設定します。

しかし、先にも述べたとおり、Path MTU Discoveryは必ずしも機能する仕組みではありません。QUICの仕様ではPath MTUが発見できなかった場合のUDP/IPパケットの最大値を "the smallest allowed maximum datagram size"と規定しています。文字通りに読むと最小の許容された最大データグラムとなり、ややこしい表現です。具体的な値を当てはめてみると、インターネットでは最小のPath MTUが1280であると仮定して、送信UDPパケットのペイロードの最大サイズを下記の値まで広げます。

  • IPv6 : 1232 = 1280 - (IPv6ヘッダ40バイト+UDPヘッダ8バイト)
  • IPv4 : 1252 = 1280 - (IPv4ヘッダ20バイト+UDPヘッダ8バイト)

モバイルアクセスなどトンネルがあるリンクをはじめ、インターネットでは、Path MTUが1500を超えることはあまり多くありません。実際にPath MTUを1280と仮定してもQUICは十分な効果を発揮しています。

Nginxの実装とQUICの性能劣化要因

本記事の実験でHTTP/3サーバとして利用したNginxのPath MTU discoveryについてコードを調べてみたところ、TODOとされ未実装でした。Path MTUに関わらず送信UDPパケットの最大サイズはIPv6, IPv4 それぞれ1232バイト、1252バイトに固定されています。この記事が対象としたPath MTUが9001のような環境では、大きなUDPパケット送信できるにも関わらず、Path MTUに比べて小さなパケット(1280バイト)を送信にとどまります。MTUに見合う十分な効率を得ることができません。

### src/event/quic/ngx_event_quic_output.csize_t
ngx_quic_max_udp_payload(ngx_connection_t *c)
{
/* TODO: path MTU discovery */

#if (NGX_HAVE_INET6)
if (c->sockaddr->sa_family == AF_INET6) {
return NGX_QUIC_MAX_UDP_PAYLOAD_OUT6;
}
#endif

return NGX_QUIC_MAX_UDP_PAYLOAD_OUT;
}

I/Oの回数の観点で比較すると、一回に送出するパケットサイズが小さい(1280)と、大きい場合(9001)に比べて、おおよそ 9001÷1280 = 7.03倍ほどに増大します。大きなMTUを十分に使い切れないことがパフォーマンス劣化の原因です。一方、カーネル空間TCPスタックとユーザ空間の受送信処理では、ストリームデータをMTUサイズより大きく集約して受け渡しを行うことができ、かつMTU 9001を最大限活用したデータ転送を行うため、高いスループット値(80.2Gbps)を得ました。

送信UDPパケットサイズ設定を改善してみた

クライアント側のh2load, サーバ側のNginxのそれぞれのコードを修正して、Patu MTU discoveryがうまく働いたと仮定して、MTU 9001を使い切る大きなサイズのUDPペイロードでパケットを送信できるように改造しました。同じ条件で負荷をかけたところ、TCPに迫る値を得ることができ、送信パケットのサイズ設定を適切に行うことは効果が高いことがわかりました。

HTTP/2 + TLSv1.3 + TCP ……..….. 80.2Gbps
HTTP/3 + QUIC(TLS) + UDP …….. 71.4Gbps (最大パケットサイズ9001)
HTTP/3 + QUIC(TLS) + UDP …….. 32.8Gbps (最大パケットサイズ1280)

しかし、TCPを超えたわけではないため、まだ改善の余地はあります。

QUICのパフォーマンス向上の市中の取り組み

本記事では、データセンタ内ネットワーク環境を想定して、大きなMTUに適したサイズのUDPパケットを送信することでパフォーマンスを改善しました。

MTUが小さな環境であっても、なるべく大きなかたまりでデータを受送信し、ユーザ空間のQUICスタックとカーネル空間のUDPスタック間のI/Oを削減することは効果的です。GSO, GRO (Generic Segmentation Offload, Generic Recieving Offload) と呼ばれる、細切れのデータを集約してI/Oを一括して行う仕組みや、NICハードウェアにUDPペイロードの分割をオフロードする仕組みの有効性が報告されています。関連する市中の取り組みを紹介して、本稿の結びとします。

さいごに

NTT研究所はWebサービスの高速化や新しいプロトコルに関心のある仲間を募集中です。興味のある方はぜひ弊社の採用情報ページをご覧ください。ご連絡お待ちしています。

--

--