Files
fusion-harness/live_final_generation/project-root/sqlite-bulk-benchmark-BUILDER-openai-gpt-5.6-sol.py
IndyDevDan 5852f2ed4f 🚀
2026-07-19 20:30:34 -05:00

632 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.11"
# ///
"""SQLite bulk-insert benchmark: elapsed time and process peak RSS.
Run:
uv run sqlite-bulk-benchmark-BUILDER-openai-gpt-5.6-sol.py
Every timed trial executes in a fresh child process against a fresh database.
The deliberately slow autocommit case uses a prefix sample and reports a
clearly marked linear projection to one million rows.
"""
from __future__ import annotations
import argparse
import gc
import itertools
import json
import os
import platform
import random
import sqlite3
import subprocess
import sys
import tempfile
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, Iterator, Sequence
IDENTITY = "BUILDER-openai-gpt-5.6-sol"
DEFAULT_ROWS = 1_000_000
DEFAULT_BASELINE_ROWS = 10_000
DEFAULT_REPEATS = 2
EXECUTEMANY_BATCH_ROWS = 10_000
MULTI_VALUES_TARGET_ROWS = 500
COLUMN_COUNT = 6
INSERT_ONE = """
INSERT INTO events
(id, account_id, created_at, amount_cents, status, payload)
VALUES (?, ?, ?, ?, ?, ?)
"""
SCHEMA = """
CREATE TABLE events (
id INTEGER PRIMARY KEY,
account_id INTEGER NOT NULL,
created_at INTEGER NOT NULL,
amount_cents INTEGER NOT NULL,
status TEXT NOT NULL CHECK (status IN ('new', 'paid', 'sent', 'void')),
payload TEXT NOT NULL
)
"""
SET_BASED_INSERT = """
WITH RECURSIVE
seq(i) AS (
VALUES(0)
UNION ALL
SELECT i + 1 FROM seq WHERE i + 1 < ?
),
generated(i, mix) AS (
SELECT i, (i * 1103515245 + 12345) % 2147483648
FROM seq
)
INSERT INTO events
(id, account_id, created_at, amount_cents, status, payload)
SELECT
i,
mix % 100003,
1700000000 + i,
(mix % 2000001) - 1000000,
CASE i % 4
WHEN 0 THEN 'new'
WHEN 1 THEN 'paid'
WHEN 2 THEN 'sent'
ELSE 'void'
END,
printf(
'event-%010d-%010x-abcdefghijklmnopqrstuvwxyz0123456789',
i,
mix
)
FROM generated
"""
DIGEST_SQL = """
SELECT
count(*),
sum(id),
sum(account_id),
sum(created_at),
sum(amount_cents),
sum(length(status)),
sum(length(payload)),
min(id),
max(id),
min(payload),
max(payload)
FROM events
"""
@dataclass(frozen=True)
class Strategy:
key: str
label: str
journal_mode: str
synchronous: str
implementation: str
durability: str
sampled: bool = False
STRATEGIES: tuple[Strategy, ...] = (
Strategy(
"naive_autocommit",
"naive autocommit",
"DELETE",
"FULL",
"naive",
"durable",
sampled=True,
),
Strategy(
"one_big_transaction",
"one transaction + execute",
"DELETE",
"FULL",
"one_transaction",
"durable",
),
Strategy(
"executemany_streamed",
"streamed executemany batches",
"DELETE",
"FULL",
"executemany",
"durable",
),
Strategy(
"wal_multi_values",
"WAL/NORMAL + multi-VALUES",
"WAL",
"NORMAL",
"multi_values",
"consistent; latest commit power-loss risk",
),
Strategy(
"off_multi_values",
"OFF/OFF + multi-VALUES",
"OFF",
"OFF",
"multi_values",
"rebuildable staging only",
),
Strategy(
"wal_set_based",
"WAL/NORMAL + INSERT-SELECT",
"WAL",
"NORMAL",
"set_based",
"consistent; latest commit power-loss risk",
),
Strategy(
"off_set_based",
"OFF/OFF + INSERT-SELECT",
"OFF",
"OFF",
"set_based",
"rebuildable staging only",
),
)
STRATEGY_BY_KEY = {strategy.key: strategy for strategy in STRATEGIES}
Row = tuple[int, int, int, int, str, str]
Digest = tuple[int | str, ...]
def make_row(i: int) -> Row:
"""The sole Python definition of benchmark data."""
mix = (i * 1103515245 + 12345) % 2147483648
status = ("new", "paid", "sent", "void")[i % 4]
return (
i,
mix % 100003,
1700000000 + i,
(mix % 2000001) - 1000000,
status,
f"event-{i:010d}-{mix:010x}-abcdefghijklmnopqrstuvwxyz0123456789",
)
def generated_rows(start: int, stop: int) -> Iterator[Row]:
for i in range(start, stop):
yield make_row(i)
def expected_digest(row_count: int) -> Digest:
count = 0
id_sum = account_sum = created_sum = amount_sum = 0
status_length_sum = payload_length_sum = 0
min_payload: str | None = None
max_payload: str | None = None
for row in generated_rows(0, row_count):
row_id, account_id, created_at, amount, status, payload = row
count += 1
id_sum += row_id
account_sum += account_id
created_sum += created_at
amount_sum += amount
status_length_sum += len(status)
payload_length_sum += len(payload)
if min_payload is None or payload < min_payload:
min_payload = payload
if max_payload is None or payload > max_payload:
max_payload = payload
return (
count,
id_sum,
account_sum,
created_sum,
amount_sum,
status_length_sum,
payload_length_sum,
0 if row_count else None,
row_count - 1 if row_count else None,
min_payload,
max_payload,
)
def peak_rss_bytes() -> int:
if os.name == "nt":
# ctypes is standard library. PeakWorkingSetSize is the Windows
# equivalent of process peak RSS.
import ctypes
from ctypes import wintypes
class ProcessMemoryCounters(ctypes.Structure):
_fields_ = (
("cb", wintypes.DWORD),
("PageFaultCount", wintypes.DWORD),
("PeakWorkingSetSize", ctypes.c_size_t),
("WorkingSetSize", ctypes.c_size_t),
("QuotaPeakPagedPoolUsage", ctypes.c_size_t),
("QuotaPagedPoolUsage", ctypes.c_size_t),
("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t),
("QuotaNonPagedPoolUsage", ctypes.c_size_t),
("PagefileUsage", ctypes.c_size_t),
("PeakPagefileUsage", ctypes.c_size_t),
)
counters = ProcessMemoryCounters()
counters.cb = ctypes.sizeof(counters)
get_current_process = ctypes.windll.kernel32.GetCurrentProcess
get_current_process.restype = wintypes.HANDLE
ok = ctypes.windll.psapi.GetProcessMemoryInfo(
get_current_process(), ctypes.byref(counters), counters.cb
)
if not ok:
raise ctypes.WinError()
return int(counters.PeakWorkingSetSize)
import resource
raw = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# macOS reports bytes; Linux and the BSDs report KiB.
if sys.platform == "darwin":
return int(raw)
return int(raw) * 1024
def setup_connection(db_path: Path, strategy: Strategy) -> sqlite3.Connection:
connection = sqlite3.connect(db_path, isolation_level=None, timeout=60.0)
connection.execute("PRAGMA page_size=4096")
actual_journal = connection.execute(
f"PRAGMA journal_mode={strategy.journal_mode}"
).fetchone()[0]
if str(actual_journal).upper() != strategy.journal_mode:
raise RuntimeError(
f"requested journal_mode={strategy.journal_mode}, got {actual_journal}"
)
connection.execute(f"PRAGMA synchronous={strategy.synchronous}")
# Common controls: bounded cache, no mmap RSS ambiguity, disk-backed temp
# structures, and spill enabled. These are identical for every strategy.
connection.execute("PRAGMA cache_size=-2048")
connection.execute("PRAGMA cache_spill=ON")
connection.execute("PRAGMA temp_store=FILE")
connection.execute("PRAGMA mmap_size=0")
connection.execute("PRAGMA foreign_keys=ON")
connection.execute(SCHEMA)
return connection
def finish_commit(connection: sqlite3.Connection, strategy: Strategy) -> None:
connection.commit()
# Include WAL's eventual write-back cost, rather than declaring victory
# while the complete database still resides in a large sidecar file.
if strategy.journal_mode == "WAL":
result = connection.execute("PRAGMA wal_checkpoint(TRUNCATE)").fetchone()
if result[0] != 0:
raise RuntimeError(f"WAL checkpoint was busy: {result!r}")
def insert_naive(connection: sqlite3.Connection, row_count: int, strategy: Strategy) -> None:
# isolation_level=None makes every statement its own transaction.
for row in generated_rows(0, row_count):
connection.execute(INSERT_ONE, row)
def insert_one_transaction(
connection: sqlite3.Connection, row_count: int, strategy: Strategy
) -> None:
connection.execute("BEGIN IMMEDIATE")
for row in generated_rows(0, row_count):
connection.execute(INSERT_ONE, row)
finish_commit(connection, strategy)
def insert_executemany(
connection: sqlite3.Connection, row_count: int, strategy: Strategy
) -> None:
connection.execute("BEGIN IMMEDIATE")
for start in range(0, row_count, EXECUTEMANY_BATCH_ROWS):
stop = min(start + EXECUTEMANY_BATCH_ROWS, row_count)
# An iterator, not a list: batching does not materialize input rows.
connection.executemany(INSERT_ONE, generated_rows(start, stop))
finish_commit(connection, strategy)
def insert_multi_values(
connection: sqlite3.Connection, row_count: int, strategy: Strategy
) -> None:
try:
variable_limit = connection.getlimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER)
except AttributeError:
variable_limit = 999
batch_rows = max(
1, min(MULTI_VALUES_TARGET_ROWS, variable_limit // COLUMN_COUNT)
)
placeholder = "(" + ",".join("?" for _ in range(COLUMN_COUNT)) + ")"
connection.execute("BEGIN IMMEDIATE")
for start in range(0, row_count, batch_rows):
stop = min(start + batch_rows, row_count)
batch = list(generated_rows(start, stop))
statement = (
"INSERT INTO events "
"(id, account_id, created_at, amount_cents, status, payload) VALUES "
+ ",".join(itertools.repeat(placeholder, len(batch)))
)
bindings = [value for row in batch for value in row]
connection.execute(statement, bindings)
finish_commit(connection, strategy)
def insert_set_based(
connection: sqlite3.Connection, row_count: int, strategy: Strategy
) -> None:
connection.execute("BEGIN IMMEDIATE")
connection.execute(SET_BASED_INSERT, (row_count,))
finish_commit(connection, strategy)
IMPLEMENTATIONS = {
"naive": insert_naive,
"one_transaction": insert_one_transaction,
"executemany": insert_executemany,
"multi_values": insert_multi_values,
"set_based": insert_set_based,
}
def worker(strategy_key: str, db_path: Path, row_count: int) -> int:
strategy = STRATEGY_BY_KEY[strategy_key]
if db_path.exists():
db_path.unlink()
connection = setup_connection(db_path, strategy)
implementation = IMPLEMENTATIONS[strategy.implementation]
gc_was_enabled = gc.isenabled()
gc.disable()
started_ns = time.perf_counter_ns()
try:
implementation(connection, row_count, strategy)
except BaseException:
if connection.in_transaction:
connection.rollback()
raise
finally:
if gc_was_enabled:
gc.enable()
elapsed_ns = time.perf_counter_ns() - started_ns
# Snapshot peak before the untimed validation query.
rss_bytes = peak_rss_bytes()
digest = tuple(connection.execute(DIGEST_SQL).fetchone())
quick_check = connection.execute("PRAGMA quick_check").fetchone()[0]
connection.close()
print(
json.dumps(
{
"strategy": strategy.key,
"rows": row_count,
"elapsed_seconds": elapsed_ns / 1_000_000_000,
"peak_rss_bytes": rss_bytes,
"digest": digest,
"quick_check": quick_check,
},
separators=(",", ":"),
)
)
return 0
def format_seconds_list(results: Sequence[dict[str, object]]) -> str:
return ",".join(f"{float(result['elapsed_seconds']):.3f}" for result in results)
def format_rss_list(results: Sequence[dict[str, object]]) -> str:
mib = 1024 * 1024
return ",".join(f"{int(result['peak_rss_bytes']) / mib:.1f}" for result in results)
def median(values: Sequence[float]) -> float:
ordered = sorted(values)
middle = len(ordered) // 2
if len(ordered) % 2:
return ordered[middle]
return (ordered[middle - 1] + ordered[middle]) / 2
def print_table(
grouped: dict[str, list[dict[str, object]]],
total_rows: int,
expected_by_count: dict[int, Digest],
) -> tuple[Strategy, Strategy]:
headers = (
"Strategy",
"Mode",
"Rows/run",
"Trial time(s)",
"Time@1M(s)",
"Peak RAM/run MiB",
"Data",
)
rows: list[tuple[str, ...]] = []
normalized_times: dict[str, float] = {}
peak_by_strategy: dict[str, int] = {}
for strategy in STRATEGIES:
results = grouped[strategy.key]
elapsed = [float(result["elapsed_seconds"]) for result in results]
row_count = int(results[0]["rows"])
projected = median(elapsed) * total_rows / row_count
normalized_times[strategy.key] = projected
peak_by_strategy[strategy.key] = max(
int(result["peak_rss_bytes"]) for result in results
)
verified = all(
tuple(result["digest"]) == expected_by_count[int(result["rows"])]
and result["quick_check"] == "ok"
for result in results
)
projection_marker = "*" if strategy.sampled else ""
rows.append(
(
strategy.label,
f"{strategy.journal_mode}/{strategy.synchronous}",
f"{row_count:,}",
format_seconds_list(results),
f"{projected:.3f}{projection_marker}",
format_rss_list(results),
"OK" if verified else "FAIL",
)
)
widths = [len(header) for header in headers]
for row in rows:
for index, value in enumerate(row):
widths[index] = max(widths[index], len(value))
def render(row: Sequence[str]) -> str:
return " | ".join(value.ljust(widths[i]) for i, value in enumerate(row))
print(render(headers))
print("-+-".join("-" * width for width in widths))
for row in rows:
print(render(row))
speed_key = min(normalized_times, key=normalized_times.get)
# The sampled baseline is not eligible for the memory title: its peak RSS
# was measured honestly but it did not execute the full row count.
full_keys = [strategy.key for strategy in STRATEGIES if not strategy.sampled]
memory_key = min(full_keys, key=peak_by_strategy.get)
return STRATEGY_BY_KEY[speed_key], STRATEGY_BY_KEY[memory_key]
def orchestrator(args: argparse.Namespace) -> int:
if args.rows <= 0 or args.baseline_rows <= 0 or args.repeats <= 0:
raise SystemExit("--rows, --baseline-rows, and --repeats must be positive")
baseline_rows = min(args.baseline_rows, args.rows)
print("SQLite bulk insert benchmark")
print(f"Identity: {IDENTITY}")
print(f"Python: {platform.python_version()} ({sys.executable})")
print(f"SQLite: {sqlite3.sqlite_version}")
print(f"Platform: {platform.platform()}")
print(f"Rows: {args.rows:,}")
print(
f"Autocommit sample: {baseline_rows:,} rows; its Time@1M is a linear projection"
)
print(f"Full-strategy trials: {args.repeats}")
print(
"Timed interval: first BEGIN/INSERT through COMMIT; WAL trials also include "
"TRUNCATE checkpoint"
)
print("Peak RAM: per-worker process ru_maxrss; every trial uses a fresh process")
print()
print("Computing independent expected data digests...")
expected_by_count = {
baseline_rows: expected_digest(baseline_rows),
args.rows: expected_digest(args.rows),
}
jobs: list[tuple[Strategy, int, int]] = []
for strategy in STRATEGIES:
trial_count = 1 if strategy.sampled else args.repeats
row_count = baseline_rows if strategy.sampled else args.rows
for trial in range(1, trial_count + 1):
jobs.append((strategy, trial, row_count))
random.Random(0x5A17E).shuffle(jobs)
grouped: dict[str, list[dict[str, object]]] = {
strategy.key: [] for strategy in STRATEGIES
}
script_path = Path(__file__).resolve()
child_environment = os.environ.copy()
child_environment["PYTHONHASHSEED"] = "0"
with tempfile.TemporaryDirectory(prefix=f"sqlite-bench-{IDENTITY}-") as temp_name:
temp_dir = Path(temp_name)
for job_number, (strategy, trial, row_count) in enumerate(jobs, start=1):
db_path = temp_dir / f"{IDENTITY}-{strategy.key}-trial-{trial}.sqlite3"
print(
f"[{job_number:02d}/{len(jobs):02d}] {strategy.label}, "
f"trial {trial}, {row_count:,} rows...",
flush=True,
)
completed = subprocess.run(
[
sys.executable,
str(script_path),
"--worker",
strategy.key,
"--db",
str(db_path),
"--worker-rows",
str(row_count),
],
check=False,
capture_output=True,
text=True,
env=child_environment,
timeout=args.worker_timeout,
)
if completed.returncode != 0:
raise RuntimeError(
f"worker failed for {strategy.key}:\n"
f"stdout:\n{completed.stdout}\n"
f"stderr:\n{completed.stderr}"
)
result = json.loads(completed.stdout.strip().splitlines()[-1])
grouped[strategy.key].append(result)
for results in grouped.values():
results.sort(key=lambda item: float(item["elapsed_seconds"]))
print("\nResults")
speed_winner, memory_winner = print_table(
grouped, args.rows, expected_by_count
)
print(
"\n* naive autocommit Time@1M is measured-time × "
f"{args.rows:,}/{baseline_rows:,}; its RAM is measured, not projected."
)
print(
"OFF/OFF rows are semantically identical but the database is rebuildable-only; "
"a crash can corrupt it."
)
print(
"INSERT-SELECT is applicable when rows can be generated in SQL or selected "
"from an attached SQLite source."
)
print(f"\nSpeed Winner: {speed_winner.label}")
print(f"Memory Winner: {memory_winner.label}")
return 0
def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--rows", type=int, default=DEFAULT_ROWS)
parser.add_argument("--baseline-rows", type=int, default=DEFAULT_BASELINE_ROWS)
parser.add_argument("--repeats", type=int, default=DEFAULT_REPEATS)
parser.add_argument("--worker-timeout", type=float, default=180.0)
parser.add_argument("--worker", choices=tuple(STRATEGY_BY_KEY))
parser.add_argument("--db", type=Path)
parser.add_argument("--worker-rows", type=int)
args = parser.parse_args(argv)
if args.worker and (args.db is None or args.worker_rows is None):
parser.error("--worker requires --db and --worker-rows")
return args
def main(argv: Sequence[str] | None = None) -> int:
args = parse_args(argv)
if args.worker:
return worker(args.worker, args.db, args.worker_rows)
return orchestrator(args)
if __name__ == "__main__":
raise SystemExit(main())