ベンチマーク
| 起動〜最初のクエリ | テストごとの複製 | インデックス SELECT | 20万行ソート | DB と計測プロセスのメモリ | ダウンロード | |
|---|---|---|---|---|---|---|
| pgmem(Go ライブラリ) | 70 ms | 14 ms (フォーク) | 34 µs10 µs プロセス内接続 | 120 ms | 110 MiB計測プロセスの増分 110 MiB | 37.9 MB (テストバイナリへのリンク増分) |
| pgmem バイナリ(Python / Java / Node.js) | 218 ms | 14 ms (フォーク) | 34 µs | 120 ms | 105 MiB | 16.8 MB (wheel / jar / npm の中身) |
| docker run | 1.65 s | 61 ms (CREATE DATABASE … TEMPLATE) | 93 µs | 259 ms | 77 MiBコンテナ使用量 76 MiB, 計測プロセスの増分 1 MiB, VM の差分 433 MiB, VM 絶対値 1335 MiB | 118 MB (イメージレイヤ) |
| Testcontainers(Go) | 1.09 s | 21 ms (Snapshot / Restore) | 102 µs | 258 ms | 137 MiBコンテナ使用量 134 MiB, 計測プロセスの増分 2 MiB, VM の差分 295 MiB, VM 絶対値 1192 MiB | 120 MB (イメージレイヤ + Ryuk) |
| devbox(nix の PostgreSQL) | 411 ms | 83 ms (CREATE DATABASE … TEMPLATE) | 28 µs | 228 ms | 36 MiBPostgreSQL プロセス合計 | 113 MB (nix closure) |
2026-09-14 計測、5 回の中央値。Apple M3、8 コア、16 GB、Darwin 27.0.0。OrbStack 2.2.3(Docker 29.4.0)、arm64。メモリ列は DB 使用量と計測プロセスの増分を比べます。Testcontainers は PostgreSQL と Ryuk のコンテナ使用量を含みます。VM の差分と絶対値は参考値として分けており、OrbStack がメモリを解放すると差分は小さくなります。
ベンチマーク中の DB と計測プロセスのメモリ
各列が測っているもの
Section titled “各列が測っているもの”どの計測も、新しいプロセスでサーバーを 1 つ起動し、同じスキーマを流し込んでから測ります。ハーネスは bench/alternatives にあります。
- 起動〜最初のクエリ:起動処理を呼び出してから、TCP 経由の
SELECT 1が成功するまで。docker pullとdevbox installは計測前に実行し、Devbox 環境の解決も計測外の準備で済ませます。docker runは新規コンテナと DB クラスタの初期化を含みます。Devbox は初期化済みの永続クラスタと app DB を計測前に準備し、devbox services up -bの実行から SQL ready までを計ります。Testcontainers は新しいテストプロセスが起動する Ryuk コンテナも含みます。pgmem バイナリは ready 行が出るまでを計り、Python、Java、Node.js のラッパーはその行を待ちます。 - テストごとの複製:準備済みデータベース(ユーザー 1,000 件、注文 10,000 件)の独立した複製を 20 回作り、そのたびに接続して結合クエリを 1 本流したときの中央値です。pgmem はスナップショットからフォークします。docker run と devbox は
CREATE DATABASE … TEMPLATEとDROP DATABASE、Testcontainers は PostgreSQL モジュールのSnapshotとRestoreを使います。後者も内部ではテンプレートデータベースで同じことをしています。 - インデックス SELECT:pgx で
SELECT v FROM bench WHERE id = $1をウォームアップ後に 3,000 回。pgmem の 2 つ目の値は、TCP の代わりにプロセス内ダイヤラ(Server.Dial)を使ったものです。 - 20万行ソート:
SELECT count(*) FROM (SELECT md5(i::text) AS h FROM generate_series(1, 200000) i ORDER BY h) tを、ウォームアップ 1 回の後に 3 回流した中央値です。 - メモリ:負荷中に OrbStack VM がメモリを返すと、コンテナの使用量が多くても Testcontainers の値が
docker runより小さく見えることがあります。そこでグラフは VM の差分を使わず、DB 側の使用量と計測プロセスの増分を比べます。pgmem はプロセスのphys_footprint増分、バイナリは単独の pgmem プロセスを使います。Docker と Testcontainers は各コンテナの Docker 使用量に Go の計測プロセスの増分を足します。Testcontainers には PostgreSQL と Ryuk が含まれ、メモリ値にはdocker statsの表示を使います(Linux CLI はキャッシュを差し引きます)。devbox は PostgreSQL のポストマスターと子プロセスを合算します。OrbStack VM の差分と絶対値は表に分けて載せています。 - ダウンロード:初回に取得するもの。Go ライブラリとしての pgmem は、strip したテストバイナリがリンクによって増える量です。バイナリの行は wheel、jar、npm パッケージが同梱する圧縮済みバイナリの大きさです。イメージは計測したアーキテクチャの圧縮レイヤ、nix closure は devbox が cache.nixos.org から取得する量です。
数字の読み方
Section titled “数字の読み方”- 既定値が違います。 サーバーは PostgreSQL の既定値(
shared_buffers=128MB)を使います。Testcontainers の計測では PostgreSQL 18 のデータディレクトリ(/var/lib/postgresql/18/docker)を tmpfs にマウントし、docker run と devbox はディスクを使います。pgmem はshared_buffers=32MBでファイルシステムもメモリ上にあるため、一時ファイルにあふれるソートでもディスクに触れません。 - グラフは DB と計測プロセスのメモリを比べます。 スキーマ準備後とクエリ・分離計測後の中央値および計測回ごとの範囲を示します。Testcontainers は PostgreSQL と Ryuk のコンテナ使用量を含みます。Linux の
docker statsCLI は表示値からキャッシュを差し引きます。 - OrbStack VM の差分は別の参考値です。 コンテナの計測ごとに VM を再起動し、アイドル時を記録します。VM は実行中にメモリを解放することがあり、コンテナ使用量が増えても VM の差分が下がる場合があります。表では差分と VM の絶対値を分けています。
- pgmem のメモリは負荷で増えます。 WebAssembly のリニアメモリはサーバー稼働中は OS に返らず、生きているフォークはそれぞれ自分の複製を持ちます。生きているフォーク 1 つあたりアイドル時の値程度を見込み、
MaxForksで数を抑えてください。 - コンテナ方式の差には実行条件も含まれます。 docker run はディスク上に保存しますが、Testcontainers は PGDATA を tmpfs に置き、Ryuk も起動します。そのため、差には保存先と起動方式の違いが含まれます。
cd bench/alternativesRUNS=5 MEM_RUNS=3 ./run.shOrbStack と devbox が必要です。メモリ計測のパスでは OrbStack を再起動しますが、ほかのコンテナが動いているときは再起動せずに止まります。生の計測値を results/raw.jsonl、中央値を results/summary.json に書き、要約を website/src/data/benchmarks.json にコピーします。このサイトの表はそのファイルを描画しています。