Skip to content

Platform Comparison Guide

This guide covers how to compare benchmark performance across platforms using the unified benchbox compare command. Compare SQL platforms, DataFrame platforms, or both.

Overview

BenchBox provides a unified comparison interface supporting:

  • SQL Platform Comparisons: DuckDB vs SQLite vs PostgreSQL vs ClickHouse
  • DataFrame Platform Comparisons: Polars vs Pandas vs DataFusion
  • Mixed Comparisons: Compare SQL and DataFrame platforms together
  • File-Based Comparisons: Compare previously saved result files
  • Performance Visualization: Generate charts and reports automatically

Same Data, Different Engines

A key BenchBox design principle: benchmark data is generated once as Parquet files and shared across all platforms. When you compare DuckDB, DataFusion, SQLite, and Polars at the same scale factor, every engine reads from the same Parquet dataset. This eliminates data variability and ensures performance differences reflect the engines themselves.

Every command reads the shared Parquet dataset. The comparisons use DuckDB as the baseline for both alternatives.

Terminal window
duckdb_result=$(benchbox run --platform duckdb --benchmark tpch --scale 1 --output ./duckdb-run --quiet)
datafusion_result=$(benchbox run --platform datafusion --benchmark tpch --scale 1 --output ./datafusion-run --quiet)
polars_result=$(benchbox run --platform polars-df --benchmark tpch --scale 1 --output ./polars-run --quiet)
benchbox compare "$duckdb_result" "$datafusion_result"
benchbox compare "$duckdb_result" "$polars_result"

Data is generated on the first run and cached in benchmark_runs/<benchmark>/sf<scale>/data/. Subsequent runs at the same scale factor reuse the cached data automatically.

External Table Mode (--table-mode external)

Use --table-mode external when you want engines to query staged files directly instead of materializing into native tables:

The first command uses the default native mode, which materializes data into platform-managed tables. The second uses external mode, which registers views or external tables over the staged files.

Terminal window
benchbox run --platform duckdb --benchmark tpch --scale 1 --table-mode native
benchbox run --platform duckdb --benchmark tpch --scale 1 --table-mode external

Key rules:

  • native remains the default.
  • external is incompatible with --tuning tuned.
  • Cloud platforms still upload/stage data in object storage during load; they skip COPY/CTAS materialization in external mode.

Current external-mode mechanisms:

PlatformExternal Mechanism
DuckDB / MotherDuckCREATE VIEW over read_parquet(...) / read_csv(...) / delta_scan(...)
DataFusion / PolarsDirect file scan path (create_external_tables delegates to existing scan loader)
AthenaExternal table registration over S3 Parquet (CTAS bypass)
SnowflakeExternal stage + CREATE EXTERNAL TABLE
BigQueryCREATE EXTERNAL TABLE ... OPTIONS(uris=[...]) over GCS
RedshiftSpectrum external schema + external table over S3
DatabricksCREATE TABLE ... USING PARQUET LOCATION ...
ClickHouse CloudCREATE VIEW ... AS SELECT * FROM s3()/gcs()
Trino / PrestoCREATE TABLE ... WITH (external_location, format='PARQUET')
Azure SynapsePolyBase external data source/file format + CREATE EXTERNAL TABLE

Unsupported: SQLite and PostgreSQL.

Quick Start

List Available Platforms

Terminal window
benchbox compare --list-platforms

Output shows installed SQL and DataFrame platforms:

SQL Platforms:
Installed:
duckdb (embedded, analytical)
sqlite (embedded, transactional)
postgresql (server, transactional)
clickhouse-local (embedded, analytical)
clickhouse-server (server, analytical)
Cloud (requires configuration):
snowflake (cloud, analytical)
bigquery (cloud, analytical)
databricks (cloud, analytical)
DataFrame Platforms:
Installed:
polars-df (expression, single_node) [lazy, streaming]
pandas-df (pandas, single_node) [standard]
datafusion-df (expression, single_node) [lazy]
Not installed:
pyspark-df (expression, distributed)
cudf-df (pandas, gpu_accelerated)

Compare SQL Platforms

This compares DuckDB with SQLite on TPC-H at scale factor 0.01.

Terminal window
benchbox compare -p duckdb -p sqlite --scale 0.01

Compare DataFrame Platforms

This compares Polars with Pandas.

Terminal window
benchbox compare -p polars-df -p pandas-df --scale 0.01

Compare Result Files

Terminal window
duckdb_result=$(benchbox run --platform duckdb --benchmark tpch --scale 0.1 --output ./duckdb-run --quiet)
sqlite_result=$(benchbox run --platform sqlite --benchmark tpch --scale 0.1 --output ./sqlite-run --quiet)
benchbox compare "$duckdb_result" "$sqlite_result"

Interactive Mode

Terminal window
benchbox compare

The interactive wizard guides you through:

  1. Choosing comparison type (platforms or files)
  2. Selecting platforms from an interactive list
  3. Configuring benchmark options
  4. Running the comparison

Command Reference

Terminal window
benchbox compare [OPTIONS] [RESULT_FILES]...

Modes

Mode is automatically detected from arguments:

ModeTriggerDescription
Run Mode-p/--platformRun benchmarks across platforms then compare
File ModeRESULT_FILESCompare existing result files
InteractiveNo argumentsLaunch interactive wizard

Platform Selection

OptionDescription
-p, --platform TEXTPlatforms to compare (triggers run mode). Repeatable
--type [sql|dataframe|auto]Platform type (default: auto-detect)
--list-platformsShow available platforms and exit

Benchmark Configuration (run mode)

OptionDefaultDescription
-b, --benchmarktpchBenchmark: tpch, tpcds, ssb, clickbench
-s, --scale FLOAT0.01Scale factor
-q, --queries TEXTallComma-separated query IDs (e.g., Q1,Q6,Q10)
--warmup INTEGER1Warmup iterations
--iterations INTEGER3Benchmark iterations

Output Options

OptionDefaultDescription
-o, --output PATHnoneOutput directory for results
--format [text|json|markdown|html]textOutput format
--generate-chartsfalseGenerate ASCII visualization charts in output directory
--theme [light|dark]lightChart color theme (with –generate-charts)
--data-dir DIRECTORYautoDirectory containing benchmark data

Query Plan Options (file mode)

OptionDefaultDescription
--include-plansfalseInclude query plan comparison in output
--plan-threshold FLOAT0.0Only show plans with similarity below threshold (0.0-1.0)

Usage Examples

SQL Platform Comparison

Compare embedded SQL databases:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
-p datafusion \
--scale 0.1 \
--iterations 5

Example output:

SQL Platform Comparison
Platforms: duckdb, sqlite, datafusion
Benchmark: tpch @ SF 0.1
Iterations: 5
Running benchmarks...
============================================================
RESULTS
============================================================
Fastest: duckdb
Slowest: sqlite
Platform Geomean (ms) Total (ms) Success
------------------------------------------------------------
duckdb 45.23 1025.34 100%
datafusion 67.89 1538.67 100%
sqlite 234.56 5318.90 100%
Query Winners:
Q1: duckdb
Q2: datafusion
Q3: duckdb
...
============================================================

DataFrame Platform Comparison

Compare DataFrame libraries:

Terminal window
benchbox compare \
-p polars-df \
-p pandas-df \
-p datafusion-df \
--scale 0.1

Mixed SQL and DataFrame Comparison

Compare across platform types:

Terminal window
benchbox compare \
-p duckdb \
-p polars-df \
--scale 0.1

Note: Mixed comparisons use SQL execution on SQL platforms and DataFrame API on DataFrame platforms. Results compare execution times for equivalent queries.

Cloud Platform Comparison

Compare cloud data warehouses (requires credentials):

Terminal window
benchbox compare \
-p snowflake \
-p bigquery \
-p redshift \
--benchmark tpch \
--scale 1

Generate Reports and Charts

Save results with visualization:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
--scale 1 \
--output ./comparison_results \
--format markdown \
--generate-charts \
--theme dark

Creates:

comparison_results/
├── comparison.md
├── comparison.json
└── charts/
├── performance_bar.txt
├── distribution_box.txt
└── query_heatmap.txt

Compare Result Files

Compare previously saved benchmark results. The two run commands export results from individual runs, and the compare command compares the result files:

Terminal window
duckdb_result=$(benchbox run --platform duckdb --benchmark tpch --scale 0.1 --output ./duckdb-run --quiet)
sqlite_result=$(benchbox run --platform sqlite --benchmark tpch --scale 0.1 --output ./sqlite-run --quiet)
benchbox compare "$duckdb_result" "$sqlite_result"

Specific Query Subset

Run only specific queries for faster iteration:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
--queries Q1,Q6,Q14,Q17 \
--iterations 10

Parallel Execution

Run platforms in parallel for faster comparisons:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
-p clickhouse-local \
--iterations 3

JSON Output for Automation

Export results for programmatic analysis:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
--format json \
--output ./results

The JSON output includes:

{
"benchmark_suite": "unified_comparison",
"timestamp": "2026-01-01T00:00:00+00:00",
"config": {
"platform_type": "sql",
"scale_factor": 0.01,
"benchmark": "tpch",
"query_ids": null,
"warmup_iterations": 1,
"benchmark_iterations": 3
},
"results": [
{
"platform": "duckdb",
"platform_type": "sql",
"query_results": [...],
"geometric_mean_ms": 45.23,
"total_time_ms": 1025.34,
"success_rate": 100.0
},
...
],
"summary": {
"platforms": ["duckdb", "sqlite"],
"fastest_platform": "duckdb",
"slowest_platform": "sqlite",
"speedup_ratio": 5.18,
"query_winners": {"Q1": "duckdb", ...}
}
}

Programmatic API

For custom analysis, use the Python API directly:

from benchbox.core.comparison import (
UnifiedBenchmarkSuite,
UnifiedBenchmarkConfig,
PlatformType,
run_unified_comparison,
)

Quick Comparison

from benchbox.core.comparison import run_unified_comparison
results = run_unified_comparison(
platforms=["duckdb", "sqlite"],
scale_factor=0.01,
)
for result in results:
print(f"{result.platform}: {result.geometric_mean_ms:.2f}ms")

Full Suite with Configuration

from benchbox.core.comparison import (
UnifiedBenchmarkSuite,
UnifiedBenchmarkConfig,
PlatformType,
)
config = UnifiedBenchmarkConfig(
platform_type=PlatformType.SQL,
scale_factor=0.1,
benchmark="tpch",
query_ids=["Q1", "Q6", "Q10"],
warmup_iterations=2,
benchmark_iterations=5,
)
suite = UnifiedBenchmarkSuite(config=config)
results = suite.run_comparison(platforms=["duckdb", "sqlite"])
summary = suite.get_summary(results)
print(f"Fastest: {summary.fastest_platform}")
print(f"Speedup ratio: {summary.speedup_ratio:.2f}x")

Generate Charts Programmatically

from benchbox.core.visualization import ResultPlotter, export_ascii
from benchbox.core.visualization.ascii.base import ChartOptions
plotter = ResultPlotter.from_sources(["results/duckdb.json", "results/sqlite.json"])
from benchbox.core.visualization.ascii import BarChart
from benchbox.core.visualization.ascii.bar_chart import BarData
bar_data = [BarData(label=r.platform, value=r.total_time_ms or 0) for r in plotter.results]
chart = BarChart(data=bar_data, title="Performance Comparison")
print(chart.render())

You can also use the CLI for automatic chart generation: benchbox visualize results/duckdb.json results/sqlite.json.

Platform Type Detection

detect_platform_type("polars-df") returns PlatformType.DATAFRAME. For the list of three platforms, detect_platform_types returns PlatformType.SQL as detected (the majority) and ["polars-df"] as inconsistent.

from benchbox.core.comparison import detect_platform_type, detect_platform_types
platform_type = detect_platform_type("polars-df")
detected, inconsistent = detect_platform_types(["duckdb", "sqlite", "polars-df"])

Platform Categories

SQL Platforms

CategoryPlatformsUse Case
EmbeddedDuckDB, SQLite, DataFusionLocal analysis, testing
ServerPostgreSQL, ClickHouseProduction workloads
CloudSnowflake, BigQuery, Redshift, DatabricksEnterprise, large scale

DataFrame Platforms

CategoryPlatformsUse Case
Single NodePolars, Pandas, DataFusionIn-memory, medium datasets
DistributedPySpark and DaskLarge datasets, cluster
GPU AcceleratedcuDFCUDA GPU acceleration

Best Practices

1. Start Small

Begin with SF 0.01 to verify everything works:

Terminal window
benchbox compare -p duckdb -p sqlite --scale 0.01

2. Use Multiple Iterations

Reduce variance with multiple iterations:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
--iterations 5 \
--warmup 2

3. Match Your Production Scale

Data SizeRecommended SF
< 1 GB0.01 - 0.1
1-10 GB0.1 - 1
10-100 GB1 - 10
> 100 GB10+

4. Compare Similar Platforms

For meaningful comparisons:

  • Compare platforms with similar deployment models
  • Use same hardware/resources
  • Run at the same scale factor

5. Document Your Results

Use markdown output for documentation:

Terminal window
benchbox compare \
-p duckdb \
-p sqlite \
--scale 1 \
--format markdown \
--output ./docs/benchmark_results

Interpreting Results

Geometric Mean

The geometric mean summarizes performance across all queries. Lower is better. It’s less sensitive to outliers than arithmetic mean.

Success Rate

Percentage of queries that completed successfully. 100% expected for production-ready platforms.

Query Winners

Shows which platform was fastest for each query:

  • Different platforms excel at different query types
  • Aggregation vs joins vs filtering
  • Use for workload-specific decisions

Speedup Ratio

Ratio between fastest and slowest platform:

  • > 1.0: Faster platform completed in less time
  • ~1.0: Platforms are approximately equal
  • Large ratios (5x+) indicate major performance differences

Troubleshooting

Data Not Found

Error: Data directory not found: benchmark_runs/tpch/sf001/data

Generate data first:

Terminal window
benchbox run --platform duckdb --benchmark tpch --scale 0.01

Platform Not Available

Warning: Platform clickhouse not available

Install the required extra:

Terminal window
uv add benchbox --extra clickhouse

Mixed Platform Types

Error: Cannot mix SQL and DataFrame platforms. Use --type to specify.

Either:

  1. Compare only same-type platforms
  2. Explicitly set --type sql or --type dataframe

Memory Issues at Large Scale

For large scale factors:

  • Use streaming-capable platforms (Polars, DuckDB)
  • Reduce concurrent platforms compared
  • Compare fewer platforms per invocation

Query Plan Comparison

When comparing result files, you can include query plan analysis to understand why performance changed, not just that it changed.

Include Plan Analysis

The first command compares files with plan analysis. The second shows only plans that changed significantly, meaning less than 90% similar (--plan-threshold 0.9).

Terminal window
benchbox compare baseline.json current.json --include-plans
benchbox compare baseline.json current.json --include-plans --plan-threshold 0.9

Plan Comparison Output

The plan comparison section shows:

ColumnDescription
SimilarityOverall plan similarity (0-100%)
Type ΔNumber of operator type mismatches
Prop ΔNumber of property mismatches
Perf ΔPerformance change (positive = slower)
StatusPlan status (Identical, Similar, Different, REGRESSION)

Plan-Correlated Regressions

The most valuable insight is plan-correlated regressions: queries where:

  1. The query plan changed (< 100% similarity)
  2. Performance degraded > 20%

These are highlighted specially because optimizer changes likely caused the regression.

Requirements

Plan comparison requires benchmark results captured with --capture-plans. The two run commands capture plans, and the compare command then compares with plan analysis:

Terminal window
baseline=$(benchbox run --platform duckdb --benchmark tpch --capture-plans --output ./baseline --quiet)
current=$(benchbox run --platform duckdb --benchmark tpch --capture-plans --output ./current --quiet)
benchbox compare "$baseline" "$current" --include-plans