メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

性能改善挙動変更

compress/flateの圧縮速度が改善され、エンコーダ実装変更に伴いWriterの出力バイト列がGo 1.26から変化しうる

compress/flate

この項目の注釈は AI により生成されており、誤りを含む場合があります。

概要

Go 1.27でcompress/flateの圧縮速度が改善された。エンコーダ実装が変更された結果、Writerが生成する正確なエンコード出力はGo 1.26と異なる場合がある。DEFLATEはarchive/zipcompress/gzipcompress/zlibimage/pngが内部で使用する圧縮方式であるため、これらパッケージの出力も同様に変化しうる。出力バイト列そのものはGo 1互換性の対象外であり、テストコードなどで特定の圧縮バイト列に依存すべきではない。

導入経緯

Kubernetes APIサーバーは大きなJSON/Protobufペイロードの転送量削減に標準のcompress/gzipを利用しているが、圧縮スループットが100〜200MB/s程度で頭打ちになっており、大きなリソースサイズをサポートする上でのボトルネックになっているとしてissue #75532が立てられた。サードパーティ実装であるgithub.com/klauspost/pgzipでは約10倍のスループット向上が確認されていたが、Kubernetes側はコア機能を大きなサードパーティ依存に置き換えるコスト・セキュリティレビュー・保守負担を避けたいという方針から、まず標準ライブラリ側の改善をGoチームに要望する形でissueを提出した。

議論のハイライト

  • パッケージの元メンテナである@dsnet(Joe Tsai)は、unsafeを使わなくても現行実装より大幅な高速化の余地があると指摘した一方、圧縮の出力バイト列(ビットストリーム)を変えることはGo 1互換性の対象外であり許容されるが、ビット単位の決定性に依存する利用者は独自にフォークすべきだと述べた。
  • github.com/klauspost/compressの作者である@klauspostが、自身の実装をベースにした圧縮レベル1〜6向けのカスタムエンコーダをstdlibへコントリビュートすることを申し出た。
  • レビューではunsafe利用の可否やfuzzコーパスの扱いが論点になり、最終的にまずunsafeを使わない安全な実装から着手し、fuzzコーパス自体は同梱せずfuzz関数とテストケースでカバーする方針となった。
  • 実装作業中に、BestSpeed指定時に圧縮しても容量削減がないケースの扱いに関連するリンカの潜在バグが見つかった。出力変化に伴う互換性リスクへの対応として、旧実装に戻すためのGODEBUGの追加も検討されたが、実装が大掛かりであることなどから見送られ、最終的にGODEBUGは追加されなかった。
  • 変更はGo 1.26のコードフリーズに間に合わず、Go 1.27での取り込みとなった。
  • 変更のマージ後、gobwas/wswsflateが、EOFが特定のエンコード(0バイトのRAWブロック)になることを前提にしていたためテストが失敗する互換性の問題が報告された。EOFは最終ブロックに埋め込まれるか、より短い固定符号化ブロックになりうるよう変わったことが原因である。

移行時の注意

compress/flatecompress/gzipcompress/zlibarchive/zipimage/pngの出力する圧縮バイト列そのものをテストの期待値や外部システムとの照合条件として固定的に扱っているコードは、Go 1.27へのアップグレードで挙動が変わる可能性がある。展開(decompress)結果自体は仕様どおり変わらないが、圧縮後のバイト列の完全一致を前提にしたテストやツールは見直しが必要になる場合がある。

実装解説

Go 1.27では圧縮レベル1〜6のエンコーダが、レベルごとに特化した新実装(fastEncL1fastEncL6)に置き換えられた。deflatefast.goのコメントにある通り、レベルごとにハッシュテーブルの大きさ・ハッシュに使うバイト数・候補数などの戦略が異なる。エンコーダの選択はnewFastEncが行い、compressor.initからレベル1〜6の場合に呼び出される(レベル7〜9は従来通りdeflateLazyによるadvancedStateベースの実装のまま)。またNewWriterのドキュメントコメントには、出力される正確なバイト列がGo 1互換性の対象外である旨が明記されている。

関連リンク