benchmark_compare.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. dbdir=$1
  5. odir=$2
  6. # Size Constants
  7. K=1024
  8. M=$((1024 * K))
  9. # Dynamic loader configuration
  10. ld_library_path=${LD_LIBRARY_PATH:-""}
  11. # Benchmark configuration
  12. duration_rw=${DURATION_RW:-65}
  13. duration_ro=${DURATION_RO:-65}
  14. num_keys=${NUM_KEYS:-1000000}
  15. num_threads=${NUM_THREADS:-16}
  16. key_size=${KEY_SIZE:-20}
  17. value_size=${VALUE_SIZE:-400}
  18. mb_write_per_sec=${MB_WRITE_PER_SEC:-2}
  19. ci_tests_only=${CI_TESTS_ONLY:-"false"}
  20. # RocksDB configuration
  21. compression_type=${COMPRESSION_TYPE:-lz4}
  22. subcompactions=${SUBCOMPACTIONS:-1}
  23. write_buffer_size_mb=${WRITE_BUFFER_SIZE_MB:-32}
  24. target_file_size_base_mb=${TARGET_FILE_SIZE_BASE_MB:-32}
  25. max_bytes_for_level_base_mb=${MAX_BYTES_FOR_LEVEL_BASE_MB:-128}
  26. max_background_jobs=${MAX_BACKGROUND_JOBS:-8}
  27. stats_interval_seconds=${STATS_INTERVAL_SECONDS:-20}
  28. cache_index_and_filter_blocks=${CACHE_INDEX_AND_FILTER_BLOCKS:-0}
  29. # USE_O_DIRECT doesn't need a default
  30. bytes_per_sync=${BYTES_PER_SYNC:-$(( 1 * M ))}
  31. # CACHE_SIZE_MB doesn't need a default
  32. min_level_to_compress=${MIN_LEVEL_TO_COMPRESS:-"-1"}
  33. compaction_style=${COMPACTION_STYLE:-leveled}
  34. if [ "$compaction_style" = "leveled" ]; then
  35. echo Use leveled compaction
  36. elif [ "$compaction_style" = "universal" ]; then
  37. echo Use universal compaction
  38. elif [ "$compaction_style" = "blob" ]; then
  39. echo Use blob compaction
  40. else
  41. echo COMPACTION_STYLE is :: "$COMPACTION_STYLE" :: and must be one of leveled, universal, blob
  42. exit 1
  43. fi
  44. # Leveled compaction configuration
  45. level0_file_num_compaction_trigger=${LEVEL0_FILE_NUM_COMPACTION_TRIGGER:-4}
  46. level0_slowdown_writes_trigger=${LEVEL0_SLOWDOWN_WRITES_TRIGGER:-20}
  47. level0_stop_writes_trigger=${LEVEL0_STOP_WRITES_TRIGGER:-30}
  48. per_level_fanout=${PER_LEVEL_FANOUT:-8}
  49. # Universal compaction configuration
  50. universal_min_merge_width=${UNIVERSAL_MIN_MERGE_WIDTH:-2}
  51. universal_max_merge_width=${UNIVERSAL_MAX_MERGE_WIDTH:-20}
  52. universal_size_ratio=${UNIVERSAL_SIZE_RATIO:-1}
  53. universal_max_size_amp=${UNIVERSAL_MAX_SIZE_AMP:-200}
  54. universal_compression_size_percent=${UNIVERSAL_COMPRESSION_SIZE_PERCENT:-"-1"}
  55. # Integrated BlobDB configuration
  56. min_blob_size=${MIN_BLOB_SIZE:-0}
  57. blob_file_size=${BLOB_FILE_SIZE:-$(( 256 * M ))}
  58. blob_compression_type=${BLOB_COMPRESSION_TYPE:-${compression_type}}
  59. blob_gc_age_cutoff=${BLOB_GC_AGE_CUTOFF:-"0.25"}
  60. blob_gc_force_threshold=${BLOB_GC_FORCE_THRESHOLD:-1}
  61. # Arguments for dynamic loading
  62. base_args=( LD_LIBRARY_PATH="$ld_library_path" )
  63. # Arguments used for all tests
  64. base_args+=( NUM_KEYS="$num_keys" )
  65. base_args+=( NUM_THREADS="$num_threads" )
  66. base_args+=( KEY_SIZE="$key_size" )
  67. base_args+=( VALUE_SIZE="$value_size" )
  68. base_args+=( SUBCOMPACTIONS="$subcompactions" )
  69. base_args+=( COMPRESSION_TYPE="$compression_type" )
  70. base_args+=( WRITE_BUFFER_SIZE_MB="$write_buffer_size_mb" )
  71. base_args+=( TARGET_FILE_SIZE_BASE_MB="$target_file_size_base_mb" )
  72. base_args+=( MAX_BYTES_FOR_LEVEL_BASE_MB="$max_bytes_for_level_base_mb" )
  73. base_args+=( MAX_BACKGROUND_JOBS="$max_background_jobs" )
  74. base_args+=( STATS_INTERVAL_SECONDS="$stats_interval_seconds" )
  75. base_args+=( CACHE_INDEX_AND_FILTER_BLOCKS="$cache_index_and_filter_blocks" )
  76. base_args+=( COMPACTION_STYLE="$compaction_style" )
  77. base_args+=( BYTES_PER_SYNC="$bytes_per_sync" )
  78. if [ -n "$USE_O_DIRECT" ]; then
  79. base_args+=( USE_O_DIRECT=1 )
  80. fi
  81. if [ -n "$NUMA" ]; then
  82. base_args+=( NUMACTL=1 )
  83. fi
  84. if [ -n "$CACHE_SIZE_MB" ]; then
  85. cacheb=$(( CACHE_SIZE_MB * M ))
  86. base_args+=( CACHE_SIZE="$cacheb" )
  87. fi
  88. if [ "$compaction_style" == "leveled" ]; then
  89. base_args+=( LEVEL0_FILE_NUM_COMPACTION_TRIGGER="$level0_file_num_compaction_trigger" )
  90. base_args+=( LEVEL0_SLOWDOWN_WRITES_TRIGGER="$level0_slowdown_writes_trigger" )
  91. base_args+=( LEVEL0_STOP_WRITES_TRIGGER="$level0_stop_writes_trigger" )
  92. base_args+=( PER_LEVEL_FANOUT="$per_level_fanout" )
  93. elif [ "$compaction_style" == "universal" ]; then
  94. base_args+=( LEVEL0_FILE_NUM_COMPACTION_TRIGGER="$level0_file_num_compaction_trigger" )
  95. base_args+=( LEVEL0_SLOWDOWN_WRITES_TRIGGER="$level0_slowdown_writes_trigger" )
  96. base_args+=( LEVEL0_STOP_WRITES_TRIGGER="$level0_stop_writes_trigger" )
  97. base_args+=( UNIVERSAL_MIN_MERGE_WIDTH="$universal_min_merge_width" )
  98. base_args+=( UNIVERSAL_MAX_MERGE_WIDTH="$universal_max_merge_width" )
  99. base_args+=( UNIVERSAL_SIZE_RATIO="$universal_size_ratio" )
  100. base_args+=( UNIVERSAL_MAX_SIZE_AMP="$universal_max_size_amp" )
  101. if [ -n "$UNIVERSAL_ALLOW_TRIVIAL_MOVE" ]; then
  102. base_args+=( UNIVERSAL_ALLOW_TRIVIAL_MOVE=1 )
  103. fi
  104. else
  105. # Inherit settings for leveled because index uses leveled LSM
  106. base_args+=( LEVEL0_FILE_NUM_COMPACTION_TRIGGER="$level0_file_num_compaction_trigger" )
  107. base_args+=( LEVEL0_SLOWDOWN_WRITES_TRIGGER="$level0_slowdown_writes_trigger" )
  108. base_args+=( LEVEL0_STOP_WRITES_TRIGGER="$level0_stop_writes_trigger" )
  109. base_args+=( PER_LEVEL_FANOUT="$per_level_fanout" )
  110. # Then add BlobDB specific settings
  111. base_args+=( MIN_BLOB_SIZE="$min_blob_size" )
  112. base_args+=( BLOB_FILE_SIZE="$blob_file_size" )
  113. base_args+=( BLOB_COMPRESSION_TYPE="$blob_compression_type" )
  114. base_args+=( BLOB_GC_AGE_CUTOFF="$blob_gc_age_cutoff" )
  115. base_args+=( BLOB_GC_FORCE_THRESHOLD="$blob_gc_force_threshold" )
  116. fi
  117. function usage {
  118. echo "usage: benchmark_compare.sh db_dir output_dir version+"
  119. echo -e "\tdb_dir\t\tcreate RocksDB database in this directory"
  120. echo -e "\toutput_dir\twrite output from performance tests in this directory"
  121. echo -e "\tversion+\tspace separated sequence of RocksDB versions to test."
  122. echo -e "\nThis expects that db_bench.\$version exists in \$PWD for each version in the sequence."
  123. echo -e "An example value for version+ is 6.23.0 6.24.0"
  124. echo ""
  125. echo -e "Environment variables for options"
  126. echo -e "\tNUM_KEYS\t\t\tnumber of keys to load"
  127. echo -e "\tKEY_SIZE\t\t\tsize of key"
  128. echo -e "\tVALUE_SIZE\t\t\tsize of value"
  129. echo -e "\tCACHE_SIZE_MB\t\t\tsize of block cache in MB"
  130. echo -e "\tDURATION_RW\t\t\tnumber of seconds for which each test runs, except for read-only tests"
  131. echo -e "\tDURATION_RO\t\t\tnumber of seconds for which each read-only test runs"
  132. echo -e "\tMB_WRITE_PER_SEC\t\trate limit for writer that runs concurrent with queries for some tests"
  133. echo -e "\tNUM_THREADS\t\t\tnumber of user threads"
  134. echo -e "\tCOMPRESSION_TYPE\t\tcompression type (zstd, lz4, none, etc)"
  135. echo -e "\tMIN_LEVEL_TO_COMPRESS\t\tmin_level_to_compress for leveled"
  136. echo -e "\tWRITE_BUFFER_SIZE_MB\t\tsize of write buffer in MB"
  137. echo -e "\tTARGET_FILE_SIZE_BASE_MB\tvalue for target_file_size_base in MB"
  138. echo -e "\tMAX_BYTES_FOR_LEVEL_BASE_MB\tvalue for max_bytes_for_level_base in MB"
  139. echo -e "\tMAX_BACKGROUND_JOBS\t\tvalue for max_background_jobs"
  140. echo -e "\tCACHE_INDEX_AND_FILTER_BLOCKS\tvalue for cache_index_and_filter_blocks"
  141. echo -e "\tUSE_O_DIRECT\t\t\tUse O_DIRECT for user reads and compaction"
  142. echo -e "\tBYTES_PER_SYNC\t\t\tValue for bytes_per_sync"
  143. echo -e "\tSTATS_INTERVAL_SECONDS\t\tvalue for stats_interval_seconds"
  144. echo -e "\tSUBCOMPACTIONS\t\t\tvalue for subcompactions"
  145. echo -e "\tCOMPACTION_STYLE\t\tCompaction style to use, one of: leveled, universal, blob"
  146. echo -e "\tCI_TESTS_ONLY\t\tRun a subset of tests tailored to a CI regression job, one of: true, false (default)"
  147. echo ""
  148. echo -e "\tOptions specific to leveled compaction:"
  149. echo -e "\t\tLEVEL0_FILE_NUM_COMPACTION_TRIGGER\tvalue for level0_file_num_compaction_trigger"
  150. echo -e "\t\tLEVEL0_SLOWDOWN_WRITES_TRIGGER\t\tvalue for level0_slowdown_writes_trigger"
  151. echo -e "\t\tLEVEL0_STOP_WRITES_TRIGGER\t\tvalue for level0_stop_writes_trigger"
  152. echo -e "\t\tPER_LEVEL_FANOUT\t\t\tvalue for max_bytes_for_level_multiplier"
  153. echo ""
  154. echo -e "\tOptions specific to universal compaction:"
  155. echo -e "\t\tSee LEVEL0_*_TRIGGER above"
  156. echo -e "\t\tUNIVERSAL_MIN_MERGE_WIDTH\t\tvalue of min_merge_width option for universal"
  157. echo -e "\t\tUNIVERSAL_MAX_MERGE_WIDTH\t\tvalue of min_merge_width option for universal"
  158. echo -e "\t\tUNIVERSAL_SIZE_RATIO\t\t\tvalue of size_ratio option for universal"
  159. echo -e "\t\tUNIVERSAL_MAX_SIZE_AMP\t\t\tmax_size_amplification_percent for universal"
  160. echo -e "\t\tUNIVERSAL_ALLOW_TRIVIAL_MOVE\t\tSet allow_trivial_move to true for universal, default is false"
  161. echo -e "\t\tUNIVERSAL_COMPRESSION_SIZE_PERCENT\tpercentage of LSM tree that should be compressed"
  162. echo ""
  163. echo -e "\tOptions for integrated BlobDB:"
  164. echo -e "\t\tMIN_BLOB_SIZE\t\t\t\tvalue for min_blob_size"
  165. echo -e "\t\tBLOB_FILE_SIZE\t\t\t\tvalue for blob_file_size"
  166. echo -e "\t\tBLOB_COMPRESSION_TYPE\t\t\tvalue for blob_compression_type"
  167. echo -e "\t\tBLOB_GC_AGE_CUTOFF\t\t\tvalue for blog_garbage_collection_age_cutoff"
  168. echo -e "\t\tBLOB_GC_FORCE_THRESHOLD\t\t\tvalue for blog_garbage_collection_force_threshold"
  169. }
  170. function dump_env {
  171. echo "Base args" > "$odir"/args
  172. echo "${base_args[@]}" | tr ' ' '\n' >> "$odir"/args
  173. echo -e "\nOther args" >> "$odir"/args
  174. echo -e "dbdir\t$dbdir" >> "$odir"/args
  175. echo -e "duration_rw\t$duration_rw" >> "$odir"/args
  176. echo -e "duration_ro\t$duration_ro" >> "$odir"/args
  177. echo -e "per_level_fanout\t$per_level_fanout" >> "$odir"/args
  178. echo -e "\nargs_load:" >> "$odir"/args
  179. echo "${args_load[@]}" | tr ' ' '\n' >> "$odir"/args
  180. echo -e "\nargs_nolim:" >> "$odir"/args
  181. echo "${args_nolim[@]}" | tr ' ' '\n' >> "$odir"/args
  182. echo -e "\nargs_lim:" >> "$odir"/args
  183. echo "${args_lim[@]}" | tr ' ' '\n' >> "$odir"/args
  184. }
  185. if [ $# -lt 3 ]; then
  186. usage
  187. echo
  188. echo "Need at least 3 arguments"
  189. exit 1
  190. fi
  191. shift 2
  192. mkdir -p "$odir"
  193. echo Test versions: "$@"
  194. echo Test versions: "$@" >> "$odir"/args
  195. for v in "$@" ; do
  196. my_odir="$odir"/"$v"
  197. if [ -d "$my_odir" ]; then
  198. echo Exiting because the output directory exists: "$my_odir"
  199. exit 1
  200. fi
  201. args_common=("${base_args[@]}")
  202. args_common+=( OUTPUT_DIR="$my_odir" DB_DIR="$dbdir" WAL_DIR="$dbdir" DB_BENCH_NO_SYNC=1 )
  203. if [ "$compaction_style" == "leveled" ]; then
  204. args_common+=( MIN_LEVEL_TO_COMPRESS="$min_level_to_compress" )
  205. elif [ "$compaction_style" == "universal" ]; then
  206. args_common+=( UNIVERSAL=1 COMPRESSION_SIZE_PERCENT="$universal_compression_size_percent" )
  207. else
  208. args_common+=( MIN_LEVEL_TO_COMPRESS="$min_level_to_compress" )
  209. fi
  210. args_load=("${args_common[@]}")
  211. args_nolim=("${args_common[@]}")
  212. args_lim=("${args_nolim[@]}")
  213. args_lim+=( MB_WRITE_PER_SEC="$mb_write_per_sec" )
  214. dump_env
  215. echo Run benchmark for "$v" at "$( date )" with results at "$my_odir"
  216. rm -f db_bench
  217. echo ln -s db_bench."$v" db_bench
  218. ln -s db_bench."$v" db_bench
  219. find "$dbdir" -type f -exec rm \{\} \;
  220. # Load in key order
  221. echo env "${args_load[@]}" bash ./benchmark.sh fillseq_disable_wal
  222. env -i "${args_load[@]}" bash ./benchmark.sh fillseq_disable_wal
  223. # Read-only tests. The LSM tree shape is in a deterministic state if trivial move
  224. # was used during the load.
  225. # Add revrange with a fixed duration and hardwired number of keys and threads to give
  226. # compaction debt leftover from fillseq a chance at being removed. Not using waitforcompaction
  227. # here because it isn't supported on older db_bench versions.
  228. env -i "${args_nolim[@]}" DURATION=300 NUM_KEYS=100 NUM_THREADS=1 bash ./benchmark.sh revrange
  229. env -i "${args_nolim[@]}" DURATION="$duration_ro" bash ./benchmark.sh readrandom
  230. # Skipped for CI - a single essentail readrandom is enough to set up for other tests
  231. if [ "$ci_tests_only" != "true" ]; then
  232. env -i "${args_nolim[@]}" DURATION="$duration_ro" bash ./benchmark.sh fwdrange
  233. env -i "${args_lim[@]}" DURATION="$duration_ro" bash ./benchmark.sh multireadrandom --multiread_batched
  234. else
  235. echo "CI_TESTS_ONLY is set, skipping optional read steps."
  236. fi
  237. # Write 10% of the keys. The goal is to randomize keys prior to Lmax
  238. p10=$( echo "$num_keys" "$num_threads" | awk '{ printf "%.0f", $1 / $2 / 10.0 }' )
  239. env -i "${args_nolim[@]}" WRITES="$p10" bash ./benchmark.sh overwritesome
  240. if [ "$compaction_style" == "leveled" ]; then
  241. # These are not supported by older versions
  242. # Flush memtable & L0 to get LSM tree into deterministic state
  243. env -i "${args_nolim[@]}" bash ./benchmark.sh flush_mt_l0
  244. elif [ "$compaction_style" == "universal" ]; then
  245. # For universal don't compact L0 as can have too many sorted runs
  246. # waitforcompaction can hang, see https://github.com/facebook/rocksdb/issues/9275
  247. # While this is disabled the test that follows will have more variance from compaction debt.
  248. # env -i "${args_nolim[@]}" bash ./benchmark.sh waitforcompaction
  249. echo TODO enable when waitforcompaction hang is fixed
  250. else
  251. # These are not supported by older versions
  252. # Flush memtable & L0 to get LSM tree into deterministic state
  253. env -i "${args_nolim[@]}" bash ./benchmark.sh flush_mt_l0
  254. fi
  255. # Read-mostly tests with a rate-limited writer
  256. env -i "${args_lim[@]}" DURATION="$duration_rw" bash ./benchmark.sh revrangewhilewriting
  257. env -i "${args_lim[@]}" DURATION="$duration_rw" bash ./benchmark.sh fwdrangewhilewriting
  258. env -i "${args_lim[@]}" DURATION="$duration_rw" bash ./benchmark.sh readwhilewriting
  259. # Write-only tests
  260. # This creates much compaction debt which will be a problem for tests added after it.
  261. # Also, the compaction stats measured at test end can underestimate write-amp depending
  262. # on how much compaction debt is allowed.
  263. if [ "$compaction_style" == "leveled" ] && ./db_bench --benchmarks=waitforcompaction ; then
  264. # Use waitforcompaction to get more accurate write-amp measurement
  265. env -i "${args_nolim[@]}" DURATION="$duration_rw" bash ./benchmark.sh overwriteandwait
  266. else
  267. # waitforcompaction hangs with universal, see https://github.com/facebook/rocksdb/issues/9275
  268. env -i "${args_nolim[@]}" DURATION="$duration_rw" bash ./benchmark.sh overwrite
  269. fi
  270. cp "$dbdir"/LOG* "$my_odir"
  271. gzip -9 "$my_odir"/LOG*
  272. done
  273. # Generate a file that groups lines from the same test for all versions
  274. basev=$1
  275. nlines=$( awk '/^ops_sec/,/END/' "$odir"/"$basev"/report.tsv | grep -v ops_sec | wc -l )
  276. hline=$( awk '/^ops_sec/ { print NR }' "$odir"/"$basev"/report.tsv )
  277. sline=$(( hline + 1 ))
  278. eline=$(( sline + nlines - 1 ))
  279. sum_file="$odir"/summary.tsv
  280. for v in "$@" ; do
  281. echo "$odir"/"$v"/report.tsv
  282. done >> "$sum_file"
  283. echo >> "$sum_file"
  284. for x in $( seq "$sline" "$eline" ); do
  285. awk '{ if (NR == lno) { print $0 } }' lno="$hline" "$odir"/"$basev"/report.tsv >> "$sum_file"
  286. for v in "$@" ; do
  287. r="$odir"/"$v"/report.tsv
  288. awk '{ if (NR == lno) { print $0 } }' lno="$x" "$r" >> "$sum_file"
  289. done
  290. echo >> "$sum_file"
  291. done