Skip to content

Commit 8b9c14f

Browse files
committed
Squashed commit of the following:
commit ef2b5dc Author: Clement Rey <cr.rey.clement@gmail.com> Date: Sat Apr 1 16:15:11 2023 +0200 benchmarks for common vector ops across `smallvec`/`tinyvec`/std (#1747) * benchmarks for common vector ops * handle N=1 commit 731d941 Author: benjamin de charmoy <BenjaminDev@users.noreply.github.com> Date: Sat Apr 1 10:57:19 2023 +0200 Fix logged obb being displayed with half of the requested size (#1749) commit 1e84aa5 Author: Clement Rey <cr.rey.clement@gmail.com> Date: Fri Mar 31 17:43:57 2023 +0200 `arrow2_convert` primitive (de)serialization benchmarks (#1742) * arrow2_convert primitive benchmarks * addressing PR comments
1 parent 75b52f7 commit 8b9c14f

File tree

3 files changed

+337
-0
lines changed

3 files changed

+337
-0
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/re_arrow_store/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ polars-core = { workspace = true, features = [
8181
"sort_multiple",
8282
] }
8383
rand = "0.8"
84+
smallvec = { version = "1.0", features = ["const_generics", "union"] }
85+
tinyvec = { version = "1.6", features = ["alloc", "rustc_1_55"] }
8486

8587

8688
[lib]
@@ -119,3 +121,7 @@ harness = false
119121
[[bench]]
120122
name = "arrow2_convert"
121123
harness = false
124+
125+
[[bench]]
126+
name = "vectors"
127+
harness = false
+329
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
//! Keeping track of performance issues/regressions for common vector operations.
2+
3+
#[global_allocator]
4+
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
5+
6+
use criterion::{criterion_group, criterion_main, Criterion};
7+
8+
use smallvec::SmallVec;
9+
use tinyvec::TinyVec;
10+
11+
criterion_group!(benches, sort, split, swap, swap_opt);
12+
criterion_main!(benches);
13+
14+
// ---
15+
16+
#[cfg(not(debug_assertions))]
17+
const NUM_INSTANCES: usize = 10_000;
18+
#[cfg(not(debug_assertions))]
19+
const SMALLVEC_SIZE: usize = 4;
20+
21+
// `cargo test` also runs the benchmark setup code, so make sure they run quickly:
22+
#[cfg(debug_assertions)]
23+
const NUM_INSTANCES: usize = 1;
24+
#[cfg(debug_assertions)]
25+
const SMALLVEC_SIZE: usize = 1;
26+
27+
// --- Benchmarks ---
28+
29+
fn split(c: &mut Criterion) {
30+
let mut group = c.benchmark_group(format!("vector_ops/split_off/instances={NUM_INSTANCES}"));
31+
group.throughput(criterion::Throughput::Elements(NUM_INSTANCES as _));
32+
33+
{
34+
fn split_off<T: Copy, const N: usize>(
35+
data: &mut SmallVec<[T; N]>,
36+
split_idx: usize,
37+
) -> SmallVec<[T; N]> {
38+
if split_idx >= data.len() {
39+
return SmallVec::default();
40+
}
41+
42+
let second_half = SmallVec::from_slice(&data[split_idx..]);
43+
data.truncate(split_idx);
44+
second_half
45+
}
46+
47+
let data: SmallVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).collect();
48+
49+
group.bench_function(format!("smallvec/n={SMALLVEC_SIZE}/manual"), |b| {
50+
b.iter(|| {
51+
let mut data = data.clone();
52+
let second_half = split_off(&mut data, NUM_INSTANCES / 2);
53+
assert_eq!(NUM_INSTANCES, data.len() + second_half.len());
54+
assert_eq!(NUM_INSTANCES as i64 / 2, second_half[0]);
55+
(data, second_half)
56+
});
57+
});
58+
}
59+
60+
{
61+
let data: TinyVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).collect();
62+
63+
group.bench_function(format!("tinyvec/n={SMALLVEC_SIZE}"), |b| {
64+
b.iter(|| {
65+
let mut data = data.clone();
66+
let second_half = data.split_off(NUM_INSTANCES / 2);
67+
assert_eq!(NUM_INSTANCES, data.len() + second_half.len());
68+
assert_eq!(NUM_INSTANCES as i64 / 2, second_half[0]);
69+
(data, second_half)
70+
});
71+
});
72+
}
73+
74+
{
75+
fn split_off<T: Default + Copy, const N: usize>(
76+
data: &mut TinyVec<[T; N]>,
77+
split_idx: usize,
78+
) -> TinyVec<[T; N]> {
79+
if split_idx >= data.len() {
80+
return TinyVec::default();
81+
}
82+
83+
let second_half = TinyVec::from(&data[split_idx..]);
84+
data.truncate(split_idx);
85+
second_half
86+
}
87+
88+
let data: TinyVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).collect();
89+
90+
group.bench_function(format!("tinyvec/n={SMALLVEC_SIZE}/manual"), |b| {
91+
b.iter(|| {
92+
let mut data = data.clone();
93+
let second_half = split_off(&mut data, NUM_INSTANCES / 2);
94+
assert_eq!(NUM_INSTANCES, data.len() + second_half.len());
95+
assert_eq!(NUM_INSTANCES as i64 / 2, second_half[0]);
96+
(data, second_half)
97+
});
98+
});
99+
}
100+
101+
{
102+
let data: Vec<i64> = (0..NUM_INSTANCES as i64).collect();
103+
104+
group.bench_function("vec", |b| {
105+
b.iter(|| {
106+
let mut data = data.clone();
107+
let second_half = data.split_off(NUM_INSTANCES / 2);
108+
assert_eq!(NUM_INSTANCES, data.len() + second_half.len());
109+
assert_eq!(NUM_INSTANCES as i64 / 2, second_half[0]);
110+
(data, second_half)
111+
});
112+
});
113+
}
114+
115+
{
116+
fn split_off<T: Copy>(data: &mut Vec<T>, split_idx: usize) -> Vec<T> {
117+
if split_idx >= data.len() {
118+
return Vec::default();
119+
}
120+
121+
let second_half = Vec::from(&data[split_idx..]);
122+
data.truncate(split_idx);
123+
second_half
124+
}
125+
126+
let data: Vec<i64> = (0..NUM_INSTANCES as i64).collect();
127+
128+
group.bench_function("vec/manual", |b| {
129+
b.iter(|| {
130+
let mut data = data.clone();
131+
let second_half = split_off(&mut data, NUM_INSTANCES / 2);
132+
assert_eq!(NUM_INSTANCES, data.len() + second_half.len());
133+
assert_eq!(NUM_INSTANCES as i64 / 2, second_half[0]);
134+
(data, second_half)
135+
});
136+
});
137+
}
138+
}
139+
140+
fn sort(c: &mut Criterion) {
141+
let mut group = c.benchmark_group(format!("vector_ops/sort/instances={NUM_INSTANCES}"));
142+
group.throughput(criterion::Throughput::Elements(NUM_INSTANCES as _));
143+
144+
{
145+
let data: SmallVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).rev().collect();
146+
147+
group.bench_function(format!("smallvec/n={SMALLVEC_SIZE}"), |b| {
148+
b.iter(|| {
149+
let mut data = data.clone();
150+
data.sort_unstable();
151+
assert_eq!(NUM_INSTANCES, data.len());
152+
assert_eq!(NUM_INSTANCES as i64 / 2, data[NUM_INSTANCES / 2]);
153+
data
154+
});
155+
});
156+
}
157+
158+
{
159+
let data: TinyVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).rev().collect();
160+
161+
group.bench_function(format!("tinyvec/n={SMALLVEC_SIZE}"), |b| {
162+
b.iter(|| {
163+
let mut data = data.clone();
164+
data.sort_unstable();
165+
assert_eq!(NUM_INSTANCES, data.len());
166+
assert_eq!(NUM_INSTANCES as i64 / 2, data[NUM_INSTANCES / 2]);
167+
data
168+
});
169+
});
170+
}
171+
172+
{
173+
let data: Vec<i64> = (0..NUM_INSTANCES as i64).rev().collect();
174+
175+
group.bench_function("vec", |b| {
176+
b.iter(|| {
177+
let mut data = data.clone();
178+
data.sort_unstable();
179+
assert_eq!(NUM_INSTANCES, data.len());
180+
assert_eq!(NUM_INSTANCES as i64 / 2, data[NUM_INSTANCES / 2]);
181+
data
182+
});
183+
});
184+
}
185+
}
186+
187+
fn swap(c: &mut Criterion) {
188+
let mut group = c.benchmark_group(format!("vector_ops/swap/instances={NUM_INSTANCES}"));
189+
group.throughput(criterion::Throughput::Elements(NUM_INSTANCES as _));
190+
191+
{
192+
let data: SmallVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).collect();
193+
let swaps: SmallVec<[usize; SMALLVEC_SIZE]> = (0..NUM_INSTANCES).rev().collect();
194+
195+
group.bench_function(format!("smallvec/n={SMALLVEC_SIZE}"), |b| {
196+
b.iter(|| {
197+
let mut data1 = data.clone();
198+
let data2 = data.clone();
199+
for &swap in &swaps {
200+
data1[NUM_INSTANCES - swap - 1] = data2[swap];
201+
}
202+
assert_eq!(NUM_INSTANCES, data1.len());
203+
assert_eq!(NUM_INSTANCES, data2.len());
204+
assert_eq!(
205+
(NUM_INSTANCES as i64 / 2).max(1) - 1,
206+
data1[NUM_INSTANCES / 2]
207+
);
208+
(data1, data2)
209+
});
210+
});
211+
}
212+
213+
{
214+
let data: TinyVec<[i64; SMALLVEC_SIZE]> = (0..NUM_INSTANCES as i64).collect();
215+
let swaps: TinyVec<[usize; SMALLVEC_SIZE]> = (0..NUM_INSTANCES).rev().collect();
216+
217+
group.bench_function(format!("tinyvec/n={SMALLVEC_SIZE}"), |b| {
218+
b.iter(|| {
219+
let mut data1 = data.clone();
220+
let data2 = data.clone();
221+
for &swap in &swaps {
222+
data1[NUM_INSTANCES - swap - 1] = data2[swap];
223+
}
224+
assert_eq!(NUM_INSTANCES, data1.len());
225+
assert_eq!(NUM_INSTANCES, data2.len());
226+
assert_eq!(
227+
(NUM_INSTANCES as i64 / 2).max(1) - 1,
228+
data1[NUM_INSTANCES / 2]
229+
);
230+
(data1, data2)
231+
});
232+
});
233+
}
234+
235+
{
236+
let data: Vec<i64> = (0..NUM_INSTANCES as i64).collect();
237+
let swaps: Vec<usize> = (0..NUM_INSTANCES).rev().collect();
238+
239+
group.bench_function("vec", |b| {
240+
b.iter(|| {
241+
let mut data1 = data.clone();
242+
let data2 = data.clone();
243+
for &swap in &swaps {
244+
data1[NUM_INSTANCES - swap - 1] = data2[swap];
245+
}
246+
assert_eq!(NUM_INSTANCES, data1.len());
247+
assert_eq!(NUM_INSTANCES, data2.len());
248+
assert_eq!(
249+
(NUM_INSTANCES as i64 / 2).max(1) - 1,
250+
data1[NUM_INSTANCES / 2]
251+
);
252+
(data1, data2)
253+
});
254+
});
255+
}
256+
}
257+
258+
fn swap_opt(c: &mut Criterion) {
259+
let mut group = c.benchmark_group(format!("vector_ops/swap_opt/instances={NUM_INSTANCES}"));
260+
group.throughput(criterion::Throughput::Elements(NUM_INSTANCES as _));
261+
262+
{
263+
let data: SmallVec<[Option<i64>; SMALLVEC_SIZE]> =
264+
(0..NUM_INSTANCES as i64).map(Some).collect();
265+
let swaps: SmallVec<[usize; SMALLVEC_SIZE]> = (0..NUM_INSTANCES).rev().collect();
266+
267+
group.bench_function(format!("smallvec/n={SMALLVEC_SIZE}"), |b| {
268+
b.iter(|| {
269+
let mut data1 = data.clone();
270+
let mut data2 = data.clone();
271+
for &swap in &swaps {
272+
data1[NUM_INSTANCES - swap - 1] = data2[swap].take();
273+
}
274+
assert_eq!(NUM_INSTANCES, data1.len());
275+
assert_eq!(NUM_INSTANCES, data2.len());
276+
assert_eq!(
277+
Some((NUM_INSTANCES as i64 / 2).max(1) - 1),
278+
data1[NUM_INSTANCES / 2]
279+
);
280+
(data1, data2)
281+
});
282+
});
283+
}
284+
285+
{
286+
let data: TinyVec<[Option<i64>; SMALLVEC_SIZE]> =
287+
(0..NUM_INSTANCES as i64).map(Some).collect();
288+
let swaps: TinyVec<[usize; SMALLVEC_SIZE]> = (0..NUM_INSTANCES).rev().collect();
289+
290+
group.bench_function(format!("tinyvec/n={SMALLVEC_SIZE}"), |b| {
291+
b.iter(|| {
292+
let mut data1 = data.clone();
293+
let mut data2 = data.clone();
294+
for &swap in &swaps {
295+
data1[NUM_INSTANCES - swap - 1] = data2[swap].take();
296+
}
297+
assert_eq!(NUM_INSTANCES, data1.len());
298+
assert_eq!(NUM_INSTANCES, data2.len());
299+
assert_eq!(
300+
Some((NUM_INSTANCES as i64 / 2).max(1) - 1),
301+
data1[NUM_INSTANCES / 2]
302+
);
303+
(data1, data2)
304+
});
305+
});
306+
}
307+
308+
{
309+
let data: Vec<Option<i64>> = (0..NUM_INSTANCES as i64).map(Some).collect();
310+
let swaps: Vec<usize> = (0..NUM_INSTANCES).rev().collect();
311+
312+
group.bench_function("vec", |b| {
313+
b.iter(|| {
314+
let mut data1 = data.clone();
315+
let mut data2 = data.clone();
316+
for &swap in &swaps {
317+
data1[NUM_INSTANCES - swap - 1] = data2[swap].take();
318+
}
319+
assert_eq!(NUM_INSTANCES, data1.len());
320+
assert_eq!(NUM_INSTANCES, data2.len());
321+
assert_eq!(
322+
Some((NUM_INSTANCES as i64 / 2).max(1) - 1),
323+
data1[NUM_INSTANCES / 2]
324+
);
325+
(data1, data2)
326+
});
327+
});
328+
}
329+
}

0 commit comments

Comments
 (0)