non_blocking_mutex 3.1.3

NonBlockingMutex is currently the fastest way to do expensive calculations under lock, or do cheap calculations under lock when concurrency/load/contention is very high
Documentation
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use nameof::name_of;
use non_blocking_mutex::dynamic_non_blocking_mutex::DynamicNonBlockingMutex;
use non_blocking_mutex::mutex_guard::MutexGuard;
use non_blocking_mutex::non_blocking_mutex::NonBlockingMutex;
use std::hint;
use std::sync::Mutex;
use std::thread::{available_parallelism, scope};

fn increment_once_without_mutex(number: &mut usize) {
    *number += 1;
    black_box(number);
}

fn increment_once_under_non_blocking_mutex_static<Task: Send + FnOnce(MutexGuard<'_, usize>)>(
    non_blocking_mutex: &NonBlockingMutex<usize, Task>,
    task: Task,
) {
    non_blocking_mutex.run_if_first_or_schedule_on_first(task);
}

fn increment_once_under_non_blocking_mutex_dynamic(
    non_blocking_mutex: &DynamicNonBlockingMutex<usize>,
) {
    non_blocking_mutex.run_fn_once_if_first_or_schedule_on_first(|mut state: MutexGuard<usize>| {
        *state += 1;
        black_box(state);
    });
}

fn increment_once_under_mutex_blockingly(number_mutex: &Mutex<usize>) {
    let mut number = number_mutex.lock().unwrap();
    *number += 1;
    black_box(*number);
}

fn increment_once_under_mutex_spinny(number_mutex: &Mutex<usize>) {
    loop {
        let try_lock_result = number_mutex.try_lock();

        if let Ok(mut number) = try_lock_result {
            *number += 1;
            black_box(*number);

            break;
        }
    }
}

fn increment_under_non_blocking_mutex_concurrently_static(
    max_concurrent_thread_count: usize,
    operation_count: usize,
    spin_under_lock_count: usize,
) {
    let non_blocking_mutex = NonBlockingMutex::new(max_concurrent_thread_count, 0);

    scope(|scope| {
        for _ in 0..max_concurrent_thread_count {
            scope.spawn(|| {
                for _i in 0..operation_count {
                    non_blocking_mutex.run_if_first_or_schedule_on_first(
                        move |mut state: MutexGuard<usize>| {
                            *state += 1;
                            black_box(*state);

                            let mut spin_under_lock_count_left = spin_under_lock_count;
                            while spin_under_lock_count_left != 0 {
                                hint::spin_loop();
                                spin_under_lock_count_left -= 1;
                            }
                        },
                    )
                }
            });
        }
    });
}

fn increment_under_non_blocking_mutex_concurrently_dynamic(
    max_concurrent_thread_count: usize,
    operation_count: usize,
    spin_under_lock_count: usize,
) {
    let non_blocking_mutex = DynamicNonBlockingMutex::new(max_concurrent_thread_count, 0);

    scope(|scope| {
        for _ in 0..max_concurrent_thread_count {
            scope.spawn(|| {
                for _i in 0..operation_count {
                    non_blocking_mutex.run_fn_once_if_first_or_schedule_on_first(
                        move |mut state| {
                            *state += 1;
                            black_box(*state);

                            let mut spin_under_lock_count_left = spin_under_lock_count;
                            while spin_under_lock_count_left != 0 {
                                hint::spin_loop();
                                spin_under_lock_count_left -= 1;
                            }
                        },
                    )
                }
            });
        }
    });
}

fn increment_under_mutex_blockingly_concurrently(
    max_concurrent_thread_count: usize,
    operation_count: usize,
    spin_under_lock_count: usize,
) {
    let state_mutex = Mutex::new(0);

    scope(|scope| {
        for _ in 0..max_concurrent_thread_count {
            scope.spawn(|| {
                for _i in 0..operation_count {
                    let mut state = state_mutex.lock().unwrap();

                    *state += 1;
                    black_box(*state);

                    let mut spin_under_lock_count_left = spin_under_lock_count;
                    while spin_under_lock_count_left != 0 {
                        hint::spin_loop();
                        spin_under_lock_count_left -= 1;
                    }
                }
            });
        }
    });
}

fn increment_under_mutex_spinny_concurrently(
    max_concurrent_thread_count: usize,
    operation_count: usize,
    spin_under_lock_count: usize,
) {
    let state_mutex = Mutex::new(0);

    scope(|scope| {
        for _ in 0..max_concurrent_thread_count {
            scope.spawn(|| {
                for _i in 0..operation_count {
                    loop {
                        let try_lock_result = state_mutex.try_lock();

                        if let Ok(mut state) = try_lock_result {
                            *state += 1;
                            black_box(*state);

                            let mut spin_under_lock_count_left = spin_under_lock_count;
                            while spin_under_lock_count_left != 0 {
                                hint::spin_loop();
                                spin_under_lock_count_left -= 1;
                            }

                            break;
                        }
                    }
                }
            });
        }
    });
}

fn criterion_benchmark(criterion: &mut Criterion) {
    struct BenchFnAndName {
        bench_fn: fn(usize, usize, usize),
        bench_fn_name: String,
    }

    let bench_fn_and_name_list = [
        BenchFnAndName {
            bench_fn: increment_under_non_blocking_mutex_concurrently_static,
            bench_fn_name: String::from(name_of!(
                increment_under_non_blocking_mutex_concurrently_static
            )),
        },
        BenchFnAndName {
            bench_fn: increment_under_non_blocking_mutex_concurrently_dynamic,
            bench_fn_name: String::from(name_of!(
                increment_under_non_blocking_mutex_concurrently_dynamic
            )),
        },
        BenchFnAndName {
            bench_fn: increment_under_mutex_blockingly_concurrently,
            bench_fn_name: String::from(name_of!(increment_under_mutex_blockingly_concurrently)),
        },
        BenchFnAndName {
            bench_fn: increment_under_mutex_spinny_concurrently,
            bench_fn_name: String::from(name_of!(increment_under_mutex_spinny_concurrently)),
        },
    ];

    let operation_count_per_thread_list = [1e3 as usize, 1e4 as usize];

    let spin_under_lock_count_list = [0usize, 1e1 as usize, 1e2 as usize];

    let max_concurrent_thread_count = available_parallelism().unwrap().get();

    criterion.bench_function(name_of!(increment_once_without_mutex), |bencher| {
        let ref mut number = black_box(0);
        bencher.iter(|| increment_once_without_mutex(number))
    });
    criterion.bench_function(
        "increment_once_under_non_blocking_mutex_static",
        |bencher| {
            let non_blocking_mutex =
                black_box(NonBlockingMutex::new(max_concurrent_thread_count, 0));

            let task = |mut state: MutexGuard<usize>| {
                *state += 1;
                black_box(state);
            };

            bencher
                .iter(|| increment_once_under_non_blocking_mutex_static(&non_blocking_mutex, task))
        },
    );
    criterion.bench_function(
        name_of!(increment_once_under_non_blocking_mutex_dynamic),
        |bencher| {
            let non_blocking_mutex =
                black_box(DynamicNonBlockingMutex::new(max_concurrent_thread_count, 0));
            bencher.iter(|| increment_once_under_non_blocking_mutex_dynamic(&non_blocking_mutex))
        },
    );
    criterion.bench_function(name_of!(increment_once_under_mutex_blockingly), |bencher| {
        let number_mutex = black_box(Mutex::new(0));
        bencher.iter(|| increment_once_under_mutex_blockingly(&number_mutex))
    });
    criterion.bench_function(name_of!(increment_once_under_mutex_spinny), |bencher| {
        let number_mutex = black_box(Mutex::new(0));
        bencher.iter(|| increment_once_under_mutex_spinny(&number_mutex))
    });

    for spin_under_lock_count in spin_under_lock_count_list {
        for operation_count_per_thread in operation_count_per_thread_list {
            for bench_fn_and_name in bench_fn_and_name_list.iter() {
                let (bench_fn, bench_fn_name) =
                    (bench_fn_and_name.bench_fn, &bench_fn_and_name.bench_fn_name);
                criterion.bench_function(
                    &format!("{bench_fn_name}|{operation_count_per_thread}|{spin_under_lock_count}|{max_concurrent_thread_count}"),
                    |bencher| {
                        bencher.iter(|| {
                            bench_fn(black_box(max_concurrent_thread_count), black_box(operation_count_per_thread), black_box(spin_under_lock_count))
                        })
                    },
                );
            }
        }
    }
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);