Practical Spark benchmark focused on testing performance decisions with measurable evidence.
Main hypothesis tested in this project:
Hypothesis: applying
ZORDER BY (customer_id)should improve customer-level filtering queries after data is written in Delta format.
I also compare:
- baseline joins (no partition strategy / no broadcast)
- optimized path (partitioned write + broadcast joins)
- an intentional "bad" attempt (aggressive repartition) to document a real trade-off
efficiency-performance-tuning-framework/
├─ data/
├─ reports/
│ └─ performance_notes.md
├─ src/
│ └─ tuning_framework.py
├─ requirements.txt
└─ README.md
python -m venv .venv
.venv\\Scripts\\activate
pip install -r requirements.txtpython src/tuning_framework.py --rows 5000000Main outputs:
reports/benchmark_results.csvreports/explain_baseline_groupby.txtreports/explain_optimized_groupby.txtreports/explain_customer_filter_before_zorder.txtreports/explain_customer_filter_after_zorder.txt
Optional arguments:
python src/tuning_framework.py --rows 5000000 --probe-customer-id 42 --reports-dir reportsThe benchmark exports practical metrics, such as:
- baseline vs optimized runtime
- customer filter runtime before/after
ZORDER - exchange node count (proxy for shuffle pressure)
- Delta data file count and data size
- result of the high-partition attempt vs baseline
To avoid a tutorial-style perfect dataset, the synthetic data intentionally includes:
- skewed
region_iddistribution (region_id=1concentrated) - random nulls in
status - out-of-order timestamps and some late-like timestamp behavior
-
Partitioning by
region_id- Reduces broad scans for regional workloads and gives predictable write layout.
-
Z-Order (Delta Lake)
- Tested specifically for customer-filter query behavior.
- Automatically skipped if the runtime does not support
OPTIMIZE.
-
Broadcast Joins
- Broadcasts small dimensions (
customers,regions) to reduce join shuffle.
- Broadcasts small dimensions (
-
Failed attempt documented
- Aggressive repartition (
600) is kept as an explicit experiment because it often increases shuffle overhead.
- Aggressive repartition (
- Runtime can vary by cluster size, file compaction state, and concurrent jobs.
OPTIMIZE ... ZORDERdepends on Delta-compatible runtime support.- On Windows local runs, Spark may require
HADOOP_HOME/winutils.exeto initialize correctly. - Benchmark is interpreted by trend and relative gain, not absolute wall-clock only.
Study notes and lessons are documented in LESSONS_LEARNED.md.