roc/crates/compiler/test_gen/benches/quicksort.rs
2024-01-26 16:06:07 -05:00

163 lines
4.8 KiB
Rust

#[path = "../src/helpers/mod.rs"]
mod helpers;
// defines roc_alloc and friends
pub use helpers::platform_functions::*;
use bumpalo::Bump;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc_dylib};
use roc_mono::ir::OptLevel;
use roc_std::RocList;
// results April 9, 2023
//
// > pure roc quicksort time: [106.97 us 107.27 us 107.63 us]
// > roc zig quicksort time: [34.765 us 35.301 us 35.865 us]
// > rust std sort time: [20.413 us 20.623 us 20.838 us]
type Input = RocList<i64>;
type Output = RocList<i64>;
type Main<I, O> = unsafe extern "C" fn(I, *mut RocCallResult<O>);
const ZIG_ROC_QUICKSORT: &str = indoc::indoc!(
r#"
app "bench" provides [main] to "./platform"
main : List I64 -> List I64
main = \originalList -> List.sortAsc originalList
"#
);
const PURE_ROC_QUICKSORT: &str = indoc::indoc!(
r#"
app "bench" provides [main] to "./platform"
main : List I64 -> List I64
main = \originalList ->
n = List.len originalList
quicksortHelp originalList 0 (n - 1)
quicksortHelp : List (Num a), U64, U64 -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
when partition low high list is
Pair partitionIndex partitioned ->
partitioned
|> quicksortHelp low (partitionIndex - 1)
|> quicksortHelp (partitionIndex + 1) high
else
list
partition : U64, U64, List (Num a) -> [Pair U64 (List (Num a))]
partition = \low, high, initialList ->
when List.get initialList high is
Ok pivot ->
when partitionHelp low low initialList high pivot is
Pair newI newList ->
Pair newI (List.swap newList newI high)
Err _ ->
Pair low initialList
partitionHelp : U64, U64, List (Num c), U64, Num c -> [Pair U64 (List (Num c))]
partitionHelp = \i, j, list, high, pivot ->
if j < high then
when List.get list j is
Ok value ->
if value <= pivot then
partitionHelp (i + 1) (j + 1) (List.swap list i j) high pivot
else
partitionHelp i (j + 1) list high pivot
Err _ ->
Pair i list
else
Pair i list
"#
);
fn roc_function<'a>(
arena: &'a Bump,
source: &str,
) -> libloading::Symbol<'a, Main<*mut Input, Output>> {
let config = helpers::llvm::HelperConfig {
mode: LlvmBackendMode::GenTest,
ignore_problems: false,
emit_debug_info: true,
opt_level: OptLevel::Optimize,
};
let context = inkwell::context::Context::create();
let (main_fn_name, errors, lib) = helpers::llvm::helper(
arena,
config,
source,
arena.alloc(context),
roc_load::FunctionKind::LambdaSet,
);
assert!(errors.is_empty(), "Encountered errors:\n{errors}");
run_roc_dylib!(arena.alloc(lib), main_fn_name, *mut Input, Output)
}
pub fn criterion_benchmark(c: &mut Criterion) {
let arena = Bump::new();
let pure_roc_quicksort_main = roc_function(&arena, PURE_ROC_QUICKSORT);
let roc_zig_quicksort_main = roc_function(&arena, ZIG_ROC_QUICKSORT);
let input_numbers: Vec<_> = std::iter::repeat([1, 2, 3, 4, 5, 6, 7, 8])
.flatten()
.take(1000)
.collect();
let input = arena.alloc(RocList::from_slice(&input_numbers));
c.bench_function("pure roc quicksort", |b| {
b.iter(|| unsafe {
let mut main_result = RocCallResult::default();
assert!(input.is_unique());
// reset_input
input.copy_from_slice(&input_numbers);
pure_roc_quicksort_main(black_box(input), &mut main_result);
})
});
c.bench_function("roc zig quicksort", |b| {
b.iter(|| unsafe {
let mut main_result = RocCallResult::default();
assert!(input.is_unique());
// reset_input
input.copy_from_slice(&input_numbers);
roc_zig_quicksort_main(black_box(input), &mut main_result);
})
});
c.bench_function("rust std sort", |b| {
b.iter(|| unsafe {
assert!(input.is_unique());
// reset_input
input.copy_from_slice(&input_numbers);
// an attempt to block optimizing based on the input list
let ptr = black_box(input.as_mut_ptr());
let input = std::slice::from_raw_parts_mut(ptr, 1000);
input.sort()
})
});
}
criterion_group!(quicksort_benches, criterion_benchmark);
criterion_main!(quicksort_benches);