Skip to content

Commit cbdcade

Browse files
bench(vortex-geo): add collect benchmark
Signed-off-by: Nemo Yu <zyu379@wisc.edu>
1 parent 3f83f34 commit cbdcade

2 files changed

Lines changed: 153 additions & 0 deletions

File tree

vortex-geo/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,9 @@ harness = false
7070
name = "area"
7171
harness = false
7272

73+
[[bench]]
74+
name = "collect"
75+
harness = false
76+
7377
[lints]
7478
workspace = true

vortex-geo/benches/collect.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Microbenchmarks for native `ST_Collect` over homogeneous geometry lists.
5+
//!
6+
//! The cases cover each strict overload and the inner-null compaction path. They execute the
7+
//! result to its canonical representation so the full multi-geometry construction is measured.
8+
//!
9+
//! Run with `cargo bench -p vortex-geo --bench collect`.
10+
11+
#![expect(clippy::unwrap_used)]
12+
13+
use std::sync::LazyLock;
14+
15+
use divan::Bencher;
16+
use divan::counter::ItemsCount;
17+
use mimalloc::MiMalloc;
18+
use vortex_array::ArrayRef;
19+
use vortex_array::Canonical;
20+
use vortex_array::ExecutionCtx;
21+
use vortex_array::IntoArray;
22+
use vortex_array::VortexSessionExecute;
23+
use vortex_array::arrays::ListArray;
24+
use vortex_array::arrays::PrimitiveArray;
25+
use vortex_array::validity::Validity;
26+
use vortex_geo::scalar_fn::collect::GeoCollect;
27+
use vortex_geo::test_harness::geo_session;
28+
use vortex_geo::test_harness::linestring_column;
29+
use vortex_geo::test_harness::nullable_point_column;
30+
use vortex_geo::test_harness::point_column;
31+
use vortex_geo::test_harness::polygon_column;
32+
use vortex_session::VortexSession;
33+
34+
// Scalar function execution allocates its output inside the timed region, so use the vendored
35+
// allocator instead of measuring glibc differences between CodSpeed runner images.
36+
#[global_allocator]
37+
static GLOBAL: MiMalloc = MiMalloc;
38+
39+
static SESSION: LazyLock<VortexSession> = LazyLock::new(geo_session);
40+
41+
const ROWS: usize = 512;
42+
43+
fn main() {
44+
divan::main();
45+
}
46+
47+
fn geometry_lists(elements: ArrayRef, elements_per_row: usize) -> ArrayRef {
48+
let offsets = PrimitiveArray::from_iter(
49+
(0..=ROWS).map(|row| u64::try_from(row * elements_per_row).unwrap()),
50+
)
51+
.into_array();
52+
ListArray::try_new(elements, offsets, Validity::NonNullable)
53+
.unwrap()
54+
.into_array()
55+
}
56+
57+
fn point_lists(nullable: bool) -> ArrayRef {
58+
const POINTS_PER_ROW: usize = 8;
59+
let len = ROWS * POINTS_PER_ROW;
60+
let points = if nullable {
61+
nullable_point_column(
62+
(0..len)
63+
.map(|i| (!i.is_multiple_of(8)).then_some((i as f64, (i + 1) as f64)))
64+
.collect(),
65+
)
66+
.unwrap()
67+
} else {
68+
point_column(
69+
(0..len).map(|i| i as f64).collect(),
70+
(0..len).map(|i| (i + 1) as f64).collect(),
71+
)
72+
.unwrap()
73+
};
74+
geometry_lists(points, POINTS_PER_ROW)
75+
}
76+
77+
fn linestring_lists() -> ArrayRef {
78+
const LINES_PER_ROW: usize = 4;
79+
let lines = linestring_column(
80+
(0..ROWS * LINES_PER_ROW)
81+
.map(|line| {
82+
(0..8)
83+
.map(|vertex| {
84+
let value = (line * 8 + vertex) as f64;
85+
(value, value + 1.0)
86+
})
87+
.collect()
88+
})
89+
.collect(),
90+
)
91+
.unwrap();
92+
geometry_lists(lines, LINES_PER_ROW)
93+
}
94+
95+
fn polygon_lists() -> ArrayRef {
96+
const POLYGONS_PER_ROW: usize = 2;
97+
let polygons = polygon_column(
98+
(0..ROWS * POLYGONS_PER_ROW)
99+
.map(|polygon| {
100+
let x = polygon as f64;
101+
vec![vec![
102+
(x, 0.0),
103+
(x + 1.0, 0.0),
104+
(x + 1.0, 1.0),
105+
(x, 1.0),
106+
(x, 0.0),
107+
]]
108+
})
109+
.collect(),
110+
)
111+
.unwrap();
112+
geometry_lists(polygons, POLYGONS_PER_ROW)
113+
}
114+
115+
fn collect(input: &ArrayRef, ctx: &mut ExecutionCtx) -> ArrayRef {
116+
GeoCollect::try_new_array(input.clone())
117+
.unwrap()
118+
.into_array()
119+
.execute::<Canonical>(ctx)
120+
.unwrap()
121+
.into_array()
122+
}
123+
124+
fn bench_collect(bencher: Bencher, input: ArrayRef) {
125+
let mut ctx = SESSION.create_execution_ctx();
126+
bencher
127+
.counter(ItemsCount::new(ROWS))
128+
.bench_local(|| collect(&input, &mut ctx));
129+
}
130+
131+
#[divan::bench]
132+
fn points(bencher: Bencher) {
133+
bench_collect(bencher, point_lists(false));
134+
}
135+
136+
#[divan::bench]
137+
fn linestrings(bencher: Bencher) {
138+
bench_collect(bencher, linestring_lists());
139+
}
140+
141+
#[divan::bench]
142+
fn polygons(bencher: Bencher) {
143+
bench_collect(bencher, polygon_lists());
144+
}
145+
146+
#[divan::bench]
147+
fn nullable_points(bencher: Bencher) {
148+
bench_collect(bencher, point_lists(true));
149+
}

0 commit comments

Comments
 (0)