0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2回目だけ5ミリ秒で見抜かれたpgrust、ClickBench削除の境界線

0
Posted at

その速さは、どこから来たのか

画像: github.com より
画像: github.com より

ClickBench から pgrust の結果が外されたとき、争点になったのは「速いか遅いか」ではない。もっと手前の、そもそもその数字が何を測っているのかだった。削除を扱う Pull Request には、単なる性能差ではなく、per-query kernels と result caching を使っているため entry is cheating だと書かれている。ここで問題視されたのは、クエリごとに専用の経路を持つことと、前回の実行結果を次の実行に持ち越すことだ。

pgrust 側は、これを正面から否定するのではなく、「その前の版はまだ開発途中の pgrust 0.4-preview だった」と説明した。しかも 0.4-beta では、キャッシュの種類ごとに設定を公開し、どれをオンにしているのかまで明示している。つまり論点は、キャッシュがあるかないかではなく、どこまでがベンチマークで許される“中間データの再利用”で、どこからが“答えそのものの再利用”なのかに移った。

ベンチマークにだけ最適化された道

画像: github.com より
画像: github.com より

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?