hotloop_benchmark_avg_std/
hotloop_benchmark_avg_std.rs

1//! ---------------------------------------------------------
2//! Runs averaged sum benchmark comparisons on `Minarrow` and `Arrow-Rs`,
3//! at various layers of library abstraction, using SIMD:
4//!
5//!     1. Raw Vec / Vec64
6//!     2. Typed "inner" arrays
7//!     3. Top-level unified `Array` type
8//!
9//! Run with:
10//!     RUSTFLAGS="-C target-cpu=native" cargo run --release --example hotloop_benchmark_simd
11//!
12//! The *RUSTFLAGS* argument ensures it compiles to your host instruction-set.
13//!
14//! Use 2, 4, 8, or 16 LANES as per your processor's SIMD support.
15//! ---------------------------------------------------------
16
17#[cfg(feature = "cast_arrow")]
18use crate::benchmarks_avg::run_benchmark;
19#[cfg(feature = "cast_arrow")]
20mod benchmarks_avg {
21    use std::hint::black_box;
22    use std::sync::Arc;
23    use std::time::Instant;
24
25    use arrow::array::{
26        Array as ArrowArrayTrait, ArrayRef, Float64Array as ArrowF64Array,
27        Int64Array as ArrowI64Array
28    };
29    use minarrow::{Array, Buffer, FloatArray, IntegerArray, NumericArray, Vec64};
30
31    use crate::fmt_duration_ns;
32
33    const N: usize = 1000;
34    const ITERATIONS: usize = 1000;
35
36    pub(crate) fn run_benchmark() {
37        let mut total_arrow_dyn_i64 = std::time::Duration::ZERO;
38        let mut total_arrow_struct_i64 = std::time::Duration::ZERO;
39        let mut total_minarrow_enum_i64 = std::time::Duration::ZERO;
40        let mut total_minarrow_direct_i64 = std::time::Duration::ZERO;
41        let mut total_vec_i64 = std::time::Duration::ZERO;
42        let mut total_arrow_dyn_f64 = std::time::Duration::ZERO;
43        let mut total_arrow_struct_f64 = std::time::Duration::ZERO;
44        let mut total_minarrow_enum_f64 = std::time::Duration::ZERO;
45        let mut total_minarrow_direct_f64 = std::time::Duration::ZERO;
46        let mut total_vec_f64 = std::time::Duration::ZERO;
47
48        for _ in 0..ITERATIONS {
49            // ----------- Arrow i64 (dynamic) -----------
50            let data: Vec<i64> = (0..N as i64).collect();
51            let start = Instant::now();
52            let arr: ArrayRef = Arc::new(ArrowI64Array::from(data));
53            let mut acc = 0i64;
54            if let Some(int) = arr.as_any().downcast_ref::<ArrowI64Array>() {
55                for i in 0..int.len() {
56                    acc += int.value(i);
57                }
58            }
59            let dur_arrow_dyn_i64 = start.elapsed();
60            total_arrow_dyn_i64 += dur_arrow_dyn_i64;
61            black_box(acc);
62
63            // ----------- Arrow i64 (struct direct) -----------
64            let data: Vec<i64> = (0..N as i64).collect();
65            let start = Instant::now();
66            let arr = ArrowI64Array::from(data);
67            let mut acc = 0i64;
68            for i in 0..arr.len() {
69                acc += arr.value(i);
70            }
71            let dur_arrow_struct_i64 = start.elapsed();
72            total_arrow_struct_i64 += dur_arrow_struct_i64;
73            black_box(acc);
74
75            // ----------- Minarrow i64 (enum) -----------
76            let min_data: Vec64<i64> = (0..N as i64).collect();
77            let start = Instant::now();
78            let array = Array::NumericArray(NumericArray::Int64(Arc::new(IntegerArray {
79                data: Buffer::from(min_data),
80                null_mask: None
81            })));
82            let mut acc = 0i64;
83            let int_arr = array.num().i64().unwrap();
84            let slice = int_arr.data.as_slice();
85            for &v in slice {
86                acc += v;
87            }
88            let dur_minarrow_enum_i64 = start.elapsed();
89            total_minarrow_enum_i64 += dur_minarrow_enum_i64;
90            black_box(acc);
91
92            // ----------- Minarrow i64 (direct struct, no enum) -----------
93            let min_data: Vec64<i64> = (0..N as i64).collect();
94            let start = Instant::now();
95            let int_arr = IntegerArray {
96                data: Buffer::from(min_data),
97                null_mask: None
98            };
99            let mut acc = 0i64;
100            let slice = int_arr.data.as_slice();
101            for &v in slice {
102                acc += v;
103            }
104            let dur_minarrow_direct_i64 = start.elapsed();
105            total_minarrow_direct_i64 += dur_minarrow_direct_i64;
106            black_box(acc);
107
108            // ----------- Raw Vec<i64> -----------
109            let raw_vec: Vec<i64> = (0..N as i64).collect();
110            let start = Instant::now();
111            let mut acc = 0i64;
112            for &v in &raw_vec {
113                acc += v;
114            }
115            let dur_vec_i64 = start.elapsed();
116            total_vec_i64 += dur_vec_i64;
117            black_box(acc);
118
119            // ----------- Arrow f64 (dynamic) -----------
120            let data_f64: Vec<f64> = (0..N as i64).map(|x| x as f64).collect();
121            let start = Instant::now();
122            let arr: ArrayRef = Arc::new(ArrowF64Array::from(data_f64));
123            let mut acc = 0.0f64;
124            if let Some(f) = arr.as_any().downcast_ref::<ArrowF64Array>() {
125                for i in 0..f.len() {
126                    acc += f.value(i);
127                }
128            }
129            let dur_arrow_dyn_f64 = start.elapsed();
130            total_arrow_dyn_f64 += dur_arrow_dyn_f64;
131            black_box(acc);
132
133            // ----------- Arrow f64 (struct direct) -----------
134            let data_f64: Vec<f64> = (0..N as i64).map(|x| x as f64).collect();
135            let start = Instant::now();
136            let arr = ArrowF64Array::from(data_f64);
137            let mut acc = 0.0f64;
138            for i in 0..arr.len() {
139                acc += arr.value(i);
140            }
141            let dur_arrow_struct_f64 = start.elapsed();
142            total_arrow_struct_f64 += dur_arrow_struct_f64;
143            black_box(acc);
144
145            // ----------- Minarrow f64 (enum) -----------
146            let min_data_f64: Vec64<f64> = (0..N as i64).map(|x| x as f64).collect();
147            let start = Instant::now();
148            let array = Array::NumericArray(NumericArray::Float64(Arc::new(FloatArray {
149                data: Buffer::from(min_data_f64),
150                null_mask: None
151            })));
152            let mut acc = 0.0f64;
153            let float_arr = array.num().f64().unwrap();
154            let slice = float_arr.data.as_slice();
155            for &v in slice {
156                acc += v;
157            }
158            let dur_minarrow_enum_f64 = start.elapsed();
159            total_minarrow_enum_f64 += dur_minarrow_enum_f64;
160            black_box(acc);
161
162            // ----------- Minarrow f64 (direct struct, no enum) -----------
163            let min_data_f64: Vec64<f64> = (0..N as i64).map(|x| x as f64).collect();
164            let start = Instant::now();
165            let float_arr = FloatArray {
166                data: Buffer::from(min_data_f64),
167                null_mask: None
168            };
169            let mut acc = 0.0f64;
170            let slice = float_arr.data.as_slice();
171            for &v in slice {
172                acc += v;
173            }
174            let dur_minarrow_direct_f64 = start.elapsed();
175            total_minarrow_direct_f64 += dur_minarrow_direct_f64;
176            black_box(acc);
177
178            // ----------- Raw Vec<f64> -----------
179            let raw_vec: Vec<f64> = (0..N as i64).map(|x| x as f64).collect();
180            let start = Instant::now();
181            let mut acc = 0.0f64;
182            for &v in &raw_vec {
183                acc += v;
184            }
185            let dur_vec_f64 = start.elapsed();
186            total_vec_f64 += dur_vec_f64;
187            black_box(acc);
188        }
189
190        println!("Averaged Results from {} runs:", ITERATIONS);
191        println!("---------------------------------");
192
193        let avg_vec_i64 = total_vec_i64.as_nanos() as f64 / ITERATIONS as f64;
194        let avg_minarrow_direct_i64 =
195            total_minarrow_direct_i64.as_nanos() as f64 / ITERATIONS as f64;
196        let avg_arrow_struct_i64 = total_arrow_struct_i64.as_nanos() as f64 / ITERATIONS as f64;
197        let avg_minarrow_enum_i64 = total_minarrow_enum_i64.as_nanos() as f64 / ITERATIONS as f64;
198        let avg_arrow_dyn_i64 = total_arrow_dyn_i64.as_nanos() as f64 / ITERATIONS as f64;
199
200        let avg_vec_f64 = total_vec_f64.as_nanos() as f64 / ITERATIONS as f64;
201        let avg_minarrow_direct_f64 =
202            total_minarrow_direct_f64.as_nanos() as f64 / ITERATIONS as f64;
203        let avg_arrow_struct_f64 = total_arrow_struct_f64.as_nanos() as f64 / ITERATIONS as f64;
204        let avg_minarrow_enum_f64 = total_minarrow_enum_f64.as_nanos() as f64 / ITERATIONS as f64;
205        let avg_arrow_dyn_f64 = total_arrow_dyn_f64.as_nanos() as f64 / ITERATIONS as f64;
206
207        println!(
208            "raw vec: Vec<i64>                             avg = {} (n={})",
209            fmt_duration_ns(avg_vec_i64),
210            ITERATIONS
211        );
212        println!(
213            "minarrow direct: IntegerArray                 avg = {} (n={})",
214            fmt_duration_ns(avg_minarrow_direct_i64),
215            ITERATIONS
216        );
217        println!(
218            "arrow-rs struct: Int64Array                   avg = {} (n={})",
219            fmt_duration_ns(avg_arrow_struct_i64),
220            ITERATIONS
221        );
222        println!();
223        println!(
224            "minarrow enum: IntegerArray                   avg = {} (n={})",
225            fmt_duration_ns(avg_minarrow_enum_i64),
226            ITERATIONS
227        );
228        println!(
229            "arrow-rs dyn: Int64Array                      avg = {} (n={})",
230            fmt_duration_ns(avg_arrow_dyn_i64),
231            ITERATIONS
232        );
233        println!();
234        println!(
235            "raw vec: Vec<f64>                             avg = {} (n={})",
236            fmt_duration_ns(avg_vec_f64),
237            ITERATIONS
238        );
239        println!(
240            "minarrow direct: FloatArray                   avg = {} (n={})",
241            fmt_duration_ns(avg_minarrow_direct_f64),
242            ITERATIONS
243        );
244        println!(
245            "arrow-rs struct: Float64Array                 avg = {} (n={})",
246            fmt_duration_ns(avg_arrow_struct_f64),
247            ITERATIONS
248        );
249        println!();
250        println!(
251            "minarrow enum: FloatArray                     avg = {} (n={})",
252            fmt_duration_ns(avg_minarrow_enum_f64),
253            ITERATIONS
254        );
255        println!(
256            "arrow-rs dyn: Float64Array                    avg = {} (n={})",
257            fmt_duration_ns(avg_arrow_dyn_f64),
258            ITERATIONS
259        );
260    }
261}
262
263#[cfg(feature = "cast_arrow")]
264fn fmt_duration_ns(avg_ns: f64) -> String {
265    if avg_ns < 1000.0 {
266        format!("{:.0} ns", avg_ns)
267    } else if avg_ns < 1_000_000.0 {
268        format!("{:.3} µs", avg_ns / 1000.0)
269    } else {
270        format!("{:.3} ms", avg_ns / 1_000_000.0)
271    }
272}
273
274fn main() {
275    if cfg!(feature = "cast_arrow") {
276        #[cfg(feature = "cast_arrow")]
277        run_benchmark()
278    } else {
279        println!(
280            "The hotloop_benchmark_avg_std example requires enabling the `cast_arrow` feature."
281        )
282    }
283}