Skip to content

Commit b7ee873

Browse files
Revert "Lazily untranspose DeltaArray validity and depend on fastlanes bit transpose code" (#8883)
Reverts #8725
1 parent f68d60d commit b7ee873

13 files changed

Lines changed: 1835 additions & 488 deletions

File tree

encodings/fastlanes/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ name = "compute_between"
5858
harness = false
5959
required-features = ["_test-harness"]
6060

61+
[[bench]]
62+
name = "bit_transpose"
63+
harness = false
64+
required-features = ["_test-harness"]
65+
6166
[[bench]]
6267
name = "bitpack_compare"
6368
harness = false
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use divan::Bencher;
5+
use vortex_fastlanes::bit_transpose::scalar::transpose_bits_scalar;
6+
use vortex_fastlanes::bit_transpose::scalar::untranspose_bits_scalar;
7+
8+
fn main() {
9+
divan::main();
10+
}
11+
12+
/// Generate deterministic test data.
13+
#[expect(clippy::cast_possible_truncation)]
14+
fn generate_test_data(seed: usize) -> [u8; 128] {
15+
let mut data = [0u8; 128];
16+
for (i, byte) in data.iter_mut().enumerate() {
17+
*byte = seed.wrapping_mul(17).wrapping_add(i).wrapping_mul(31) as u8;
18+
}
19+
data
20+
}
21+
22+
const BATCH_SIZE: usize = 1000;
23+
24+
// ============================================================================
25+
// Transpose: single array
26+
// ============================================================================
27+
28+
#[divan::bench]
29+
fn transpose_scalar(bencher: Bencher) {
30+
let input = generate_test_data(42);
31+
32+
bencher
33+
.with_inputs(|| (&input, [0u8; 128]))
34+
.bench_refs(|(input, output)| {
35+
transpose_bits_scalar(input, output);
36+
});
37+
}
38+
39+
// ============================================================================
40+
// Transpose: throughput (1000 arrays)
41+
// ============================================================================
42+
43+
#[divan::bench]
44+
fn transpose_scalar_throughput(bencher: Bencher) {
45+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
46+
47+
bencher
48+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
49+
.bench_refs(|(inputs, outputs)| {
50+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
51+
transpose_bits_scalar(input, output);
52+
}
53+
});
54+
}
55+
56+
// ============================================================================
57+
// Untranspose: single array
58+
// ============================================================================
59+
60+
#[divan::bench]
61+
fn untranspose_scalar(bencher: Bencher) {
62+
let input = generate_test_data(42);
63+
64+
bencher
65+
.with_inputs(|| (&input, [0u8; 128]))
66+
.bench_refs(|(input, output)| {
67+
untranspose_bits_scalar(input, output);
68+
});
69+
}
70+
71+
// ============================================================================
72+
// Untranspose: throughput (1000 arrays)
73+
// ============================================================================
74+
75+
#[divan::bench]
76+
fn untranspose_scalar_throughput(bencher: Bencher) {
77+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
78+
79+
bencher
80+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
81+
.bench_refs(|(inputs, outputs)| {
82+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
83+
untranspose_bits_scalar(input, output);
84+
}
85+
});
86+
}
87+
88+
// ============================================================================
89+
// x86_64 benchmarks
90+
// ============================================================================
91+
92+
#[cfg(target_arch = "x86_64")]
93+
mod x86 {
94+
use divan::Bencher;
95+
use vortex_fastlanes::bit_transpose::x86::has_bmi2;
96+
use vortex_fastlanes::bit_transpose::x86::has_vbmi;
97+
use vortex_fastlanes::bit_transpose::x86::transpose_bits_bmi2;
98+
use vortex_fastlanes::bit_transpose::x86::transpose_bits_vbmi;
99+
use vortex_fastlanes::bit_transpose::x86::untranspose_bits_bmi2;
100+
use vortex_fastlanes::bit_transpose::x86::untranspose_bits_vbmi;
101+
102+
use super::BATCH_SIZE;
103+
use super::generate_test_data;
104+
105+
// --- Transpose: single array ---
106+
107+
#[divan::bench]
108+
fn transpose_bmi2(bencher: Bencher) {
109+
if !has_bmi2() {
110+
return;
111+
}
112+
113+
let input = generate_test_data(42);
114+
115+
bencher
116+
.with_inputs(|| (&input, [0u8; 128]))
117+
.bench_refs(|(input, output)| {
118+
unsafe { transpose_bits_bmi2(input, output) };
119+
});
120+
}
121+
122+
#[divan::bench]
123+
fn transpose_vbmi(bencher: Bencher) {
124+
if !has_vbmi() {
125+
return;
126+
}
127+
128+
let input = generate_test_data(42);
129+
130+
bencher
131+
.with_inputs(|| (&input, [0u8; 128]))
132+
.bench_refs(|(input, output)| {
133+
unsafe { transpose_bits_vbmi(input, output) };
134+
});
135+
}
136+
137+
// --- Untranspose: single array ---
138+
139+
#[divan::bench]
140+
fn untranspose_bmi2(bencher: Bencher) {
141+
if !has_bmi2() {
142+
return;
143+
}
144+
145+
let input = generate_test_data(42);
146+
147+
bencher
148+
.with_inputs(|| (&input, [0u8; 128]))
149+
.bench_refs(|(input, output)| {
150+
unsafe { untranspose_bits_bmi2(input, output) };
151+
});
152+
}
153+
154+
#[divan::bench]
155+
fn untranspose_vbmi(bencher: Bencher) {
156+
if !has_vbmi() {
157+
return;
158+
}
159+
160+
let input = generate_test_data(42);
161+
162+
bencher
163+
.with_inputs(|| (&input, [0u8; 128]))
164+
.bench_refs(|(input, output)| {
165+
unsafe { untranspose_bits_vbmi(input, output) };
166+
});
167+
}
168+
169+
// --- Transpose: throughput (1000 arrays) ---
170+
171+
#[divan::bench]
172+
fn transpose_bmi2_throughput(bencher: Bencher) {
173+
if !has_bmi2() {
174+
return;
175+
}
176+
177+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
178+
179+
bencher
180+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
181+
.bench_refs(|(inputs, outputs)| {
182+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
183+
unsafe { transpose_bits_bmi2(input, output) };
184+
}
185+
});
186+
}
187+
188+
#[divan::bench]
189+
fn transpose_vbmi_throughput(bencher: Bencher) {
190+
if !has_vbmi() {
191+
return;
192+
}
193+
194+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
195+
196+
bencher
197+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
198+
.bench_refs(|(inputs, outputs)| {
199+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
200+
unsafe { transpose_bits_vbmi(input, output) };
201+
}
202+
});
203+
}
204+
205+
// --- Untranspose: throughput (1000 arrays) ---
206+
207+
#[divan::bench]
208+
fn untranspose_bmi2_throughput(bencher: Bencher) {
209+
if !has_bmi2() {
210+
return;
211+
}
212+
213+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
214+
215+
bencher
216+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
217+
.bench_refs(|(inputs, outputs)| {
218+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
219+
unsafe { untranspose_bits_bmi2(input, output) };
220+
}
221+
});
222+
}
223+
224+
#[divan::bench]
225+
fn untranspose_vbmi_throughput(bencher: Bencher) {
226+
if !has_vbmi() {
227+
return;
228+
}
229+
230+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
231+
232+
bencher
233+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
234+
.bench_refs(|(inputs, outputs)| {
235+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
236+
unsafe { untranspose_bits_vbmi(input, output) };
237+
}
238+
});
239+
}
240+
}
241+
242+
// ============================================================================
243+
// aarch64 benchmarks
244+
// ============================================================================
245+
246+
#[cfg(target_arch = "aarch64")]
247+
mod aarch64 {
248+
use vortex_fastlanes::bit_transpose::aarch64::transpose_bits_neon;
249+
use vortex_fastlanes::bit_transpose::aarch64::untranspose_bits_neon;
250+
251+
use super::BATCH_SIZE;
252+
use super::Bencher;
253+
use super::generate_test_data;
254+
255+
// --- Transpose: single array ---
256+
257+
#[divan::bench]
258+
fn transpose_neon(bencher: Bencher) {
259+
let input = generate_test_data(42);
260+
261+
bencher
262+
.with_inputs(|| (&input, [0u8; 128]))
263+
.bench_refs(|(input, output)| {
264+
unsafe { transpose_bits_neon(input, output) };
265+
});
266+
}
267+
268+
// --- Untranspose: single array ---
269+
270+
#[divan::bench]
271+
fn untranspose_neon(bencher: Bencher) {
272+
let input = generate_test_data(42);
273+
274+
bencher
275+
.with_inputs(|| (&input, [0u8; 128]))
276+
.bench_refs(|(input, output)| {
277+
unsafe { untranspose_bits_neon(input, output) };
278+
});
279+
}
280+
281+
// --- Transpose: throughput (1000 arrays) ---
282+
283+
#[divan::bench]
284+
fn transpose_neon_throughput(bencher: Bencher) {
285+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
286+
287+
bencher
288+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
289+
.bench_refs(|(inputs, outputs)| {
290+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
291+
unsafe { transpose_bits_neon(input, output) };
292+
}
293+
});
294+
}
295+
296+
// --- Untranspose: throughput (1000 arrays) ---
297+
298+
#[divan::bench]
299+
fn untranspose_neon_throughput(bencher: Bencher) {
300+
let inputs: Vec<[u8; 128]> = (0..BATCH_SIZE).map(generate_test_data).collect();
301+
302+
bencher
303+
.with_inputs(|| (&inputs, vec![[0u8; 128]; BATCH_SIZE]))
304+
.bench_refs(|(inputs, outputs)| {
305+
for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
306+
unsafe { untranspose_bits_neon(input, output) };
307+
}
308+
});
309+
}
310+
}

0 commit comments

Comments
 (0)