Skip to content

Commit 3883c7a

Browse files
authored
feat: continuous s3 benchmarking (#2355)
1 parent 64f1e23 commit 3883c7a

File tree

8 files changed

+115
-25
lines changed

8 files changed

+115
-25
lines changed

.github/workflows/bench-pr.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ jobs:
156156
run: |
157157
echo "TMPDIR=/work" >> $GITHUB_ENV
158158
159-
- name: Run TPC-H benchmark
159+
- name: Run ${{ matrix.benchmark.name }} benchmark
160160
shell: bash
161161
env:
162162
BENCH_VORTEX_RATIOS: '.*'

.github/workflows/sql-benchmarks.yml

+48-12
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,26 @@ on:
99

1010
jobs:
1111
bench:
12-
runs-on: [ self-hosted, gcp ]
1312
strategy:
1413
fail-fast: false
1514
matrix:
16-
benchmark:
17-
- id: tpch
18-
name: TPC-H
19-
- id: clickbench
20-
name: Clickbench
15+
# Regarding "include:":
16+
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow#example-adding-configurations
17+
include:
18+
- id: tpch-nvme
19+
binary_name: tpch
20+
name: TPC-H on NVME
21+
cloud_provider: gcp
22+
- id: clickbench-nvme
23+
binary_name: clickbench
24+
name: Clickbench on NVME
25+
cloud_provider: gcp
26+
- id: tpch-s3
27+
binary_name: tpch
28+
name: TPC-H on S3
29+
cloud_provider: aws
30+
remote_storage: s3://vortex-bench-dev/tpch-sf1/
31+
runs-on: [ self-hosted, ${{ matrix.cloud_provider }} ]
2132
steps:
2233
- uses: actions/checkout@v4
2334
- uses: ./.github/actions/cleanup
@@ -27,13 +38,35 @@ jobs:
2738
run: |
2839
echo "TMPDIR=/work" >> $GITHUB_ENV
2940
30-
- name: Run ${{ matrix.benchmark.name }} benchmark
41+
- name: Run ${{ matrix.name }} benchmark
42+
if: matrix.remote_storage == null
3143
shell: bash
3244
env:
3345
BENCH_VORTEX_RATIOS: '.*'
3446
RUSTFLAGS: '-C target-cpu=native'
3547
run: |
36-
cargo run --bin ${{ matrix.benchmark.id }} --release -- -d gh-json | tee ${{ matrix.benchmark.id }}.json
48+
cargo run \
49+
--bin ${{ matrix.binary_name }} \
50+
--release \
51+
-- \
52+
-d gh-json \
53+
| tee results.json
54+
55+
- name: Run ${{ matrix.name }} benchmark
56+
if: matrix.remote_storage != null
57+
shell: bash
58+
env:
59+
BENCH_VORTEX_RATIOS: '.*'
60+
RUSTFLAGS: '-C target-cpu=native'
61+
run: |
62+
cargo run \
63+
--bin ${{ matrix.binary_name }} \
64+
--release \
65+
-- \
66+
--use-remote-data-dir ${{ matrix.remote_storage }} \
67+
--formats 'parquet,vortex' \
68+
-d gh-json \
69+
| tee results.json
3770
3871
- name: Setup AWS CLI
3972
uses: aws-actions/configure-aws-credentials@v4
@@ -55,21 +88,24 @@ jobs:
5588
| grep $base_commit_sha \
5689
> base.json
5790
58-
echo '# Benchmarks: ${{ matrix.benchmark.name }}' > comment.md
91+
echo '# Benchmarks: ${{ matrix.name }}' > comment.md
5992
echo '<details>' >> comment.md
6093
echo '<summary>Table of Results</summary>' >> comment.md
6194
echo '' >> comment.md
62-
uv run --no-project scripts/compare-benchmark-jsons.py base.json tpch.json \
95+
uv run --no-project scripts/compare-benchmark-jsons.py base.json results.json \
6396
>> comment.md
6497
echo '</details>' >> comment.md
6598
- name: Comment PR
6699
if: inputs.mode == 'pr'
67100
uses: thollander/actions-comment-pull-request@v3
68101
with:
69102
file-path: comment.md
70-
comment-tag: bench-pr-comment-tpch
103+
# There is exactly one comment per comment-tag. If a comment with this tag already exists,
104+
# this action will *update* the comment instead of posting a new comment. Therefore, each
105+
# unique benchmark configuration must have a unique comment-tag.
106+
comment-tag: bench-pr-comment-{{ matrix.id }}
71107
- name: Upload Benchmark Results
72108
if: inputs.mode == 'develop'
73109
shell: bash
74110
run: |
75-
bash scripts/cat-s3.sh vortex-benchmark-results-database data.json ${{ matrix.benchmark.id }}.json
111+
bash scripts/cat-s3.sh vortex-benchmark-results-database data.json results.json

bench-vortex/src/bin/clickbench.rs

+1
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ fn main() {
225225

226226
all_measurements.push(QueryMeasurement {
227227
query_idx,
228+
storage: "nvme".to_string(),
228229
time: fastest_result,
229230
format: *format,
230231
dataset: "clickbench".to_string(),

bench-vortex/src/bin/tpch.rs

+13
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,20 @@ async fn bench_main(
169169
fastest_result = fastest_result.min(elapsed);
170170
}
171171

172+
let storage = match url.scheme() {
173+
"s3" => "s3",
174+
"gcs" => "gcs",
175+
"file" => "nvme",
176+
otherwise => {
177+
println!("unknown URL scheme: {}", otherwise);
178+
return ExitCode::FAILURE;
179+
}
180+
}
181+
.to_owned();
182+
172183
measurements.push(QueryMeasurement {
173184
query_idx,
185+
storage,
174186
time: fastest_result,
175187
format,
176188
dataset: "tpch".to_string(),
@@ -196,6 +208,7 @@ async fn bench_main(
196208
.zip_eq(EXPECTED_ROW_COUNTS)
197209
.enumerate()
198210
.filter(|(idx, _)| queries.as_ref().map(|q| q.contains(idx)).unwrap_or(true))
211+
.filter(|(idx, _)| exclude_queries.as_ref().map(|excluded| !excluded.contains(idx)).unwrap_or(true))
199212
.for_each(|(idx, (row_count, expected_row_count))| {
200213
if row_count != expected_row_count {
201214
eprintln!("Mismatched row count {row_count} instead of {expected_row_count} in query {idx} for format {format:?}");

bench-vortex/src/measurements.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub trait ToGeneric {
1515
#[derive(Serialize)]
1616
pub struct JsonValue {
1717
pub name: String,
18+
pub storage: Option<String>,
1819
pub unit: String,
1920
pub value: u128,
2021
pub commit_id: String,
@@ -38,6 +39,7 @@ impl ToJson for GenericMeasurement {
3839
fn to_json(&self) -> JsonValue {
3940
JsonValue {
4041
name: self.name.clone(),
42+
storage: None,
4143
unit: "ns".to_string(),
4244
value: self.time.as_nanos(),
4345
commit_id: crate::GIT_COMMIT_ID.to_string(),
@@ -48,6 +50,8 @@ impl ToJson for GenericMeasurement {
4850
#[derive(Clone, Debug)]
4951
pub struct QueryMeasurement {
5052
pub query_idx: usize,
53+
/// The storage backend against which this test was run. One of: s3, gcs, nvme.
54+
pub storage: String,
5155
pub time: Duration,
5256
pub format: Format,
5357
pub dataset: String,
@@ -64,6 +68,7 @@ impl ToJson for QueryMeasurement {
6468

6569
JsonValue {
6670
name,
71+
storage: Some(self.storage.to_string()),
6772
unit: "ns".to_string(),
6873
value: self.time.as_nanos(),
6974
commit_id: crate::GIT_COMMIT_ID.to_string(),
@@ -76,10 +81,11 @@ impl ToGeneric for QueryMeasurement {
7681
GenericMeasurement {
7782
id: self.query_idx,
7883
name: format!(
79-
"{dataset}_q{query_idx:02}/{format}",
84+
"{dataset}_q{query_idx:02}_{storage}/{format}",
8085
dataset = self.dataset,
8186
format = self.format.name(),
82-
query_idx = self.query_idx
87+
query_idx = self.query_idx,
88+
storage = self.storage,
8389
),
8490
format: self.format,
8591
time: self.time,

benchmarks-website/code.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ window.initAndRender = (function () {
33
function stringToColor(str) {
44
// Random colours are generally pretty disgusting...
55
const MAP = {
6-
"vortex-file-uncompressed": '#98da8d',
7-
"vortex-file-compressed": '#23d100',
8-
"vortex-in-memory-no-pushdown": '#79a6df',
9-
"vortex-in-memory-pushdown": '#0c53ae',
106
"arrow": '#58067e',
117
"parquet": '#ef7f1d',
8+
"vortex-file-compressed": '#23d100',
129
};
1310

1411
if (MAP[str]) {
@@ -36,7 +33,8 @@ window.initAndRender = (function () {
3633
let groups = {
3734
"Random Access": new Map(),
3835
"Compression": new Map(),
39-
"TPC-H": new Map(),
36+
"TPC-H (NVME)": new Map(),
37+
"TPC-H (S3)": new Map(),
4038
"Clickbench": new Map(),
4139
};
4240

@@ -60,21 +58,27 @@ window.initAndRender = (function () {
6058
}
6159

6260
let {name, unit, value, commit} = benchmark_result;
61+
let storage = benchmark_result.storage;
6362
let group = undefined;
6463

6564
if (name.startsWith("random-access/")) {
6665
group = groups["Random Access"];
6766
} else if (name.includes("compress time/")) {
6867
group = groups["Compression"];
6968
} else if (name.startsWith("tpch_q")) {
70-
group = groups["TPC-H"];
69+
if (storage === undefined || storage == "nvme") {
70+
group = groups["TPC-H (NVME)"];
71+
} else {
72+
group = groups["TPC-H (S3)"];
73+
}
7174
} else if (name.startsWith("clickbench")) {
7275
group = groups["Clickbench"];
7376
} else {
7477
uncategorizable_names.add(name)
7578
continue
7679
}
7780

81+
7882
// Normalize name and units
7983
let [q, seriesName] = name.split("/");
8084
if (seriesName.endsWith(" throughput")) {
@@ -84,6 +88,7 @@ window.initAndRender = (function () {
8488
seriesName = seriesName.slice(0, seriesName.length - "throughput".length);
8589
q = q.replace("time", "throughput");
8690
}
91+
8792
let prettyQ = q.replace("_", " ")
8893
.toUpperCase()
8994
.replace("VORTEX:RAW SIZE", "VORTEX COMPRESSION RATIO");
@@ -336,7 +341,8 @@ window.initAndRender = (function () {
336341
}
337342

338343
function initAndRender(keptGroups) {
339-
let data = fetch('https://vortex-benchmark-results-database.s3.amazonaws.com/data.json')
344+
// let data = fetch('https://vortex-benchmark-results-database.s3.amazonaws.com/data.json')
345+
let data = fetch('data.json')
340346
.then(response => response.text())
341347
.then(parse_jsonl)
342348
.catch(error => console.error('unable to load data.json:', error));

benchmarks-website/index.html

+13-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
]),
3939
"renamedDatasets": undefined,
4040
}],
41-
["TPC-H", {
41+
["TPC-H (NVME)", {
4242
"keptCharts": undefined,
4343
"hiddenDatasets": undefined,
4444
"removedDatasets": new Set([
@@ -50,7 +50,18 @@
5050
"vortex-file-compressed": "vortex",
5151
},
5252
}],
53-
["Clickbench", undefined],
53+
["TPC-H (S3)", {
54+
"keptCharts": undefined,
55+
"hiddenDatasets": undefined,
56+
"renamedDatasets": {
57+
"vortex-file-compressed": "vortex",
58+
},
59+
}],
60+
["Clickbench", {
61+
"renamedDatasets" : {
62+
"vortex-file-compressed": "vortex",
63+
}
64+
}],
5465
]);
5566
</script>
5667
</head>

scripts/compare-benchmark-jsons.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,24 @@
2020
assert len(pr_commit_id) == 1, pr_commit_id
2121
pr_commit_id = next(iter(pr_commit_id))
2222

23-
df3 = pd.merge(base, pr, on="name", how="inner", suffixes=("_base", "_pr"))
23+
if "storage" not in base:
24+
# This means the base commit was generated in the pre-object-store days. We cannot give a true
25+
# diff because we're comparing different storage systems.
26+
pr
27+
print(
28+
pd.DataFrame(
29+
{
30+
"name": pr["name"],
31+
f"PR {pr_commit_id[:8]}": pr["value"],
32+
f"base {base_commit_id[:8]} (no S3 results found)": pd.NA,
33+
"ratio (PR/base)": pd.NA,
34+
"unit": pr["unit"],
35+
}
36+
).to_markdown(index=False)
37+
)
38+
sys.exit(0)
39+
40+
df3 = pd.merge(base, pr, on=["name", "storage"], how="right", suffixes=("_base", "_pr"))
2441

2542
assert df3["unit_base"].equals(df3["unit_pr"]), (df3["unit_base"], df3["unit_pr"])
2643

0 commit comments

Comments
 (0)