1#!/usr/bin/env bash 2# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 3# REQUIRE: db_bench binary exists in the current directory 4 5if [ $# -ne 1 ]; then 6 echo -n "./benchmark.sh [bulkload/fillseq/overwrite/filluniquerandom/" 7 echo "readrandom/readwhilewriting/readwhilemerging/updaterandom/" 8 echo "mergerandom/randomtransaction/compact]" 9 exit 0 10fi 11 12# Make it easier to run only the compaction test. Getting valid data requires 13# a number of iterations and having an ability to run the test separately from 14# rest of the benchmarks helps. 15if [ "$COMPACTION_TEST" == "1" -a "$1" != "universal_compaction" ]; then 16 echo "Skipping $1 because it's not a compaction test." 17 exit 0 18fi 19 20# size constants 21K=1024 22M=$((1024 * K)) 23G=$((1024 * M)) 24T=$((1024 * G)) 25 26if [ -z $DB_DIR ]; then 27 echo "DB_DIR is not defined" 28 exit 0 29fi 30 31if [ -z $WAL_DIR ]; then 32 echo "WAL_DIR is not defined" 33 exit 0 34fi 35 36output_dir=${OUTPUT_DIR:-/tmp/} 37if [ ! -d $output_dir ]; then 38 mkdir -p $output_dir 39fi 40 41# all multithreaded tests run with sync=1 unless 42# $DB_BENCH_NO_SYNC is defined 43syncval="1" 44if [ ! -z $DB_BENCH_NO_SYNC ]; then 45 echo "Turning sync off for all multithreaded tests" 46 syncval="0"; 47fi 48 49num_threads=${NUM_THREADS:-64} 50mb_written_per_sec=${MB_WRITE_PER_SEC:-0} 51# Only for tests that do range scans 52num_nexts_per_seek=${NUM_NEXTS_PER_SEEK:-10} 53cache_size=${CACHE_SIZE:-$((17179869184))} 54compression_max_dict_bytes=${COMPRESSION_MAX_DICT_BYTES:-0} 55compression_type=${COMPRESSION_TYPE:-zstd} 56duration=${DURATION:-0} 57 58num_keys=${NUM_KEYS:-8000000000} 59key_size=${KEY_SIZE:-20} 60value_size=${VALUE_SIZE:-400} 61block_size=${BLOCK_SIZE:-8192} 62 63const_params=" 64 --db=$DB_DIR \ 65 --wal_dir=$WAL_DIR \ 66 \ 67 --num=$num_keys \ 68 --num_levels=6 \ 69 --key_size=$key_size \ 70 --value_size=$value_size \ 71 --block_size=$block_size \ 72 --cache_size=$cache_size \ 73 --cache_numshardbits=6 \ 74 --compression_max_dict_bytes=$compression_max_dict_bytes \ 75 --compression_ratio=0.5 \ 76 --compression_type=$compression_type \ 77 --level_compaction_dynamic_level_bytes=true \ 78 --bytes_per_sync=$((8 * M)) \ 79 --cache_index_and_filter_blocks=0 \ 80 --pin_l0_filter_and_index_blocks_in_cache=1 \ 81 --benchmark_write_rate_limit=$(( 1024 * 1024 * $mb_written_per_sec )) \ 82 \ 83 --hard_rate_limit=3 \ 84 --rate_limit_delay_max_milliseconds=1000000 \ 85 --write_buffer_size=$((128 * M)) \ 86 --target_file_size_base=$((128 * M)) \ 87 --max_bytes_for_level_base=$((1 * G)) \ 88 \ 89 --verify_checksum=1 \ 90 --delete_obsolete_files_period_micros=$((60 * M)) \ 91 --max_bytes_for_level_multiplier=8 \ 92 \ 93 --statistics=0 \ 94 --stats_per_interval=1 \ 95 --stats_interval_seconds=60 \ 96 --histogram=1 \ 97 \ 98 --memtablerep=skip_list \ 99 --bloom_bits=10 \ 100 --open_files=-1" 101 102l0_config=" 103 --level0_file_num_compaction_trigger=4 \ 104 --level0_stop_writes_trigger=20" 105 106if [ $duration -gt 0 ]; then 107 const_params="$const_params --duration=$duration" 108fi 109 110params_w="$const_params \ 111 $l0_config \ 112 --max_background_compactions=16 \ 113 --max_write_buffer_number=8 \ 114 --max_background_flushes=7" 115 116params_bulkload="$const_params \ 117 --max_background_compactions=16 \ 118 --max_write_buffer_number=8 \ 119 --allow_concurrent_memtable_write=false \ 120 --max_background_flushes=7 \ 121 --level0_file_num_compaction_trigger=$((10 * M)) \ 122 --level0_slowdown_writes_trigger=$((10 * M)) \ 123 --level0_stop_writes_trigger=$((10 * M))" 124 125params_fillseq="$params_w \ 126 --allow_concurrent_memtable_write=false" 127# 128# Tune values for level and universal compaction. 129# For universal compaction, these level0_* options mean total sorted of runs in 130# LSM. In level-based compaction, it means number of L0 files. 131# 132params_level_compact="$const_params \ 133 --max_background_flushes=4 \ 134 --max_write_buffer_number=4 \ 135 --level0_file_num_compaction_trigger=4 \ 136 --level0_slowdown_writes_trigger=16 \ 137 --level0_stop_writes_trigger=20" 138 139params_univ_compact="$const_params \ 140 --max_background_flushes=4 \ 141 --max_write_buffer_number=4 \ 142 --level0_file_num_compaction_trigger=8 \ 143 --level0_slowdown_writes_trigger=16 \ 144 --level0_stop_writes_trigger=20" 145 146function summarize_result { 147 test_out=$1 148 test_name=$2 149 bench_name=$3 150 151 # Note that this function assumes that the benchmark executes long enough so 152 # that "Compaction Stats" is written to stdout at least once. If it won't 153 # happen then empty output from grep when searching for "Sum" will cause 154 # syntax errors. 155 uptime=$( grep ^Uptime\(secs $test_out | tail -1 | awk '{ printf "%.0f", $2 }' ) 156 stall_time=$( grep "^Cumulative stall" $test_out | tail -1 | awk '{ print $3 }' ) 157 stall_pct=$( grep "^Cumulative stall" $test_out| tail -1 | awk '{ print $5 }' ) 158 ops_sec=$( grep ^${bench_name} $test_out | awk '{ print $5 }' ) 159 mb_sec=$( grep ^${bench_name} $test_out | awk '{ print $7 }' ) 160 lo_wgb=$( grep "^ L0" $test_out | tail -1 | awk '{ print $9 }' ) 161 sum_wgb=$( grep "^ Sum" $test_out | tail -1 | awk '{ print $9 }' ) 162 sum_size=$( grep "^ Sum" $test_out | tail -1 | awk '{ printf "%.1f", $3 / 1024.0 }' ) 163 wamp=$( echo "scale=1; $sum_wgb / $lo_wgb" | bc ) 164 wmb_ps=$( echo "scale=1; ( $sum_wgb * 1024.0 ) / $uptime" | bc ) 165 usecs_op=$( grep ^${bench_name} $test_out | awk '{ printf "%.1f", $3 }' ) 166 p50=$( grep "^Percentiles:" $test_out | tail -1 | awk '{ printf "%.1f", $3 }' ) 167 p75=$( grep "^Percentiles:" $test_out | tail -1 | awk '{ printf "%.1f", $5 }' ) 168 p99=$( grep "^Percentiles:" $test_out | tail -1 | awk '{ printf "%.0f", $7 }' ) 169 p999=$( grep "^Percentiles:" $test_out | tail -1 | awk '{ printf "%.0f", $9 }' ) 170 p9999=$( grep "^Percentiles:" $test_out | tail -1 | awk '{ printf "%.0f", $11 }' ) 171 echo -e "$ops_sec\t$mb_sec\t$sum_size\t$lo_wgb\t$sum_wgb\t$wamp\t$wmb_ps\t$usecs_op\t$p50\t$p75\t$p99\t$p999\t$p9999\t$uptime\t$stall_time\t$stall_pct\t$test_name" \ 172 >> $output_dir/report.txt 173} 174 175function run_bulkload { 176 # This runs with a vector memtable and the WAL disabled to load faster. It is still crash safe and the 177 # client can discover where to restart a load after a crash. I think this is a good way to load. 178 echo "Bulk loading $num_keys random keys" 179 cmd="./db_bench --benchmarks=fillrandom \ 180 --use_existing_db=0 \ 181 --disable_auto_compactions=1 \ 182 --sync=0 \ 183 $params_bulkload \ 184 --threads=1 \ 185 --memtablerep=vector \ 186 --allow_concurrent_memtable_write=false \ 187 --disable_wal=1 \ 188 --seed=$( date +%s ) \ 189 2>&1 | tee -a $output_dir/benchmark_bulkload_fillrandom.log" 190 echo $cmd | tee $output_dir/benchmark_bulkload_fillrandom.log 191 eval $cmd 192 summarize_result $output_dir/benchmark_bulkload_fillrandom.log bulkload fillrandom 193 echo "Compacting..." 194 cmd="./db_bench --benchmarks=compact \ 195 --use_existing_db=1 \ 196 --disable_auto_compactions=1 \ 197 --sync=0 \ 198 $params_w \ 199 --threads=1 \ 200 2>&1 | tee -a $output_dir/benchmark_bulkload_compact.log" 201 echo $cmd | tee $output_dir/benchmark_bulkload_compact.log 202 eval $cmd 203} 204 205# 206# Parameter description: 207# 208# $1 - 1 if I/O statistics should be collected. 209# $2 - compaction type to use (level=0, universal=1). 210# $3 - number of subcompactions. 211# $4 - number of maximum background compactions. 212# 213function run_manual_compaction_worker { 214 # This runs with a vector memtable and the WAL disabled to load faster. 215 # It is still crash safe and the client can discover where to restart a 216 # load after a crash. I think this is a good way to load. 217 echo "Bulk loading $num_keys random keys for manual compaction." 218 219 fillrandom_output_file=$output_dir/benchmark_man_compact_fillrandom_$3.log 220 man_compact_output_log=$output_dir/benchmark_man_compact_$3.log 221 222 if [ "$2" == "1" ]; then 223 extra_params=$params_univ_compact 224 else 225 extra_params=$params_level_compact 226 fi 227 228 # Make sure that fillrandom uses the same compaction options as compact. 229 cmd="./db_bench --benchmarks=fillrandom \ 230 --use_existing_db=0 \ 231 --disable_auto_compactions=0 \ 232 --sync=0 \ 233 $extra_params \ 234 --threads=$num_threads \ 235 --compaction_measure_io_stats=$1 \ 236 --compaction_style=$2 \ 237 --subcompactions=$3 \ 238 --memtablerep=vector \ 239 --allow_concurrent_memtable_write=false \ 240 --disable_wal=1 \ 241 --max_background_compactions=$4 \ 242 --seed=$( date +%s ) \ 243 2>&1 | tee -a $fillrandom_output_file" 244 245 echo $cmd | tee $fillrandom_output_file 246 eval $cmd 247 248 summarize_result $fillrandom_output_file man_compact_fillrandom_$3 fillrandom 249 250 echo "Compacting with $3 subcompactions specified ..." 251 252 # This is the part we're really interested in. Given that compact benchmark 253 # doesn't output regular statistics then we'll just use the time command to 254 # measure how long this step takes. 255 cmd="{ \ 256 time ./db_bench --benchmarks=compact \ 257 --use_existing_db=1 \ 258 --disable_auto_compactions=0 \ 259 --sync=0 \ 260 $extra_params \ 261 --threads=$num_threads \ 262 --compaction_measure_io_stats=$1 \ 263 --compaction_style=$2 \ 264 --subcompactions=$3 \ 265 --max_background_compactions=$4 \ 266 ;} 267 2>&1 | tee -a $man_compact_output_log" 268 269 echo $cmd | tee $man_compact_output_log 270 eval $cmd 271 272 # Can't use summarize_result here. One way to analyze the results is to run 273 # "grep real" on the resulting log files. 274} 275 276function run_univ_compaction { 277 # Always ask for I/O statistics to be measured. 278 io_stats=1 279 280 # Values: kCompactionStyleLevel = 0x0, kCompactionStyleUniversal = 0x1. 281 compaction_style=1 282 283 # Define a set of benchmarks. 284 subcompactions=(1 2 4 8 16) 285 max_background_compactions=(16 16 8 4 2) 286 287 i=0 288 total=${#subcompactions[@]} 289 290 # Execute a set of benchmarks to cover variety of scenarios. 291 while [ "$i" -lt "$total" ] 292 do 293 run_manual_compaction_worker $io_stats $compaction_style ${subcompactions[$i]} \ 294 ${max_background_compactions[$i]} 295 ((i++)) 296 done 297} 298 299function run_fillseq { 300 # This runs with a vector memtable. WAL can be either disabled or enabled 301 # depending on the input parameter (1 for disabled, 0 for enabled). The main 302 # benefit behind disabling WAL is to make loading faster. It is still crash 303 # safe and the client can discover where to restart a load after a crash. I 304 # think this is a good way to load. 305 306 # Make sure that we'll have unique names for all the files so that data won't 307 # be overwritten. 308 if [ $1 == 1 ]; then 309 log_file_name=$output_dir/benchmark_fillseq.wal_disabled.v${value_size}.log 310 test_name=fillseq.wal_disabled.v${value_size} 311 else 312 log_file_name=$output_dir/benchmark_fillseq.wal_enabled.v${value_size}.log 313 test_name=fillseq.wal_enabled.v${value_size} 314 fi 315 316 echo "Loading $num_keys keys sequentially" 317 cmd="./db_bench --benchmarks=fillseq \ 318 --use_existing_db=0 \ 319 --sync=0 \ 320 $params_fillseq \ 321 --min_level_to_compress=0 \ 322 --threads=1 \ 323 --memtablerep=vector \ 324 --allow_concurrent_memtable_write=false \ 325 --disable_wal=$1 \ 326 --seed=$( date +%s ) \ 327 2>&1 | tee -a $log_file_name" 328 echo $cmd | tee $log_file_name 329 eval $cmd 330 331 # The constant "fillseq" which we pass to db_bench is the benchmark name. 332 summarize_result $log_file_name $test_name fillseq 333} 334 335function run_change { 336 operation=$1 337 echo "Do $num_keys random $operation" 338 out_name="benchmark_${operation}.t${num_threads}.s${syncval}.log" 339 cmd="./db_bench --benchmarks=$operation \ 340 --use_existing_db=1 \ 341 --sync=$syncval \ 342 $params_w \ 343 --threads=$num_threads \ 344 --merge_operator=\"put\" \ 345 --seed=$( date +%s ) \ 346 2>&1 | tee -a $output_dir/${out_name}" 347 echo $cmd | tee $output_dir/${out_name} 348 eval $cmd 349 summarize_result $output_dir/${out_name} ${operation}.t${num_threads}.s${syncval} $operation 350} 351 352function run_filluniquerandom { 353 echo "Loading $num_keys unique keys randomly" 354 cmd="./db_bench --benchmarks=filluniquerandom \ 355 --use_existing_db=0 \ 356 --sync=0 \ 357 $params_w \ 358 --threads=1 \ 359 --seed=$( date +%s ) \ 360 2>&1 | tee -a $output_dir/benchmark_filluniquerandom.log" 361 echo $cmd | tee $output_dir/benchmark_filluniquerandom.log 362 eval $cmd 363 summarize_result $output_dir/benchmark_filluniquerandom.log filluniquerandom filluniquerandom 364} 365 366function run_readrandom { 367 echo "Reading $num_keys random keys" 368 out_name="benchmark_readrandom.t${num_threads}.log" 369 cmd="./db_bench --benchmarks=readrandom \ 370 --use_existing_db=1 \ 371 $params_w \ 372 --threads=$num_threads \ 373 --seed=$( date +%s ) \ 374 2>&1 | tee -a $output_dir/${out_name}" 375 echo $cmd | tee $output_dir/${out_name} 376 eval $cmd 377 summarize_result $output_dir/${out_name} readrandom.t${num_threads} readrandom 378} 379 380function run_readwhile { 381 operation=$1 382 echo "Reading $num_keys random keys while $operation" 383 out_name="benchmark_readwhile${operation}.t${num_threads}.log" 384 cmd="./db_bench --benchmarks=readwhile${operation} \ 385 --use_existing_db=1 \ 386 --sync=$syncval \ 387 $params_w \ 388 --threads=$num_threads \ 389 --merge_operator=\"put\" \ 390 --seed=$( date +%s ) \ 391 2>&1 | tee -a $output_dir/${out_name}" 392 echo $cmd | tee $output_dir/${out_name} 393 eval $cmd 394 summarize_result $output_dir/${out_name} readwhile${operation}.t${num_threads} readwhile${operation} 395} 396 397function run_rangewhile { 398 operation=$1 399 full_name=$2 400 reverse_arg=$3 401 out_name="benchmark_${full_name}.t${num_threads}.log" 402 echo "Range scan $num_keys random keys while ${operation} for reverse_iter=${reverse_arg}" 403 cmd="./db_bench --benchmarks=seekrandomwhile${operation} \ 404 --use_existing_db=1 \ 405 --sync=$syncval \ 406 $params_w \ 407 --threads=$num_threads \ 408 --merge_operator=\"put\" \ 409 --seek_nexts=$num_nexts_per_seek \ 410 --reverse_iterator=$reverse_arg \ 411 --seed=$( date +%s ) \ 412 2>&1 | tee -a $output_dir/${out_name}" 413 echo $cmd | tee $output_dir/${out_name} 414 eval $cmd 415 summarize_result $output_dir/${out_name} ${full_name}.t${num_threads} seekrandomwhile${operation} 416} 417 418function run_range { 419 full_name=$1 420 reverse_arg=$2 421 out_name="benchmark_${full_name}.t${num_threads}.log" 422 echo "Range scan $num_keys random keys for reverse_iter=${reverse_arg}" 423 cmd="./db_bench --benchmarks=seekrandom \ 424 --use_existing_db=1 \ 425 $params_w \ 426 --threads=$num_threads \ 427 --seek_nexts=$num_nexts_per_seek \ 428 --reverse_iterator=$reverse_arg \ 429 --seed=$( date +%s ) \ 430 2>&1 | tee -a $output_dir/${out_name}" 431 echo $cmd | tee $output_dir/${out_name} 432 eval $cmd 433 summarize_result $output_dir/${out_name} ${full_name}.t${num_threads} seekrandom 434} 435 436function run_randomtransaction { 437 echo "..." 438 cmd="./db_bench $params_r --benchmarks=randomtransaction \ 439 --num=$num_keys \ 440 --transaction_db \ 441 --threads=5 \ 442 --transaction_sets=5 \ 443 2>&1 | tee $output_dir/benchmark_randomtransaction.log" 444 echo $cmd | tee $output_dir/benchmark_rangescanwhilewriting.log 445 eval $cmd 446} 447 448function now() { 449 echo `date +"%s"` 450} 451 452report="$output_dir/report.txt" 453schedule="$output_dir/schedule.txt" 454 455echo "===== Benchmark =====" 456 457# Run!!! 458IFS=',' read -a jobs <<< $1 459# shellcheck disable=SC2068 460for job in ${jobs[@]}; do 461 462 if [ $job != debug ]; then 463 echo "Start $job at `date`" | tee -a $schedule 464 fi 465 466 start=$(now) 467 if [ $job = bulkload ]; then 468 run_bulkload 469 elif [ $job = fillseq_disable_wal ]; then 470 run_fillseq 1 471 elif [ $job = fillseq_enable_wal ]; then 472 run_fillseq 0 473 elif [ $job = overwrite ]; then 474 syncval="0" 475 params_w="$params_w \ 476 --writes=125000000 \ 477 --subcompactions=4 \ 478 --soft_pending_compaction_bytes_limit=$((1 * T)) \ 479 --hard_pending_compaction_bytes_limit=$((4 * T)) " 480 run_change overwrite 481 elif [ $job = updaterandom ]; then 482 run_change updaterandom 483 elif [ $job = mergerandom ]; then 484 run_change mergerandom 485 elif [ $job = filluniquerandom ]; then 486 run_filluniquerandom 487 elif [ $job = readrandom ]; then 488 run_readrandom 489 elif [ $job = fwdrange ]; then 490 run_range $job false 491 elif [ $job = revrange ]; then 492 run_range $job true 493 elif [ $job = readwhilewriting ]; then 494 run_readwhile writing 495 elif [ $job = readwhilemerging ]; then 496 run_readwhile merging 497 elif [ $job = fwdrangewhilewriting ]; then 498 run_rangewhile writing $job false 499 elif [ $job = revrangewhilewriting ]; then 500 run_rangewhile writing $job true 501 elif [ $job = fwdrangewhilemerging ]; then 502 run_rangewhile merging $job false 503 elif [ $job = revrangewhilemerging ]; then 504 run_rangewhile merging $job true 505 elif [ $job = randomtransaction ]; then 506 run_randomtransaction 507 elif [ $job = universal_compaction ]; then 508 run_univ_compaction 509 elif [ $job = debug ]; then 510 num_keys=1000; # debug 511 echo "Setting num_keys to $num_keys" 512 else 513 echo "unknown job $job" 514 exit 515 fi 516 end=$(now) 517 518 if [ $job != debug ]; then 519 echo "Complete $job in $((end-start)) seconds" | tee -a $schedule 520 fi 521 522 echo -e "ops/sec\tmb/sec\tSize-GB\tL0_GB\tSum_GB\tW-Amp\tW-MB/s\tusec/op\tp50\tp75\tp99\tp99.9\tp99.99\tUptime\tStall-time\tStall%\tTest" 523 tail -1 $output_dir/report.txt 524 525done 526