Skip to content

Commit 09a57ad

Browse files
gatesnrobert3005
andauthored
Remove more scalar_at from sparse arrays (#7439)
| Benchmark case | Before (mean) | After (mean) | Speedup | |---|---:|---:|---:| | `extend_from_array_non_zctl_overlapping (1000, 8)` | `1.413 ms` | `80.71 us` | `17.5x` | | `extend_from_array_non_zctl_overlapping (1000, 32)` | `2.586 ms` | `83.67 us` | `30.9x` | | `extend_from_array_non_zctl_overlapping (10000, 8)` | `9.708 ms` | `560.3 us` | `17.3x` | | `extend_from_array_zctl (1000, 8)` | `1.116 ms` | `14.31 us` | `78.0x` | | `extend_from_array_zctl (1000, 64)` | `5.511 ms` | `38.68 us` | `142.5x` | | `extend_from_array_zctl (10000, 8)` | `11.01 ms` | `108.4 us` | `101.6x` | Before = commit `d72bf9b93` (pre-fix), After = commit `c4ad4e2ad` (the ListViewBuilder scalar-at removal fix). --------- Signed-off-by: Nicholas Gates <nick@nickgates.com> Signed-off-by: Robert Kruszewski <github@robertk.io> Co-authored-by: Robert Kruszewski <github@robertk.io>
1 parent 4c1ae92 commit 09a57ad

4 files changed

Lines changed: 230 additions & 90 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

encodings/sparse/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ vortex-mask = { workspace = true }
2727
vortex-session = { workspace = true }
2828

2929
[dev-dependencies]
30+
divan = { workspace = true }
3031
itertools = { workspace = true }
3132
rstest = { workspace = true }
3233
vortex-array = { workspace = true, features = ["_test-harness"] }
34+
35+
[[bench]]
36+
name = "sparse_canonical"
37+
harness = false
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
#![expect(clippy::cast_possible_truncation)]
5+
6+
use std::sync::Arc;
7+
8+
use divan::Bencher;
9+
use vortex_array::ArrayRef;
10+
use vortex_array::Canonical;
11+
use vortex_array::IntoArray;
12+
use vortex_array::LEGACY_SESSION;
13+
use vortex_array::VortexSessionExecute;
14+
use vortex_array::arrays::FixedSizeListArray;
15+
use vortex_array::arrays::ListViewArray;
16+
use vortex_array::arrays::PrimitiveArray;
17+
use vortex_array::dtype::Nullability::NonNullable;
18+
use vortex_array::dtype::PType::I32;
19+
use vortex_array::scalar::Scalar;
20+
use vortex_array::validity::Validity;
21+
use vortex_buffer::Buffer;
22+
use vortex_error::VortexExpect;
23+
use vortex_sparse::Sparse;
24+
25+
fn main() {
26+
divan::main();
27+
}
28+
29+
const LIST_ARGS: &[(usize, usize, usize)] = &[
30+
// len, patch_stride, list_size
31+
(512, 7, 4),
32+
(1_024, 17, 8),
33+
];
34+
35+
const FIXED_SIZE_LIST_ARGS: &[(usize, usize, u32)] = &[
36+
// len, patch_stride, list_size
37+
(512, 7, 4),
38+
(1_024, 17, 8),
39+
];
40+
41+
fn make_sparse_list(len: usize, patch_stride: usize, list_size: usize) -> ArrayRef {
42+
let patch_indices: Buffer<u32> = (0..len).step_by(patch_stride).map(|i| i as u32).collect();
43+
let n_patches = patch_indices.len();
44+
45+
let patch_elements = PrimitiveArray::from_iter(0..(n_patches * list_size) as i32).into_array();
46+
let patch_offsets: Buffer<u32> = (0..n_patches).map(|i| (i * list_size) as u32).collect();
47+
let patch_sizes: Buffer<u32> = std::iter::repeat_n(list_size as u32, n_patches).collect();
48+
let patch_values = ListViewArray::new(
49+
patch_elements,
50+
patch_offsets.into_array(),
51+
patch_sizes.into_array(),
52+
Validity::NonNullable,
53+
)
54+
.into_array();
55+
56+
let fill_value = Scalar::list(
57+
Arc::new(I32.into()),
58+
(0..list_size as i32).map(Scalar::from).collect(),
59+
NonNullable,
60+
);
61+
62+
Sparse::try_new(patch_indices.into_array(), patch_values, len, fill_value)
63+
.vortex_expect("sparse list input should be valid")
64+
.into_array()
65+
}
66+
67+
fn make_sparse_fixed_size_list(len: usize, patch_stride: usize, list_size: u32) -> ArrayRef {
68+
let patch_indices: Buffer<u32> = (0..len).step_by(patch_stride).map(|i| i as u32).collect();
69+
let n_patches = patch_indices.len();
70+
71+
let patch_elements =
72+
PrimitiveArray::from_iter(0..(n_patches * list_size as usize) as i32).into_array();
73+
let patch_values =
74+
FixedSizeListArray::new(patch_elements, list_size, Validity::NonNullable, n_patches)
75+
.into_array();
76+
77+
let fill_value = Scalar::fixed_size_list(
78+
Arc::new(I32.into()),
79+
(0..list_size as i32).map(Scalar::from).collect(),
80+
NonNullable,
81+
);
82+
83+
Sparse::try_new(patch_indices.into_array(), patch_values, len, fill_value)
84+
.vortex_expect("sparse fixed-size-list input should be valid")
85+
.into_array()
86+
}
87+
88+
#[divan::bench(args = LIST_ARGS)]
89+
fn canonicalize_sparse_list(
90+
bencher: Bencher,
91+
(len, patch_stride, list_size): (usize, usize, usize),
92+
) {
93+
let sparse = make_sparse_list(len, patch_stride, list_size);
94+
95+
bencher
96+
.with_inputs(|| (sparse.clone(), LEGACY_SESSION.create_execution_ctx()))
97+
.bench_values(|(array, mut ctx)| {
98+
divan::black_box(
99+
array
100+
.execute::<Canonical>(&mut ctx)
101+
.vortex_expect("sparse list canonicalization"),
102+
)
103+
});
104+
}
105+
106+
#[divan::bench(args = FIXED_SIZE_LIST_ARGS)]
107+
fn canonicalize_sparse_fixed_size_list(
108+
bencher: Bencher,
109+
(len, patch_stride, list_size): (usize, usize, u32),
110+
) {
111+
let sparse = make_sparse_fixed_size_list(len, patch_stride, list_size);
112+
113+
bencher
114+
.with_inputs(|| (sparse.clone(), LEGACY_SESSION.create_execution_ctx()))
115+
.bench_values(|(array, mut ctx)| {
116+
divan::black_box(
117+
array
118+
.execute::<Canonical>(&mut ctx)
119+
.vortex_expect("sparse fixed-size-list canonicalization"),
120+
)
121+
});
122+
}

0 commit comments

Comments
 (0)