Last active
August 7, 2025 14:43
-
-
Save navahas/18e947c88d2f4f203dc310503250cd64 to your computer and use it in GitHub Desktop.
Benchmark: Indexing vs Iterator in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::hint::black_box; | |
| use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; | |
| // Indexing approaches | |
| pub fn sum_indexing(v: &[u64]) -> u64 { | |
| let mut sum = 0; | |
| for i in 0..v.len() { | |
| sum += v[i]; | |
| } | |
| sum | |
| } | |
| pub fn sum_indexing_unchecked(v: &[u64]) -> u64 { | |
| let mut sum = 0; | |
| for i in 0..v.len() { | |
| unsafe { sum += *v.get_unchecked(i) }; | |
| } | |
| sum | |
| } | |
| // Iterator approaches | |
| pub fn sum_iter(v: &[u64]) -> u64 { | |
| let mut sum = 0; | |
| for value in v.iter() { | |
| sum += *value; | |
| } | |
| sum | |
| } | |
| pub fn sum_iter_copied(v: &[u64]) -> u64 { | |
| let mut sum = 0; | |
| for value in v.iter().copied() { | |
| sum += value; | |
| } | |
| sum | |
| } | |
| pub fn sum_fold(v: &[u64]) -> u64 { | |
| v.iter().fold(0, |acc, &x| acc + x) | |
| } | |
| pub fn sum_reduce(v: &[u64]) -> u64 { | |
| v.iter().copied().reduce(|acc, x| acc + x).unwrap_or(0) | |
| } | |
| // Functional approaches | |
| pub fn sum_functional(v: &[u64]) -> u64 { | |
| v.iter().sum() | |
| } | |
| // Memory access patterns test | |
| pub fn sum_indexing_strided(v: &[u64], stride: usize) -> u64 { | |
| let mut sum = 0; | |
| let mut i = 0; | |
| while i < v.len() { | |
| sum += v[i]; | |
| i += stride; | |
| } | |
| sum | |
| } | |
| pub fn sum_iter_strided(v: &[u64], stride: usize) -> u64 { | |
| let mut sum = 0; | |
| for value in v.iter().step_by(stride) { | |
| sum += *value; | |
| } | |
| sum | |
| } | |
| // Complex operations to test beyond simple addition | |
| pub fn complex_indexing(v: &[u64]) -> u64 { | |
| let mut result = 0; | |
| for i in 0..v.len() { | |
| let val = v[i]; | |
| result += val.wrapping_mul(val).wrapping_add(1); | |
| } | |
| result | |
| } | |
| pub fn complex_iter(v: &[u64]) -> u64 { | |
| let mut result = 0; | |
| for &val in v.iter() { | |
| result += val.wrapping_mul(val).wrapping_add(1); | |
| } | |
| result | |
| } | |
| fn bench_basic_operations(c: &mut Criterion) { | |
| let mut group = c.benchmark_group("basic_sum"); | |
| // Test different sizes to see scaling behavior | |
| let sizes = [100, 1_000, 10_000, 100_000, 1_000_000]; | |
| for size in sizes.iter() { | |
| let vec: Vec<u64> = (0..*size).collect(); | |
| group.throughput(Throughput::Elements(*size as u64)); | |
| group.bench_with_input( | |
| BenchmarkId::new("indexing", size), | |
| &vec, | |
| |b, v| b.iter(|| sum_indexing(black_box(v))) | |
| ); | |
| group.bench_with_input( | |
| BenchmarkId::new("iterator", size), | |
| &vec, | |
| |b, v| b.iter(|| sum_iter(black_box(v))) | |
| ); | |
| group.bench_with_input( | |
| BenchmarkId::new("iterator_copied", size), | |
| &vec, | |
| |b, v| b.iter(|| sum_iter_copied(black_box(v))) | |
| ); | |
| group.bench_with_input( | |
| BenchmarkId::new("fold", size), | |
| &vec, | |
| |b, v| b.iter(|| sum_fold(black_box(v))) | |
| ); | |
| group.bench_with_input( | |
| BenchmarkId::new("functional_sum", size), | |
| &vec, | |
| |b, v| b.iter(|| sum_functional(black_box(v))) | |
| ); | |
| } | |
| group.finish(); | |
| } | |
| fn bench_unsafe_vs_safe(c: &mut Criterion) { | |
| let mut group = c.benchmark_group("unsafe_comparison"); | |
| let vec: Vec<u64> = (0..1_000_000).collect(); | |
| group.throughput(Throughput::Elements(1_000_000)); | |
| group.bench_function("safe_indexing", |b| { | |
| b.iter(|| sum_indexing(black_box(&vec))) | |
| }); | |
| group.bench_function("unsafe_indexing", |b| { | |
| b.iter(|| sum_indexing_unchecked(black_box(&vec))) | |
| }); | |
| group.bench_function("iterator", |b| { | |
| b.iter(|| sum_iter(black_box(&vec))) | |
| }); | |
| group.finish(); | |
| } | |
| fn bench_memory_patterns(c: &mut Criterion) { | |
| let mut group = c.benchmark_group("memory_access_patterns"); | |
| let vec: Vec<u64> = (0..1_000_000).collect(); | |
| let strides = [1, 2, 4, 8, 16]; | |
| for stride in strides.iter() { | |
| group.throughput(Throughput::Elements(1_000_000 / *stride as u64)); | |
| group.bench_with_input( | |
| BenchmarkId::new("indexing_stride", stride), | |
| stride, | |
| |b, &s| b.iter(|| sum_indexing_strided(black_box(&vec), s)) | |
| ); | |
| group.bench_with_input( | |
| BenchmarkId::new("iterator_stride", stride), | |
| stride, | |
| |b, &s| b.iter(|| sum_iter_strided(black_box(&vec), s)) | |
| ); | |
| } | |
| group.finish(); | |
| } | |
| fn bench_complex_operations(c: &mut Criterion) { | |
| let mut group = c.benchmark_group("complex_operations"); | |
| let vec: Vec<u64> = (0..1_000_000).collect(); | |
| group.throughput(Throughput::Elements(1_000_000)); | |
| group.bench_function("complex_indexing", |b| { | |
| b.iter(|| complex_indexing(black_box(&vec))) | |
| }); | |
| group.bench_function("complex_iterator", |b| { | |
| b.iter(|| complex_iter(black_box(&vec))) | |
| }); | |
| group.finish(); | |
| } | |
| fn bench_different_types(c: &mut Criterion) { | |
| let mut group = c.benchmark_group("different_types"); | |
| // Test with different data types | |
| let vec_u64: Vec<u64> = (0..1_000_000u64).cycle().take(1_000_000).collect(); | |
| let vec_f64: Vec<f64> = (0..1_000_000).map(|x| x as f64).collect(); | |
| group.throughput(Throughput::Elements(1_000_000)); | |
| // u64 benchmarks | |
| group.bench_function("u64_indexing", |b| { | |
| b.iter(|| { | |
| let mut sum = 0u64; | |
| for i in 0..vec_u64.len() { | |
| sum += vec_u64[i] as u64; | |
| } | |
| sum | |
| }) | |
| }); | |
| group.bench_function("u64_iterator", |b| { | |
| b.iter(|| { | |
| let mut sum = 0u64; | |
| for &value in vec_u64.iter() { | |
| sum += value as u64; | |
| } | |
| sum | |
| }) | |
| }); | |
| // f64 benchmarks | |
| group.bench_function("f64_indexing", |b| { | |
| b.iter(|| { | |
| let mut sum = 0.0; | |
| for i in 0..vec_f64.len() { | |
| sum += vec_f64[i]; | |
| } | |
| sum | |
| }) | |
| }); | |
| group.bench_function("f64_iterator", |b| { | |
| b.iter(|| { | |
| let mut sum = 0.0; | |
| for &value in vec_f64.iter() { | |
| sum += value; | |
| } | |
| sum | |
| }) | |
| }); | |
| group.finish(); | |
| } | |
| fn bench_cache_behavior(c: &mut Criterion) { | |
| let mut group = c.benchmark_group("cache_behavior"); | |
| // Small data that fits in L1 cache | |
| let small_vec: Vec<u64> = (0..1000).collect(); | |
| // Large data that doesn't fit in cache | |
| let large_vec: Vec<u64> = (0..10_000_000).collect(); | |
| group.bench_function("small_data_indexing", |b| { | |
| b.iter(|| sum_indexing(black_box(&small_vec))) | |
| }); | |
| group.bench_function("small_data_iterator", |b| { | |
| b.iter(|| sum_iter(black_box(&small_vec))) | |
| }); | |
| group.bench_function("large_data_indexing", |b| { | |
| b.iter(|| sum_indexing(black_box(&large_vec))) | |
| }); | |
| group.bench_function("large_data_iterator", |b| { | |
| b.iter(|| sum_iter(black_box(&large_vec))) | |
| }); | |
| group.finish(); | |
| } | |
| criterion_group!( | |
| benches, | |
| bench_basic_operations, | |
| bench_unsafe_vs_safe, | |
| bench_memory_patterns, | |
| bench_complex_operations, | |
| bench_different_types, | |
| bench_cache_behavior | |
| ); | |
| criterion_main!(benches); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment