import time
import random
import tempfile
import shutil
import sys
import os
from pathlib import Path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'apexbase', 'python'))
from apexbase import ApexClient
def generate_test_data(n_rows: int):
return [
{
"int_col": i,
"float_col": float(i) * 1.5,
"str_col": f"value_{i % 1000}", "str_col2": f"unique_{i}", }
for i in range(n_rows)
]
def benchmark_sequential_read(client, table_name: str, n_iterations: int = 10):
times = []
for _ in range(n_iterations):
start = time.perf_counter()
result = client.execute(f"SELECT * FROM {table_name}")
_ = result.to_pandas()
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
max_time = max(times)
return {"avg": avg_time, "min": min_time, "max": max_time, "times": times}
def benchmark_column_projection(client, table_name: str, n_iterations: int = 10):
times = []
for _ in range(n_iterations):
start = time.perf_counter()
result = client.execute(f"SELECT int_col, str_col FROM {table_name}")
_ = result.to_pandas()
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
return {"avg": avg_time, "min": min_time}
def benchmark_filter_int(client, table_name: str, n_rows: int, n_iterations: int = 10):
mid = n_rows // 2
times = []
for _ in range(n_iterations):
start = time.perf_counter()
result = client.execute(f"SELECT * FROM {table_name} WHERE int_col >= {mid} AND int_col < {mid + 1000}")
_ = result.to_pandas()
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
return {"avg": avg_time, "min": min_time}
def benchmark_filter_string(client, table_name: str, n_iterations: int = 10):
times = []
for _ in range(n_iterations):
start = time.perf_counter()
result = client.execute(f"SELECT * FROM {table_name} WHERE str_col = 'value_500'")
_ = result.to_pandas()
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
return {"avg": avg_time, "min": min_time}
def benchmark_random_access(client, table_name: str, n_rows: int, n_samples: int = 100):
ids_result = client.execute(f"SELECT _id FROM {table_name}")
all_ids = [row["_id"] for row in ids_result]
sample_ids = random.sample(all_ids, min(n_samples, len(all_ids)))
times = []
for id_val in sample_ids:
start = time.perf_counter()
result = client.execute(f"SELECT * FROM {table_name} WHERE _id = {id_val}")
_ = list(result)
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
max_time = max(times)
return {"avg": avg_time, "min": min_time, "max": max_time, "total": sum(times)}
def benchmark_limit_query(client, table_name: str, n_iterations: int = 10):
times = []
for _ in range(n_iterations):
start = time.perf_counter()
result = client.execute(f"SELECT * FROM {table_name} WHERE str_col = 'value_100' LIMIT 10")
_ = list(result)
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
return {"avg": avg_time, "min": min_time}
def benchmark_aggregation(client, table_name: str, n_iterations: int = 10):
times = []
for _ in range(n_iterations):
start = time.perf_counter()
result = client.execute(f"SELECT str_col, COUNT(*), SUM(int_col), AVG(float_col) FROM {table_name} GROUP BY str_col")
_ = result.to_pandas()
elapsed = time.perf_counter() - start
times.append(elapsed)
avg_time = sum(times) / len(times)
min_time = min(times)
return {"avg": avg_time, "min": min_time}
def run_benchmarks(n_rows: int = 100_000):
print(f"\n{'='*60}")
print(f"ApexBase Read Performance Benchmark")
print(f"Rows: {n_rows:,}")
print(f"{'='*60}\n")
temp_dir = tempfile.mkdtemp(prefix="apexbase_bench_")
try:
client = ApexClient(temp_dir)
table_name = "bench_table"
client.create_table(table_name)
print("Generating test data...")
start = time.perf_counter()
data = generate_test_data(n_rows)
gen_time = time.perf_counter() - start
print(f" Data generation: {gen_time:.2f}s")
print("Inserting data...")
start = time.perf_counter()
client.store(data)
insert_time = time.perf_counter() - start
print(f" Insert time: {insert_time:.2f}s ({n_rows/insert_time:,.0f} rows/sec)")
print("\n--- Sequential Read Benchmarks ---")
result = benchmark_sequential_read(client, table_name)
rows_per_sec = n_rows / result["avg"]
print(f"Full table scan (SELECT *): avg={result['avg']*1000:.2f}ms min={result['min']*1000:.2f}ms ({rows_per_sec:,.0f} rows/sec)")
result = benchmark_column_projection(client, table_name)
rows_per_sec = n_rows / result["avg"]
print(f"Column projection (2 cols): avg={result['avg']*1000:.2f}ms min={result['min']*1000:.2f}ms ({rows_per_sec:,.0f} rows/sec)")
result = benchmark_filter_int(client, table_name, n_rows)
print(f"Integer range filter (1000 rows): avg={result['avg']*1000:.2f}ms min={result['min']*1000:.2f}ms")
result = benchmark_filter_string(client, table_name)
print(f"String equality filter: avg={result['avg']*1000:.2f}ms min={result['min']*1000:.2f}ms")
result = benchmark_limit_query(client, table_name)
print(f"String filter + LIMIT 10: avg={result['avg']*1000:.2f}ms min={result['min']*1000:.2f}ms")
result = benchmark_aggregation(client, table_name)
print(f"GROUP BY aggregation: avg={result['avg']*1000:.2f}ms min={result['min']*1000:.2f}ms")
print("\n--- Random Access Benchmarks ---")
result = benchmark_random_access(client, table_name, n_rows, n_samples=100)
print(f"Random single row by _id (100x): avg={result['avg']*1000:.3f}ms total={result['total']*1000:.1f}ms")
client.close()
print(f"\n{'='*60}")
print("Benchmark complete!")
print(f"{'='*60}\n")
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
run_benchmarks(n_rows=100_000)
print("\n" + "="*60)
print("Running larger benchmark (1M rows)...")
print("="*60)
run_benchmarks(n_rows=1_000_000)