From a01159003d733cb241979fab029f0759ae2db50d Mon Sep 17 00:00:00 2001 From: Renan Magagnin Date: Wed, 8 Jan 2025 10:17:00 +0000 Subject: [PATCH 1/5] Create file-system-benchmarks.rs and update fs_latency_bench.sh accordingly Signed-off-by: Renan Magagnin --- Cargo.lock | 288 +++++++++++++++--- mountpoint-s3/Cargo.toml | 7 + mountpoint-s3/scripts/fs_latency_bench.sh | 26 +- .../src/bin/file-system-benchmarks.rs | 172 +++++++++++ 4 files changed, 455 insertions(+), 38 deletions(-) create mode 100644 mountpoint-s3/src/bin/file-system-benchmarks.rs diff --git a/Cargo.lock b/Cargo.lock index 947a0271e..5102a07da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -385,6 +385,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -843,7 +852,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -870,6 +879,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -1077,6 +1092,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "cmake" version = "0.1.52" @@ -1244,7 +1268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1255,7 +1279,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1409,7 +1433,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -1502,7 +1526,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1554,6 +1578,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "2.0.0" @@ -1728,7 +1758,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags", + "bitflags 2.6.0", "libc", "libgit2-sys", "log", @@ -1760,7 +1790,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags", + "bitflags 2.6.0", "ignore", "walkdir", ] @@ -1784,7 +1814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2380,7 +2410,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags", + "bitflags 2.6.0", "libc", "redox_syscall", ] @@ -2421,7 +2451,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg", + "autocfg 1.4.0", "scopeguard", ] @@ -2520,7 +2550,7 @@ dependencies = [ "aws-sdk-s3", "base64ct", "bincode", - "bitflags", + "bitflags 2.6.0", "built", "bytes", "clap", @@ -2547,14 +2577,15 @@ dependencies = [ "procfs", "proptest", "proptest-derive", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "regex", "serde", "serde_json", "serial_test", "sha2", "shuttle", + "statistical", "supports-color 3.0.2", "sysinfo", "syslog", @@ -2599,8 +2630,8 @@ dependencies = [ "pin-project", "platform-info", "proptest", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "regex", "rusty-fork", "serde_json", @@ -2627,7 +2658,7 @@ dependencies = [ "libc", "log", "mountpoint-s3-crt-sys", - "rand", + "rand 0.8.5", "serde_json", "smallstr", "static_assertions", @@ -2661,7 +2692,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", @@ -2702,6 +2733,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.4.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg 1.4.0", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2717,13 +2783,36 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg 1.4.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg 1.4.0", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg", + "autocfg 1.4.0", "libm", ] @@ -3066,7 +3155,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ - "bitflags", + "bitflags 2.6.0", "hex", "procfs-core", "rustix", @@ -3078,7 +3167,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" dependencies = [ - "bitflags", + "bitflags 2.6.0", "hex", ] @@ -3090,12 +3179,12 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags", + "bitflags 2.6.0", "lazy_static", "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift 0.3.0", "regex-syntax 0.8.5", "rusty-fork", "tempfile", @@ -3134,6 +3223,25 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg 0.1.2", + "rand_xorshift 0.1.1", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -3141,8 +3249,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", ] [[package]] @@ -3152,9 +3270,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -3164,13 +3297,75 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + [[package]] name = "rand_pcg" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" dependencies = [ - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", ] [[package]] @@ -3179,7 +3374,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3202,13 +3397,22 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -3331,7 +3535,7 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -3480,7 +3684,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -3619,9 +3823,9 @@ dependencies = [ "generator", "hex", "owo-colors 3.5.0", - "rand", - "rand_core", - "rand_pcg", + "rand 0.8.5", + "rand_core 0.6.4", + "rand_pcg 0.3.1", "scoped-tls", "smallvec", "tracing", @@ -3643,7 +3847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3664,7 +3868,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] [[package]] @@ -3720,6 +3924,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "statistical" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49d57902bb128e5e38b5218d3681215ae3e322d99f65d5420e9849730d2ea372" +dependencies = [ + "num", + "rand 0.6.5", +] + [[package]] name = "string_cache" version = "0.8.7" diff --git a/mountpoint-s3/Cargo.toml b/mountpoint-s3/Cargo.toml index 40686aa85..e030dfbf3 100644 --- a/mountpoint-s3/Cargo.toml +++ b/mountpoint-s3/Cargo.toml @@ -48,6 +48,8 @@ tracing = { version = "0.1.41", features = ["log"] } tracing-log = "0.2.0" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } +statistical = { version = "1.0", optional = true } + [target.'cfg(target_os = "linux")'.dependencies] procfs = { version = "0.17.0", default-features = false } @@ -105,3 +107,8 @@ required-features = ["mock"] [[bin]] name = "mount-s3-log-analyzer" path = "src/bin/mount-s3-log-analyzer.rs" + +[[bin]] +name = "file-system-benchmarks" +path = "src/bin/file-system-benchmarks.rs" +required-features = ["statistical"] diff --git a/mountpoint-s3/scripts/fs_latency_bench.sh b/mountpoint-s3/scripts/fs_latency_bench.sh index c5803509d..6514cd695 100755 --- a/mountpoint-s3/scripts/fs_latency_bench.sh +++ b/mountpoint-s3/scripts/fs_latency_bench.sh @@ -126,6 +126,29 @@ do dir_size=$(awk "BEGIN {print $dir_size*10}") done +run_file_system_benchmarks() { + mount_dir=$(mktemp -d /tmp/fio-XXXXXXXXXXXX) + log_dir=logs/file_system_benchmarks + + # mount file system + cargo run --release ${S3_BUCKET_NAME} ${mount_dir} \ + --allow-delete \ + --allow-overwrite \ + --log-directory=$log_dir \ + --prefix=${S3_BUCKET_TEST_PREFIX} \ + --log-metrics \ + ${optional_args} + mount_status=$? + if [ $mount_status -ne 0 ]; then + echo "Failed to mount file system" + exit 1 + fi + + # run file system benchmarks binary + file_system_benchmarks_out_file_path=${results_dir}/file_system_benchmarks_parsed.json + cargo run --release --features=statistical --bin file-system-benchmarks ${mount_dir} ${file_system_benchmarks_out_file_path} # This generates a *_parsed.json file which will be included in the resulting output json +} + run_file_benchmarks() { category=$1 jobs_dir=mountpoint-s3/scripts/fio/${category}_latency @@ -188,8 +211,9 @@ run_file_benchmarks() { done } +run_file_system_benchmarks run_file_benchmarks read run_file_benchmarks write # combine all bench results into one json file -jq -n '[inputs]' ${results_dir}/*.json | tee ${results_dir}/output.json +jq -n '[inputs] | flatten' ${results_dir}/*.json | tee ${results_dir}/output.json diff --git a/mountpoint-s3/src/bin/file-system-benchmarks.rs b/mountpoint-s3/src/bin/file-system-benchmarks.rs new file mode 100644 index 000000000..748b5a27e --- /dev/null +++ b/mountpoint-s3/src/bin/file-system-benchmarks.rs @@ -0,0 +1,172 @@ +use anyhow::{anyhow, Result}; +use clap::{Parser, ValueEnum}; +use serde::Serialize; +use serde_json::json; +use statistical::mean; +use std::{ + fs::{self, File, OpenOptions}, + io::{BufWriter, Write}, + path::PathBuf, + time::Instant, +}; + +#[derive(Parser, Debug, Clone, ValueEnum)] +enum BenchmarkType { + OneByteFile, + All, +} + +#[derive(Parser, Debug)] +struct CliArgs { + #[clap(help = "Directory of mounted S3 bucket", value_name = "MOUNT_DIRECTORY")] + mount_dir: PathBuf, + + #[clap(help = "Output JSON file name", value_name = "OUTPUT_FILE")] + out_file: PathBuf, + + #[clap(value_enum, short, long, help = "Type of benchmark to run", default_value = "all")] + benchmark_type: BenchmarkType, + + #[clap(long, help = "Include detailed breakdown of operations", default_value = "false")] + detailed: bool, +} + +#[derive(Serialize)] +struct BenchmarkResult { + name: String, + value: f64, + unit: String, +} + +fn one_byte_file_creation_benchmark( + mount_dir_str: &str, + num_files: u32, + include_breakdown: bool, +) -> Result> { + file_creation_benchmark(mount_dir_str, num_files, include_breakdown, "One Byte File Creation", 1) +} + +fn file_creation_benchmark( + mount_dir_str: &str, + num_files: u32, + include_breakdown: bool, + benchmark_name: &str, + file_size: u64, // In bytes +) -> Result> { + const NANOS_PER_MILLI: f64 = 1_000_000.0; + + let mut lookup_latency_samples = vec![]; + let mut open_latency_samples = vec![]; + let mut write_latency_samples = vec![]; + let mut flush_latency_samples = vec![]; + let mut total_latency_samples = vec![]; + + for file_number in 1..=num_files { + let mut elapsed_total_ms: f64 = 0.0; + let path = format!("{mount_dir_str}/bench_file_{file_number}"); + + // Perform and time the lookup operation + let start = Instant::now(); + let _ = fs::metadata(path.clone()); + let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; + lookup_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + + // Perform and time the open operation + let mut open = OpenOptions::new(); + open.create(true); + open.truncate(true); + open.write(true); + open.read(true); + let start = Instant::now(); + let mut file = open + .open(path.clone()) + .map_err(|e| anyhow::anyhow!("Failed to open file {}: {}", path, e))?; + let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; + open_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + + // Perform and time the writing operation + let start = Instant::now(); + file.write_all(&vec![0u8; file_size as usize]) + .map_err(|e| anyhow::anyhow!("Failed to write to file {}: {}", path, e))?; + let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; + write_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + + // Perform and time the flush operation + let start = Instant::now(); + drop(file); + let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; + flush_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + + total_latency_samples.push(elapsed_total_ms); + + fs::remove_file(path.clone()).map_err(|e| anyhow::anyhow!("Failed to remove file {}: {}", path, e))?; + } + + let total_latency_result = BenchmarkResult { + name: format!("{benchmark_name} - Average Total Latency"), + value: mean(&total_latency_samples), + unit: "milliseconds".to_string(), + }; + + if !include_breakdown { + Ok(vec![total_latency_result]) + } else { + Ok(vec![ + total_latency_result, + BenchmarkResult { + name: format!("{benchmark_name} - Average Lookup Latency"), + value: mean(&lookup_latency_samples), + unit: "milliseconds".to_string(), + }, + BenchmarkResult { + name: format!("{benchmark_name} - Average Open Latency"), + value: mean(&open_latency_samples), + unit: "milliseconds".to_string(), + }, + BenchmarkResult { + name: format!("{benchmark_name} - Average Write Latency"), + value: mean(&write_latency_samples), + unit: "milliseconds".to_string(), + }, + BenchmarkResult { + name: format!("{benchmark_name} - Average Flush Latency"), + value: mean(&flush_latency_samples), + unit: "milliseconds".to_string(), + }, + ]) + } +} + +fn main() -> Result<()> { + let CliArgs { + mount_dir, + out_file, + benchmark_type, + detailed, + } = CliArgs::parse(); + let mount_dir_os_str = mount_dir.into_os_string(); + let mount_dir_str = mount_dir_os_str + .to_str() + .ok_or_else(|| anyhow!("Invalid UTF-8 in mount directory path"))?; + + const NUM_FILES: u32 = 100; + let benchmark_results = match benchmark_type { + BenchmarkType::OneByteFile => one_byte_file_creation_benchmark(mount_dir_str, NUM_FILES, detailed)?, + BenchmarkType::All => vec![one_byte_file_creation_benchmark(mount_dir_str, NUM_FILES, detailed)?] + .into_iter() + .flatten() + .collect(), + }; + + let contents = json!(benchmark_results); + let file = File::create(out_file)?; + let mut writer = BufWriter::new(file); + serde_json::to_writer_pretty(&mut writer, &contents)?; + writer.flush()?; + + Ok(()) +} From d61c1e43282c4c735e1fb0ae282117c259e90100 Mon Sep 17 00:00:00 2001 From: Renan Magagnin Date: Mon, 13 Jan 2025 16:11:45 +0000 Subject: [PATCH 2/5] Incorporate feedback from review Signed-off-by: Renan Magagnin --- Cargo.lock | 288 +++--------------- mountpoint-s3/Cargo.toml | 3 - mountpoint-s3/scripts/fs_latency_bench.sh | 9 +- .../src/bin/file-system-benchmarks.rs | 155 ++++++---- 4 files changed, 143 insertions(+), 312 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5102a07da..947a0271e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -385,15 +385,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.4.0", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -852,7 +843,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", @@ -879,12 +870,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -1092,15 +1077,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "cmake" version = "0.1.52" @@ -1268,7 +1244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -1279,7 +1255,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1433,7 +1409,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -1526,7 +1502,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1578,12 +1554,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "funty" version = "2.0.0" @@ -1758,7 +1728,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.6.0", + "bitflags", "libc", "libgit2-sys", "log", @@ -1790,7 +1760,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.6.0", + "bitflags", "ignore", "walkdir", ] @@ -1814,7 +1784,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -2410,7 +2380,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags", "libc", "redox_syscall", ] @@ -2451,7 +2421,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.4.0", + "autocfg", "scopeguard", ] @@ -2550,7 +2520,7 @@ dependencies = [ "aws-sdk-s3", "base64ct", "bincode", - "bitflags 2.6.0", + "bitflags", "built", "bytes", "clap", @@ -2577,15 +2547,14 @@ dependencies = [ "procfs", "proptest", "proptest-derive", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", "regex", "serde", "serde_json", "serial_test", "sha2", "shuttle", - "statistical", "supports-color 3.0.2", "sysinfo", "syslog", @@ -2630,8 +2599,8 @@ dependencies = [ "pin-project", "platform-info", "proptest", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", "regex", "rusty-fork", "serde_json", @@ -2658,7 +2627,7 @@ dependencies = [ "libc", "log", "mountpoint-s3-crt-sys", - "rand 0.8.5", + "rand", "serde_json", "smallstr", "static_assertions", @@ -2692,7 +2661,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -2733,41 +2702,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg 1.4.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg 1.4.0", - "num-traits", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -2783,36 +2717,13 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg 1.4.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg 1.4.0", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.4.0", + "autocfg", "libm", ] @@ -3155,7 +3066,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ - "bitflags 2.6.0", + "bitflags", "hex", "procfs-core", "rustix", @@ -3167,7 +3078,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" dependencies = [ - "bitflags 2.6.0", + "bitflags", "hex", ] @@ -3179,12 +3090,12 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags", "lazy_static", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_xorshift 0.3.0", + "rand", + "rand_chacha", + "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", "tempfile", @@ -3223,25 +3134,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg 0.1.2", - "rand_xorshift 0.1.1", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -3249,18 +3141,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -3270,24 +3152,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -3297,75 +3164,13 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - [[package]] name = "rand_pcg" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", + "rand_core", ] [[package]] @@ -3374,7 +3179,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -3397,22 +3202,13 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -3535,7 +3331,7 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -3684,7 +3480,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3823,9 +3619,9 @@ dependencies = [ "generator", "hex", "owo-colors 3.5.0", - "rand 0.8.5", - "rand_core 0.6.4", - "rand_pcg 0.3.1", + "rand", + "rand_core", + "rand_pcg", "scoped-tls", "smallvec", "tracing", @@ -3847,7 +3643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ "digest", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -3868,7 +3664,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg 1.4.0", + "autocfg", ] [[package]] @@ -3924,16 +3720,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "statistical" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49d57902bb128e5e38b5218d3681215ae3e322d99f65d5420e9849730d2ea372" -dependencies = [ - "num", - "rand 0.6.5", -] - [[package]] name = "string_cache" version = "0.8.7" diff --git a/mountpoint-s3/Cargo.toml b/mountpoint-s3/Cargo.toml index e030dfbf3..cf84d4e87 100644 --- a/mountpoint-s3/Cargo.toml +++ b/mountpoint-s3/Cargo.toml @@ -48,8 +48,6 @@ tracing = { version = "0.1.41", features = ["log"] } tracing-log = "0.2.0" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -statistical = { version = "1.0", optional = true } - [target.'cfg(target_os = "linux")'.dependencies] procfs = { version = "0.17.0", default-features = false } @@ -111,4 +109,3 @@ path = "src/bin/mount-s3-log-analyzer.rs" [[bin]] name = "file-system-benchmarks" path = "src/bin/file-system-benchmarks.rs" -required-features = ["statistical"] diff --git a/mountpoint-s3/scripts/fs_latency_bench.sh b/mountpoint-s3/scripts/fs_latency_bench.sh index 6514cd695..2e23c1397 100755 --- a/mountpoint-s3/scripts/fs_latency_bench.sh +++ b/mountpoint-s3/scripts/fs_latency_bench.sh @@ -126,6 +126,7 @@ do dir_size=$(awk "BEGIN {print $dir_size*10}") done +# Run benchmarks which measure the latencies of real-world usage patterns of Mountpoint. These include file system operations instead of focusing solely on IO data transfer. run_file_system_benchmarks() { mount_dir=$(mktemp -d /tmp/fio-XXXXXXXXXXXX) log_dir=logs/file_system_benchmarks @@ -146,10 +147,10 @@ run_file_system_benchmarks() { # run file system benchmarks binary file_system_benchmarks_out_file_path=${results_dir}/file_system_benchmarks_parsed.json - cargo run --release --features=statistical --bin file-system-benchmarks ${mount_dir} ${file_system_benchmarks_out_file_path} # This generates a *_parsed.json file which will be included in the resulting output json + cargo run --release --bin file-system-benchmarks ${mount_dir} ${file_system_benchmarks_out_file_path} # This generates a *_parsed.json file which will be included in the resulting output json } -run_file_benchmarks() { +run_file_io_benchmarks() { category=$1 jobs_dir=mountpoint-s3/scripts/fio/${category}_latency @@ -212,8 +213,8 @@ run_file_benchmarks() { } run_file_system_benchmarks -run_file_benchmarks read -run_file_benchmarks write +run_file_io_benchmarks read +run_file_io_benchmarks write # combine all bench results into one json file jq -n '[inputs] | flatten' ${results_dir}/*.json | tee ${results_dir}/output.json diff --git a/mountpoint-s3/src/bin/file-system-benchmarks.rs b/mountpoint-s3/src/bin/file-system-benchmarks.rs index 748b5a27e..1b52ac764 100644 --- a/mountpoint-s3/src/bin/file-system-benchmarks.rs +++ b/mountpoint-s3/src/bin/file-system-benchmarks.rs @@ -1,14 +1,13 @@ use anyhow::{anyhow, Result}; use clap::{Parser, ValueEnum}; -use serde::Serialize; +use serde::{Serialize, Serializer}; use serde_json::json; -use statistical::mean; -use std::{ - fs::{self, File, OpenOptions}, - io::{BufWriter, Write}, - path::PathBuf, - time::Instant, -}; + +use std::fs::{self, File, OpenOptions}; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; +use std::time::Duration; +use std::time::Instant; #[derive(Parser, Debug, Clone, ValueEnum)] enum BenchmarkType { @@ -16,6 +15,7 @@ enum BenchmarkType { All, } +/// Benchmark tool for measuring the time of Linux file system operations. #[derive(Parser, Debug)] struct CliArgs { #[clap(help = "Directory of mounted S3 bucket", value_name = "MOUNT_DIRECTORY")] @@ -35,7 +35,39 @@ struct CliArgs { struct BenchmarkResult { name: String, value: f64, - unit: String, + unit: Unit, +} + +enum Unit { + Milliseconds, +} + +impl Serialize for Unit { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Unit::Milliseconds => serializer.serialize_str("milliseconds"), + } + } +} + +trait DurationExt { + // This is a temporary alternative to 'as_millis' to avoid loss of precision and should be removed when 'as_millis_f64_temp' is no longer a nightly-only experimental API. + fn as_millis_f64_temp(&self) -> f64; +} + +impl DurationExt for Duration { + fn as_millis_f64_temp(&self) -> f64 { + const NANOS_PER_MILLI: f64 = 1_000_000.0; + return self.as_nanos() as f64 / NANOS_PER_MILLI; + } +} + +fn mean(v: &[f64]) -> f64 { + let len = v.len() as f64; + v.iter().fold(0.0, |acc: f64, elem| acc + *elem) / len } fn one_byte_file_creation_benchmark( @@ -43,19 +75,38 @@ fn one_byte_file_creation_benchmark( num_files: u32, include_breakdown: bool, ) -> Result> { - file_creation_benchmark(mount_dir_str, num_files, include_breakdown, "One Byte File Creation", 1) + file_creation_benchmark("One Byte File Creation", mount_dir_str, num_files, 1, include_breakdown) } +/// Benchmarks file creation operations by measuring the latency of opening, writing, and flushing files. +/// +/// # Arguments +/// +/// * `benchmark_name` - A string slice containing the name of the benchmark for result labeling +/// * `mount_dir_str` - A string slice containing the path to the directory where benchmark files will be created +/// * `num_files` - The number of files to create during the benchmark (the creation of these files is done in serial) +/// * `file_len_bytes` - The size of each file to create in bytes +/// * `include_breakdown` - Whether to include detailed timing breakdown in the results +/// +/// # Returns +/// +/// Returns a `Result>` where: +/// * On success: Vector of `BenchmarkResult` containing timing measurements +/// * On error: An `anyhow::Error` describing what went wrong +/// +/// # Measurements +/// +/// The function measures three distinct operations for each file: +/// * Open latency: Time taken to create and open a new file +/// * Write latency: Time taken to write the specified number of bytes +/// * Flush latency: Time taken to flush and close the file fn file_creation_benchmark( + benchmark_name: &str, mount_dir_str: &str, num_files: u32, + file_size_bytes: u64, include_breakdown: bool, - benchmark_name: &str, - file_size: u64, // In bytes ) -> Result> { - const NANOS_PER_MILLI: f64 = 1_000_000.0; - - let mut lookup_latency_samples = vec![]; let mut open_latency_samples = vec![]; let mut write_latency_samples = vec![]; let mut flush_latency_samples = vec![]; @@ -65,41 +116,42 @@ fn file_creation_benchmark( let mut elapsed_total_ms: f64 = 0.0; let path = format!("{mount_dir_str}/bench_file_{file_number}"); - // Perform and time the lookup operation - let start = Instant::now(); - let _ = fs::metadata(path.clone()); - let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; - lookup_latency_samples.push(elapsed_ms); - elapsed_total_ms += elapsed_ms; - // Perform and time the open operation - let mut open = OpenOptions::new(); - open.create(true); - open.truncate(true); - open.write(true); - open.read(true); - let start = Instant::now(); - let mut file = open - .open(path.clone()) - .map_err(|e| anyhow::anyhow!("Failed to open file {}: {}", path, e))?; - let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; - open_latency_samples.push(elapsed_ms); - elapsed_total_ms += elapsed_ms; + let mut file = { + let mut open = OpenOptions::new(); + open.create(true); + open.truncate(true); + open.write(true); + open.read(true); + + let start = Instant::now(); + let file = open + .open(path.clone()) + .map_err(|e| anyhow::anyhow!("Failed to open file {}: {}", path, e))?; + let elapsed_ms = start.elapsed().as_millis_f64_temp(); + open_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + file + }; // Perform and time the writing operation - let start = Instant::now(); - file.write_all(&vec![0u8; file_size as usize]) - .map_err(|e| anyhow::anyhow!("Failed to write to file {}: {}", path, e))?; - let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; - write_latency_samples.push(elapsed_ms); - elapsed_total_ms += elapsed_ms; + { + let start = Instant::now(); + file.write_all(&vec![0u8; file_size_bytes as usize]) + .map_err(|e| anyhow::anyhow!("Failed to write to file {}: {}", path, e))?; + let elapsed_ms = start.elapsed().as_millis_f64_temp(); + write_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + }; // Perform and time the flush operation - let start = Instant::now(); - drop(file); - let elapsed_ms = start.elapsed().as_nanos() as f64 / NANOS_PER_MILLI; - flush_latency_samples.push(elapsed_ms); - elapsed_total_ms += elapsed_ms; + { + let start = Instant::now(); + drop(file); + let elapsed_ms = start.elapsed().as_millis_f64_temp(); + flush_latency_samples.push(elapsed_ms); + elapsed_total_ms += elapsed_ms; + }; total_latency_samples.push(elapsed_total_ms); @@ -109,7 +161,7 @@ fn file_creation_benchmark( let total_latency_result = BenchmarkResult { name: format!("{benchmark_name} - Average Total Latency"), value: mean(&total_latency_samples), - unit: "milliseconds".to_string(), + unit: Unit::Milliseconds, }; if !include_breakdown { @@ -117,25 +169,20 @@ fn file_creation_benchmark( } else { Ok(vec![ total_latency_result, - BenchmarkResult { - name: format!("{benchmark_name} - Average Lookup Latency"), - value: mean(&lookup_latency_samples), - unit: "milliseconds".to_string(), - }, BenchmarkResult { name: format!("{benchmark_name} - Average Open Latency"), value: mean(&open_latency_samples), - unit: "milliseconds".to_string(), + unit: Unit::Milliseconds, }, BenchmarkResult { name: format!("{benchmark_name} - Average Write Latency"), value: mean(&write_latency_samples), - unit: "milliseconds".to_string(), + unit: Unit::Milliseconds, }, BenchmarkResult { name: format!("{benchmark_name} - Average Flush Latency"), value: mean(&flush_latency_samples), - unit: "milliseconds".to_string(), + unit: Unit::Milliseconds, }, ]) } From bbe3456648b968bf5a9f23fad4d07e586a0593a3 Mon Sep 17 00:00:00 2001 From: Renan Magagnin Date: Mon, 13 Jan 2025 16:22:15 +0000 Subject: [PATCH 3/5] Add clippy suggestion Signed-off-by: Renan Magagnin --- mountpoint-s3/src/bin/file-system-benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mountpoint-s3/src/bin/file-system-benchmarks.rs b/mountpoint-s3/src/bin/file-system-benchmarks.rs index 1b52ac764..18d81950c 100644 --- a/mountpoint-s3/src/bin/file-system-benchmarks.rs +++ b/mountpoint-s3/src/bin/file-system-benchmarks.rs @@ -61,7 +61,7 @@ trait DurationExt { impl DurationExt for Duration { fn as_millis_f64_temp(&self) -> f64 { const NANOS_PER_MILLI: f64 = 1_000_000.0; - return self.as_nanos() as f64 / NANOS_PER_MILLI; + self.as_nanos() as f64 / NANOS_PER_MILLI } } From b24cd4d438605da6dbbcb10544f0043e573000ad Mon Sep 17 00:00:00 2001 From: Renan Magagnin Date: Mon, 13 Jan 2025 18:32:39 +0000 Subject: [PATCH 4/5] Improve file path handling and add tests to mean() Signed-off-by: Renan Magagnin --- .../src/bin/file-system-benchmarks.rs | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/mountpoint-s3/src/bin/file-system-benchmarks.rs b/mountpoint-s3/src/bin/file-system-benchmarks.rs index 18d81950c..5590fa966 100644 --- a/mountpoint-s3/src/bin/file-system-benchmarks.rs +++ b/mountpoint-s3/src/bin/file-system-benchmarks.rs @@ -1,11 +1,11 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use clap::{Parser, ValueEnum}; use serde::{Serialize, Serializer}; use serde_json::json; use std::fs::{self, File, OpenOptions}; use std::io::{BufWriter, Write}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::time::Duration; use std::time::Instant; @@ -71,11 +71,11 @@ fn mean(v: &[f64]) -> f64 { } fn one_byte_file_creation_benchmark( - mount_dir_str: &str, + mount_dir: &Path, num_files: u32, include_breakdown: bool, ) -> Result> { - file_creation_benchmark("One Byte File Creation", mount_dir_str, num_files, 1, include_breakdown) + file_creation_benchmark("One Byte File Creation", mount_dir, num_files, 1, include_breakdown) } /// Benchmarks file creation operations by measuring the latency of opening, writing, and flushing files. @@ -102,7 +102,7 @@ fn one_byte_file_creation_benchmark( /// * Flush latency: Time taken to flush and close the file fn file_creation_benchmark( benchmark_name: &str, - mount_dir_str: &str, + mount_dir: &Path, num_files: u32, file_size_bytes: u64, include_breakdown: bool, @@ -114,7 +114,7 @@ fn file_creation_benchmark( for file_number in 1..=num_files { let mut elapsed_total_ms: f64 = 0.0; - let path = format!("{mount_dir_str}/bench_file_{file_number}"); + let path = mount_dir.join(format!("bench_file_{file_number}")); // Perform and time the open operation let mut file = { @@ -127,7 +127,7 @@ fn file_creation_benchmark( let start = Instant::now(); let file = open .open(path.clone()) - .map_err(|e| anyhow::anyhow!("Failed to open file {}: {}", path, e))?; + .map_err(|e| anyhow::anyhow!("Failed to open file {}: {}", path.display(), e))?; let elapsed_ms = start.elapsed().as_millis_f64_temp(); open_latency_samples.push(elapsed_ms); elapsed_total_ms += elapsed_ms; @@ -138,7 +138,7 @@ fn file_creation_benchmark( { let start = Instant::now(); file.write_all(&vec![0u8; file_size_bytes as usize]) - .map_err(|e| anyhow::anyhow!("Failed to write to file {}: {}", path, e))?; + .map_err(|e| anyhow::anyhow!("Failed to write to file {}: {}", path.display(), e))?; let elapsed_ms = start.elapsed().as_millis_f64_temp(); write_latency_samples.push(elapsed_ms); elapsed_total_ms += elapsed_ms; @@ -155,7 +155,8 @@ fn file_creation_benchmark( total_latency_samples.push(elapsed_total_ms); - fs::remove_file(path.clone()).map_err(|e| anyhow::anyhow!("Failed to remove file {}: {}", path, e))?; + fs::remove_file(path.clone()) + .map_err(|e| anyhow::anyhow!("Failed to remove file {}: {}", path.display(), e))?; } let total_latency_result = BenchmarkResult { @@ -195,18 +196,13 @@ fn main() -> Result<()> { benchmark_type, detailed, } = CliArgs::parse(); - let mount_dir_os_str = mount_dir.into_os_string(); - let mount_dir_str = mount_dir_os_str - .to_str() - .ok_or_else(|| anyhow!("Invalid UTF-8 in mount directory path"))?; - const NUM_FILES: u32 = 100; let benchmark_results = match benchmark_type { - BenchmarkType::OneByteFile => one_byte_file_creation_benchmark(mount_dir_str, NUM_FILES, detailed)?, - BenchmarkType::All => vec![one_byte_file_creation_benchmark(mount_dir_str, NUM_FILES, detailed)?] - .into_iter() - .flatten() - .collect(), + BenchmarkType::OneByteFile => one_byte_file_creation_benchmark(&mount_dir, NUM_FILES, detailed)?, + BenchmarkType::All => vec![one_byte_file_creation_benchmark(&mount_dir, NUM_FILES, detailed)?] + .into_iter() + .flatten() + .collect(), }; let contents = json!(benchmark_results); @@ -217,3 +213,38 @@ fn main() -> Result<()> { Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mean_empty_vector() { + let numbers: Vec = vec![]; + assert!((mean(&numbers)).is_nan()); + } + + #[test] + fn test_mean_single_number() { + let numbers = vec![42.0]; + assert_eq!(mean(&numbers), 42.0); + } + + #[test] + fn test_mean_mixed_numbers() { + let numbers = vec![-2.0, -1.5, 0.0, 1.0, 2.5]; + assert_eq!(mean(&numbers), 0.0); + } + + #[test] + fn test_mean_large_numbers() { + let numbers = vec![1e7, 2e7, 3e7]; + assert_eq!(mean(&numbers), 2e7); + } + + #[test] + fn test_mean_precision() { + let numbers = vec![1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0]; + assert!((mean(&numbers) - 1.0 / 3.0).abs() < f64::EPSILON); + } +} From c777f841d6fe37b8152491b5d299141a413736a5 Mon Sep 17 00:00:00 2001 From: Renan Magagnin Date: Tue, 14 Jan 2025 10:20:11 +0000 Subject: [PATCH 5/5] Fix formatting Signed-off-by: Renan Magagnin --- mountpoint-s3/src/bin/file-system-benchmarks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mountpoint-s3/src/bin/file-system-benchmarks.rs b/mountpoint-s3/src/bin/file-system-benchmarks.rs index 5590fa966..736dad543 100644 --- a/mountpoint-s3/src/bin/file-system-benchmarks.rs +++ b/mountpoint-s3/src/bin/file-system-benchmarks.rs @@ -200,9 +200,9 @@ fn main() -> Result<()> { let benchmark_results = match benchmark_type { BenchmarkType::OneByteFile => one_byte_file_creation_benchmark(&mount_dir, NUM_FILES, detailed)?, BenchmarkType::All => vec![one_byte_file_creation_benchmark(&mount_dir, NUM_FILES, detailed)?] - .into_iter() - .flatten() - .collect(), + .into_iter() + .flatten() + .collect(), }; let contents = json!(benchmark_results);