diff --git a/README.md b/README.md index 79ab596..d6a8b18 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,20 @@ remove 5% update 10% ``` +### Custom + +Define your own workload pattern with flexible operation percentages. + ## How to run it? ```sh mv results results.bk -./scripts/bench.bash -./scripts/plot.bash + +./scripts/bench.bash # benchmark all +# ./scripts/bench.bash -w Custom -h std --read 60 --insert 20 --remove 20 --skip CHashMap + +./scripts/plot.bash # plot all +# ./scripts/plot.bash -f ReadHeavy.std,Custom.std ``` ## Results diff --git a/scripts/bench.bash b/scripts/bench.bash index dbd5b8a..d34545e 100755 --- a/scripts/bench.bash +++ b/scripts/bench.bash @@ -1,36 +1,60 @@ #!/usr/bin/env bash -set -x - BIN=./target/release/conc-map-bench OUT=./results +WORKLOADS=("ReadHeavy" "Exchange" "RapidGrow") +HASHERS=("std" "ahash") + +EXTRA_ARGS=() +while [[ $# -gt 0 ]]; do + case $1 in + -w|--workload) + if [[ "$2" == "Custom" ]]; then + WORKLOADS=("Custom") + else + IFS=',' read -ra WORKLOADS <<< "$2" + fi + shift 2 + ;; + -h|--hasher) + IFS=',' read -ra HASHERS <<< "$2" + shift 2 + ;; + *) + # Pass all other arguments through + EXTRA_ARGS+=("$1") + shift + ;; + esac +done + +set -x + cargo build --release mkdir -p "$OUT" function bench { - ARGS=$3 + local workload="$1" + local hasher="$2" + date - file="$OUT/$1.$2.csv" + file="$OUT/$workload.$hasher.csv" if [ -s "$file" ]; then - ARGS+=" --csv-no-headers" + EXTRA_ARGS+=("--csv-no-headers") fi - skip=$(cat "$file" | cut -d, -f1 | uniq | paste -sd ' ' -) - - if ! "$BIN" bench -w $1 -h $2 $ARGS --skip $skip --csv 2>>"$file"; then - bench "$1" "$2" "$3" + if ! "$BIN" bench -w "$workload" -h "$hasher" "${EXTRA_ARGS[@]}" --csv 2>>"$file"; then + bench "$workload" "$hasher" fi } -bench ReadHeavy std -bench Exchange std -bench RapidGrow std - -bench ReadHeavy ahash -bench Exchange ahash -bench RapidGrow ahash +for workload in "${WORKLOADS[@]}"; do + for hasher in "${HASHERS[@]}"; do + bench "$workload" "$hasher" + done +done date diff --git a/scripts/plot.bash b/scripts/plot.bash index 4e39cc8..9d20f8f 100755 --- a/scripts/plot.bash +++ b/scripts/plot.bash @@ -1,19 +1,34 @@ #!/usr/bin/env bash -set -e - BIN=./target/release/conc-map-bench DATA_DIR=results +DATA_FILES=("ReadHeavy.std" "Exchange.std" "RapidGrow.std" "ReadHeavy.ahash" "Exchange.ahash" "RapidGrow.ahash") + +while [[ $# -gt 0 ]]; do + case $1 in + -f|--files) + IFS=',' read -ra DATA_FILES <<< "$2" + shift 2 + ;; + esac +done + +set -x + cargo build --release function plot { - cat "$DATA_DIR/$1.csv" | "$BIN" plot "$DATA_DIR" "$1" + local data_file="$1" + + if [ ! -f "$DATA_DIR/$data_file.csv" ]; then + echo "Warning: $DATA_DIR/$data_file.csv not found, skipping..." + return + fi + + cat "$DATA_DIR/$data_file.csv" | "$BIN" plot "$DATA_DIR" "$data_file" } -plot ReadHeavy.std -plot Exchange.std -plot RapidGrow.std -plot ReadHeavy.ahash -plot Exchange.ahash -plot RapidGrow.ahash +for data_file in "${DATA_FILES[@]}"; do + plot "$data_file" +done \ No newline at end of file diff --git a/src/bench.rs b/src/bench.rs index 7f7541e..c7cc3e6 100644 --- a/src/bench.rs +++ b/src/bench.rs @@ -40,6 +40,22 @@ pub struct Options { pub csv: bool, #[structopt(long)] pub csv_no_headers: bool, + + // Custom workload parameters + #[structopt(long, help = "Read percentage (0-100)")] + pub read: Option, + #[structopt(long, help = "Insert percentage (0-100)")] + pub insert: Option, + #[structopt(long, help = "Remove percentage (0-100)")] + pub remove: Option, + #[structopt(long, help = "Update percentage (0-100)")] + pub update: Option, + #[structopt(long, help = "Upsert percentage (0-100)")] + pub upsert: Option, + #[structopt(long, help = "Initial capacity log2", default_value = "25")] + pub capacity_log2: u8, + #[structopt(long, help = "Prefill fraction (0.0-1.0)", default_value = "0.75")] + pub prefill: f64, } fn gc_cycle(options: &Options) { @@ -51,6 +67,27 @@ fn gc_cycle(options: &Options) { } } +fn validate_workload(options: &Options) -> Result<(), &str> { + if matches!(options.workload, workloads::WorkloadKind::Custom) { + let read = options.read.unwrap_or(0); + let insert = options.insert.unwrap_or(0); + let remove = options.remove.unwrap_or(0); + let update = options.update.unwrap_or(0); + let upsert = options.upsert.unwrap_or(0); + let prefill = options.prefill; + + let total = read + insert + remove + update + upsert; + + if total != 100 { + return Err("operations percentages must sum to 100"); + } + if prefill < 0.0 || prefill > 1.0 { + return Err("prefill must be between 0.0 and 1.0"); + } + } + Ok(()) +} + type Handler = Box; fn case(name: &str, options: &Options, handler: &mut Handler) @@ -124,6 +161,12 @@ where } pub fn bench(options: &Options) { + // Process custom workload options, if parameters are provided + if let Err(e) = validate_workload(options) { + eprintln!("Error validating custom workload: {}", e); + std::process::exit(1); + } + println!("== {:?}", options.workload); let mut handler = if options.csv { diff --git a/src/workloads.rs b/src/workloads.rs index cd724e7..cce3f93 100644 --- a/src/workloads.rs +++ b/src/workloads.rs @@ -9,6 +9,7 @@ pub enum WorkloadKind { ReadHeavy, Exchange, RapidGrow, + Custom, } impl FromStr for WorkloadKind { @@ -19,6 +20,7 @@ impl FromStr for WorkloadKind { "ReadHeavy" => Ok(Self::ReadHeavy), "Exchange" => Ok(Self::Exchange), "RapidGrow" => Ok(Self::RapidGrow), + "Custom" => Ok(Self::Custom), _ => Err("unknown workload"), } } @@ -66,11 +68,27 @@ fn exchange(threads: u32) -> Workload { .prefill_fraction(0.75) } +fn custom(options: &Options, threads: u32) -> Workload { + // already checked the percentages sum to 100 + let mix = Mix { + read: options.read.unwrap_or(0), + insert: options.insert.unwrap_or(0), + remove: options.remove.unwrap_or(0), + update: options.update.unwrap_or(0), + upsert: options.upsert.unwrap_or(0), + }; + + *Workload::new(threads as usize, mix) + .initial_capacity_log2(options.capacity_log2) + .prefill_fraction(options.prefill) +} + pub(crate) fn create(options: &Options, threads: u32) -> Workload { let mut workload = match options.workload { WorkloadKind::ReadHeavy => read_heavy(threads), WorkloadKind::Exchange => exchange(threads), WorkloadKind::RapidGrow => rapid_grow(threads), + WorkloadKind::Custom => custom(options, threads), }; workload.operations(options.operations);