その速さは、どこから来たのか
ClickBench から pgrust の結果が外されたとき、争点になったのは「速いか遅いか」ではない。もっと手前の、そもそもその数字が何を測っているのかだった。削除を扱う Pull Request には、単なる性能差ではなく、per-query kernels と result caching を使っているため entry is cheating だと書かれている。ここで問題視されたのは、クエリごとに専用の経路を持つことと、前回の実行結果を次の実行に持ち越すことだ。
pgrust 側は、これを正面から否定するのではなく、「その前の版はまだ開発途中の pgrust 0.4-preview だった」と説明した。しかも 0.4-beta では、キャッシュの種類ごとに設定を公開し、どれをオンにしているのかまで明示している。つまり論点は、キャッシュがあるかないかではなく、どこまでがベンチマークで許される“中間データの再利用”で、どこからが“答えそのものの再利用”なのかに移った。
ベンチマークにだけ最適化された道
pgrust の削除理由を読むと、違和感はかなり具体的だ。GitHub 上の指摘では、43 個の ClickBench クエリに合わせて書かれた machinery が結果を作っており、しかも hot run では前回の答えをそのまま replay している、とされる。これが本当なら、ベンチマークは「汎用の SQL エンジンが毎回計算している時間」ではなく、「ベンチマーク専用の辞書から答えを引いてくる時間」に近づく。
実際、指摘文には q00.rs、q01.rs、q02.rs のようにクエリ番号がそのまま付いたソース断片が並び、plan matcher が benchmark 名に紐づいた拒否メッセージを返す例まで挙げられている。たとえば、clickbench group count: filter is not k <> 0 のように、ベンチマークで想定された形から少しでも外れると、その場で拒否する。これは「速い実行計画を選ぶ」より先に、「想定した形かどうかを見分ける」ための仕組みに見える。
さらに pgrust 側の新しい公開版でも、クエリ条件の再利用はかなり積極的だ。0.4-beta の説明では、filter results on columnar tables are reused across statements over the same immutable data とあり、条件キャッシュはデフォルトで有効だとされている。しかも、このキャッシュは粒度がいくつもある。条件キャッシュ、辞書フィルタキャッシュ、zone map cache が別々にあり、用途も違う。ここまでは、データベースの最適化としては珍しくない。
では、何が線を越えたのか。争点は、その最適化が「再実行時に答えを短絡して返す」方向へ寄りすぎたことだ。
43 クエリ専用機が生む見かけの速度
pgrust の 0.4-preview をめぐる説明では、ClickBench の 43 クエリのためだけに作られたソースと、そこへ向かう分岐が問題視された。GitHub の指摘には、公開版のバイナリにベンチマーク番号に対応するファイルが埋め込まれていること、そして Q20、Q21、Q23 では「hot rep = pure fold over cached verdicts — ZERO columns touched」といった注記があることまで書かれている。列を読まないのに、答えだけ返る。これが一番きつい。
ここで思い出したいのは、ClickBench 側のルールだ。公式の説明では、クエリ結果キャッシュは無効にしなければならない一方で、中間データのキャッシュは一般に許容される。ただし、それはあくまで「同じフィルタ条件の再評価」を速くするためのものだ。たとえば ClickHouse の query condition cache は、各 granule に対して「一致する行があるかどうか」を記録する。0bit ならその granule を飛ばし、1bit なら読む。重要なのは、答えそのものではなく、読まなくてよい範囲の情報だけを残す点だ。
pgrust の指摘では、この境界を越えたと見なされた。Q20 のようなクエリで、初回は 10.8 秒でも、2 回目は 11 ミリ秒、3 回目は 5 ミリ秒になった、という測定結果が挙げられている。別のクエリでは 1.65 秒が 4 ミリ秒、2.38 秒が 213 ミリ秒から 108 ミリ秒へ落ちる。数字だけ見ると優秀だが、2 回目だけ極端に縮む値は、普通の CPU キャッシュや実装最適化だけでは説明しにくい。しかも一度再起動すると、また 11 秒級に戻る。これは「速い実行計画」より「前回の判断の再利用」を疑わせる。
ClickBench が嫌うのは、速さよりも再現性の欠落
ここで誤解しやすいのは、ベンチマークが“キャッシュ全般”を嫌っているわけではないことだ。ClickBench 自身、Cold run の前に OS キャッシュと DB キャッシュをクリアし、hot run を複数回取る。つまり再実行で速くなること自体は前提にしている。問題は、何が温まって速くなったのかが説明できるかどうかだ。
pgrust の 0.4-beta では、条件キャッシュのほかに dictionary filter cache や zone map cache があると明示された。さらに、以前の版では条件キャッシュを off にしても別のキャッシュが生きていて、結果が跨いで再利用されていた、という説明もある。ここで初めて、「速い」は「本物の実行が速い」のか、「前回の判定を持ってきているだけ」なのかを分けて考える必要が出てくる。
しかも、ベンチマーク形状から外れたクエリには、かなり露骨に失敗する。statement over columnar relation "hits" is not served のようなエラーで、対象外の SQL は拒否される。20 個の近似クエリと 3 個の変形クエリが拒否された、という記述もある。これは汎用 DB が広い入力を受けるのではなく、狭く切った入力だけを最短経路でさばいている構図だ。
その結果、読者が知りたい「2回目だけ 5 ミリ秒の正体」は、単なる高速化ではなく、ベンチマーク専用の判定結果とキャッシュの組み合わせだったのではないか、というところまで見えてくる。だが、まだ肝心の内訳が残っている。どのキャッシュがどこまで許され、どこからが結果の再利用になるのか。その線引きの説明が、この件の核心になる。
👉 続き(核心はここから)は PaPoo で読めます: https://papoo.work/doc/1ffc43125c0e3cb5