1.1.1.1の巨大なキャッシュ基盤とメモリの課題

Cloudflareの「Big Pineapple」プラットフォームは、1.1.1.1をはじめとする多くのDNSサービスを支えており、常に2500億以上のDNSキャッシュエントリを保持しています。この規模では、たった1バイトのメモリ無駄も、フリート全体で250ギガバイト以上のコストとなるという試算です。

「1バイトの無駄もフリート全体で250ギガバイトのコストとなる」という事実は、大規模システムにおけるメモリ最適化の重要性を浮き彫りにします。

キャッシュエントリは、クエリ名(qname)、クエリタイプ(qtype)、認証情報、タグを含む CacheKey と、DNS応答データ、タイムスタンプ、TTL、ヒットカウンターなどを含む CacheEntry のキーバリューペアで構成されています。特にEDNS Client Subnet (ECS) を利用している場合、クライアントネットワークに応じて異なる応答が返されるため、同じクエリでも複数のバージョンをキャッシュする必要があり、エントリ数とメモリ消費がさらに増加する傾向にあります。これは私の個人プロダクトでも気をつけたい点です。

5段階の最適化戦略とその効果

Cloudflareは、主に以下の5つの変更によってキャッシュエントリのメモリフットプリントを50%以上削減しました。これらの改善は、単にメモリを節約するだけでなく、パフォーマンスも同時に向上させています。

彼らは、Rustのシステムアロケータをラップするカスタムアロケータを使用し、各変更の影響をベンチマークで詳細に測定しています。(出典)

最適化内容 改善点 メリット
Vec<T> を Box<[T]> に変更 未使用のキャパシティフィールドとオーバーアロケートされたヒープ領域を排除 メモリ効率の向上、不要な再割り当ての回避
String を Box<str> に変更 Vec<T> と同様に、容量フィールドのオーバーヘッドを削減 メモリ使用量の削減
Name 構造体の最適化 DNS名を表す Name 型の内部表現を圧縮 可変長データの効率的な格納
CacheKey の最適化 CacheKey のフィールド順序変更や型最適化 キャッシュキーのメモリフットプリント削減
CacheEntry の最適化 CacheEntry のフィールドのパッキングと型の最適化 DNS応答データ格納の効率化

特に Vec<T> から Box<[T]> への変更は大きな効果を生んでいます。Vec<T> は成長する可能性を考慮して容量フィールドやオーバーアロケートされたヒープ領域を持つため、一度キャッシュに保存されれば変更されないDNS応答データでは無駄になります。

Box<[T]> は作成後にサイズ変更ができないため、これらの無駄をなくすことができます。これはRustを使っているエンジニアの方々には馴染み深い最適化手法ではないでしょうか。

Vec<T> から Box<[T]> への変更は、不変なデータ構造を扱う上での基本的なメモリ最適化であり、他の言語やプロジェクトにも応用可能な知見です。

この最適化により、キャッシュの挿入スループットが43%向上し、検索レイテンシが19%短縮されました。これは、アロケーション回数の削減とメモリ局所性の向上によるものだと考えられます。速度とスペースを両立させている点が非常に素晴らしいと感じます。

なぜ今、大規模なメモリ最適化が求められるのか

Cloudflareは、このメモリ最適化によって約100テラバイトのメモリを解放しました。これは、同社のGen 13サーバー130台分のRAMに相当する量とのことです。(出典)近年、AIや大規模データ処理の需要が高まる中で、メモリ効率はシステム全体のコストとパフォーマンスに直結します。

スタートアップのエンジニアとして、GCPのCloud RunやBigQueryを日常的に使う中で、リソース効率の重要性を日々実感しています。特にクラウド環境では、メモリ使用量が課金に直結するため、わずかな最適化でも大きなコスト削減につながります。Cloudflareのような巨大なインフラを運用する企業にとっては、この最適化が文字通り億単位のコスト削減効果をもたらす可能性もあるでしょう。

自作PCでローカルLLMを動かす際も、メモリは常にボトルネックとなる要素です。GPUメモリだけでなく、システムメモリの効率的な利用は、あらゆる計算リソースにおいて重要なテーマだと感じます。

今回のCloudflareの事例は、既存のシステムであっても、データ構造の基本を見直すことで、これほど大きな効果が得られるという良い教訓になります。私も自分の個人プロダクトのPythonスクリプトやTypeScriptアプリケーションで、無駄なメモリ使用がないか定期的に見直す必要があると感じました。特にオブジェクトの生存期間や不変性を意識したデータ構造設計は、言語を問わず重要ですね。