Skip to content

Commit

Permalink
Merge pull request #27 from KeplerOps/feature/issue-18-heapsort
Browse files Browse the repository at this point in the history
Feature/issue 18 heapsort
  • Loading branch information
Brad-Edwards authored Jan 1, 2025
2 parents d0dd3b5 + 02f050f commit ee7caf3
Show file tree
Hide file tree
Showing 7 changed files with 724 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]
resolver = "2"
members = [
"crates/shared/blocks-core",
"crates/shared/blocks-utils",
Expand Down Expand Up @@ -53,6 +54,5 @@ edition = "2021"
authors = ["Brad Edwards <[email protected]>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/atomiklabs/blocks"
resolver = "2"

[workspace.dependencies]
15 changes: 15 additions & 0 deletions crates/cs/blocks-cs-sort/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,19 @@ authors.workspace = true
license.workspace = true
repository.workspace = true

[features]
default = []
parallel = ["rayon"]
simd = ["wide"]

[dependencies]
rayon = { version = "1.10", optional = true }
wide = { version = "0.7.3", optional = true }

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
rand = "0.8"

[[bench]]
name = "heapsort_benchmarks"
harness = false
166 changes: 166 additions & 0 deletions crates/cs/blocks-cs-sort/benches/heapsort_benchmarks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use criterion::{criterion_group, criterion_main, Criterion, BatchSize};
use rand::prelude::*;
use blocks_cs_sort::algorithms::heapsort;

/// Helper to generate different input distributions.
fn generate_data(len: usize, distribution: &str) -> Vec<i32> {
let mut rng = thread_rng();
let mut data: Vec<i32> = (0..len as i32).collect();

match distribution {
"sorted" => { /* already sorted */ },
"reverse" => data.reverse(),
"random" => data.shuffle(&mut rng),
"nearly_sorted" => {
// Swap every 100th element
for i in 0..(len/100) {
let j = i * 100;
if j + 1 < len {
data.swap(j, j + 1);
}
}
},
"few_unique" => {
// Only use values 0-9 repeated
for i in 0..len {
data[i] = (i % 10) as i32;
}
},
_ => {}
};
data
}

fn benchmark_heapsort(c: &mut Criterion) {
let sizes = [1_000, 10_000, 100_000, 1_000_000];
let distributions = ["sorted", "reverse", "random", "nearly_sorted", "few_unique"];

let mut group = c.benchmark_group("heapsort");
group.sample_size(10); // Adjust based on your needs

for &size in &sizes {
for dist in &distributions {
let bench_name = format!("heapsort_{}_{}", dist, size);

group.bench_function(&bench_name, |b| {
b.iter_batched(
|| generate_data(size, dist),
|mut data| heapsort::sort(&mut data).expect("Sort should succeed"),
BatchSize::LargeInput,
)
});
}
}

group.finish();
}

#[cfg(feature = "parallel")]
fn benchmark_parallel_heapsort(c: &mut Criterion) {
let sizes = [100_000, 1_000_000, 10_000_000];
let distributions = ["sorted", "reverse", "random", "nearly_sorted", "few_unique"];

let mut group = c.benchmark_group("parallel_heapsort");
group.sample_size(10); // Fewer samples for large parallel sorts

for &size in &sizes {
for dist in &distributions {
let bench_name = format!("parallel_heapsort_{}_{}", dist, size);

group.bench_function(&bench_name, |b| {
b.iter_batched(
|| generate_data(size, dist),
|mut data| heapsort::sort(&mut data).expect("Sort should succeed"),
BatchSize::LargeInput,
)
});
}
}

group.finish();
}

fn benchmark_std_sort(c: &mut Criterion) {
let sizes = [1_000, 10_000, 100_000, 1_000_000];
let distributions = ["sorted", "reverse", "random", "nearly_sorted", "few_unique"];

let mut group = c.benchmark_group("std_sort");
group.sample_size(10);

for &size in &sizes {
for dist in &distributions {
let bench_name = format!("std_sort_{}_{}", dist, size);

group.bench_function(&bench_name, |b| {
b.iter_batched(
|| generate_data(size, dist),
|mut data| data.sort(),
BatchSize::LargeInput,
)
});
}
}

group.finish();
}

#[cfg(feature = "simd")]
fn benchmark_simd_sort(c: &mut Criterion) {
let sizes = [1_000, 10_000, 100_000, 1_000_000];
let distributions = ["sorted", "reverse", "random", "nearly_sorted", "few_unique"];

let mut group = c.benchmark_group("simd_sort");
group.sample_size(10);

for &size in &sizes {
for dist in &distributions {
let bench_name = format!("simd_sort_{}_{}", dist, size);

group.bench_function(&bench_name, |b| {
b.iter_batched(
|| generate_data(size, dist),
|mut data| heapsort::sort_i32(&mut data).expect("Sort should succeed"),
BatchSize::LargeInput,
)
});
}
}

group.finish();
}

criterion_group! {
name = benches;
config = Criterion::default();
targets = benchmark_heapsort, benchmark_std_sort
}

#[cfg(feature = "parallel")]
criterion_group! {
name = parallel_benches;
config = Criterion::default();
targets = benchmark_parallel_heapsort
}

#[cfg(feature = "simd")]
criterion_group! {
name = simd_benches;
config = Criterion::default();
targets = benchmark_simd_sort
}

// Base benchmarks always run
#[cfg(not(any(feature = "parallel", feature = "simd")))]
criterion_main!(benches);

// With parallel feature only
#[cfg(all(feature = "parallel", not(feature = "simd")))]
criterion_main!(benches, parallel_benches);

// With SIMD feature only
#[cfg(all(feature = "simd", not(feature = "parallel")))]
criterion_main!(benches, simd_benches);

// With both parallel and SIMD features
#[cfg(all(feature = "parallel", feature = "simd"))]
criterion_main!(benches, parallel_benches, simd_benches);
4 changes: 3 additions & 1 deletion crates/cs/blocks-cs-sort/src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Sorting algorithms
pub mod quicksort;
pub mod heapsort;

// Re-export commonly used functions for convenience
pub use self::quicksort::sort as quicksort;
pub use self::quicksort::sort as quicksort;
pub use self::heapsort::sort as heapsort;
Loading

0 comments on commit ee7caf3

Please sign in to comment.