From 7753ca0abecee434c3914ab326cdcc177f5110cd Mon Sep 17 00:00:00 2001 From: ruskaof <199-41@mail.ru> Date: Sat, 15 Nov 2025 10:22:35 +0300 Subject: [PATCH] benchmarks --- Cargo.lock | 308 ++++++++++++++++++++++- db/Cargo.toml | 11 + db/benches/object_storage_benchmark.rs | 332 +++++++++++++++++++++++++ 3 files changed, 649 insertions(+), 2 deletions(-) create mode 100644 db/benches/object_storage_benchmark.rs diff --git a/Cargo.lock b/Cargo.lock index 013cf83..83d19d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.21" @@ -218,6 +224,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.44" @@ -247,6 +259,33 @@ dependencies = [ "windows-link", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "4.5.50" @@ -299,6 +338,73 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "csv" version = "1.3.1" @@ -326,8 +432,10 @@ version = "0.1.0" dependencies = [ "async-stream", "clap", + "criterion", "encoder", "env_logger", + "futures", "futures-util", "grpc-server", "log", @@ -424,6 +532,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -431,6 +554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -439,6 +563,23 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-macro" version = "0.3.31" @@ -468,9 +609,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -523,6 +668,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hashbrown" version = "0.16.0" @@ -535,6 +691,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "http" version = "1.3.1" @@ -683,12 +845,32 @@ dependencies = [ "libc", ] +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.1", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -858,6 +1040,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "parking_lot" version = "0.12.4" @@ -929,6 +1117,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -980,7 +1196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" dependencies = [ "heck", - "itertools", + "itertools 0.14.0", "log", "multimap", "once_cell", @@ -1002,7 +1218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", "syn", @@ -1052,6 +1268,26 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -1121,6 +1357,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1243,6 +1488,16 @@ dependencies = [ "windows-sys 0.61.1", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.47.1" @@ -1452,6 +1707,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -1530,6 +1795,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.1", +] + [[package]] name = "windows-core" version = "0.62.1" @@ -1750,3 +2034,23 @@ name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/db/Cargo.toml b/db/Cargo.toml index bc6eb8f..9380d49 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -23,3 +23,14 @@ tempfile = "3.22.0" tokio = { version = "1.47.1", features = ["full"] } tokio-stream = "0.1.17" master = { path = "../master" } + +[dev-dependencies.criterion] +version = "0.5" +features = ["html_reports"] + +[dev-dependencies.futures] +version = "0.3" + +[[bench]] +name = "object_storage_benchmark" +harness = false diff --git a/db/benches/object_storage_benchmark.rs b/db/benches/object_storage_benchmark.rs new file mode 100644 index 0000000..19e3905 --- /dev/null +++ b/db/benches/object_storage_benchmark.rs @@ -0,0 +1,332 @@ +use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main}; +use db::storage::simple::ObjectStorageSimple; +use db::storage::{CreateBucketDTO, DeleteObjectDTO, GetObjectDTO, ObjectStorage, PutObjectDTO}; +use tempfile::tempdir; +use tokio::runtime::{Builder, Runtime}; +use tokio_stream::{self as stream, StreamExt}; + +async fn create_storage() -> (ObjectStorageSimple, tempfile::TempDir) { + let temp_dir = tempdir().expect("Failed to create temp directory"); + let base_dir = temp_dir.path().to_path_buf(); + + let bytes_storage = db::storage::simple::chunk_file_storage::PartitionedBytesStorage::new( + base_dir.join("data"), + 4, + ); + + let buckets_metadata_storage = + db::storage::simple::buckets_metadata_storage::BucketsMetadataStorage::new( + base_dir.join("metadata").to_string_lossy().to_string(), + ) + .await + .expect("Failed to create buckets metadata storage"); + + let storage = ObjectStorageSimple { + base_dir, + bytes_storage, + buckets_metadata_storage, + }; + + (storage, temp_dir) +} + +fn create_test_data(size: usize) -> Vec { + (0..size).map(|i| (i % 256) as u8).collect() +} + +fn create_stream(data: Vec) -> impl stream::Stream> + Unpin + Send + 'static { + stream::iter(vec![data]) +} + +fn create_single_threaded_runtime() -> Runtime { + Builder::new_current_thread() + .enable_all() + .build() + .expect("Failed to create single-threaded runtime") +} + +fn create_multi_threaded_runtime() -> Runtime { + Builder::new_multi_thread() + .worker_threads(4) + .enable_all() + .build() + .expect("Failed to create multi-threaded runtime") +} + +fn bench_put_operation( + c: &mut Criterion, + group_name: &str, + sizes: &[usize], + runtime: &Runtime, + runtime_type: &str, +) { + let mut group = c.benchmark_group(format!("{}_{}", group_name, runtime_type)); + + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::new("put_object", size), size, |b, &size| { + b.iter(|| { + runtime.block_on(async { + let (storage, _temp_dir) = create_storage().await; + + storage + .create_bucket(CreateBucketDTO { + bucket_name: "test-bucket".to_string(), + }) + .await + .expect("Failed to create bucket"); + + let test_data = create_test_data(size); + let stream = create_stream(test_data.clone()); + + black_box( + storage + .put_object(PutObjectDTO { + bucket_name: "test-bucket".to_string(), + key: "test-object".to_string(), + stream: Box::new(stream), + }) + .await + .expect("Failed to put object"), + ); + }); + }); + }); + } + + group.finish(); +} + +fn bench_get_operation( + c: &mut Criterion, + group_name: &str, + sizes: &[usize], + runtime: &Runtime, + runtime_type: &str, +) { + let mut group = c.benchmark_group(format!("{}_{}", group_name, runtime_type)); + + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::new("get_object", size), size, |b, &size| { + b.iter(|| { + runtime.block_on(async { + let (storage, _temp_dir) = create_storage().await; + + storage + .create_bucket(CreateBucketDTO { + bucket_name: "test-bucket".to_string(), + }) + .await + .expect("Failed to create bucket"); + + let test_data = create_test_data(size); + let put_stream = create_stream(test_data.clone()); + + storage + .put_object(PutObjectDTO { + bucket_name: "test-bucket".to_string(), + key: "test-object".to_string(), + stream: Box::new(put_stream), + }) + .await + .expect("Failed to put object"); + + let mut stream = storage + .get_object(GetObjectDTO { + bucket_name: "test-bucket".to_string(), + key: "test-object".to_string(), + }) + .await + .expect("Failed to get object"); + + let mut total_bytes = 0; + while let Some(chunk) = stream.next().await { + total_bytes += chunk.len(); + black_box(chunk); + } + + assert_eq!(total_bytes, size); + }); + }); + }); + } + + group.finish(); +} + +fn bench_put_get_delete_operation( + c: &mut Criterion, + group_name: &str, + sizes: &[usize], + runtime: &Runtime, + runtime_type: &str, +) { + let mut group = c.benchmark_group(format!("{}_{}", group_name, runtime_type)); + + for size in sizes.iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input( + BenchmarkId::new("put_get_delete", size), + size, + |b, &size| { + b.iter(|| { + runtime.block_on(async { + let (storage, _temp_dir) = create_storage().await; + + storage + .create_bucket(CreateBucketDTO { + bucket_name: "test-bucket".to_string(), + }) + .await + .expect("Failed to create bucket"); + + let test_data = create_test_data(size); + let stream = create_stream(test_data.clone()); + + let metadata = storage + .put_object(PutObjectDTO { + bucket_name: "test-bucket".to_string(), + key: "test-object".to_string(), + stream: Box::new(stream), + }) + .await + .expect("Failed to put object"); + + let mut get_stream = storage + .get_object(GetObjectDTO { + bucket_name: "test-bucket".to_string(), + key: "test-object".to_string(), + }) + .await + .expect("Failed to get object"); + + let mut total_bytes = 0; + while let Some(chunk) = get_stream.next().await { + total_bytes += chunk.len(); + black_box(chunk); + } + + assert_eq!(total_bytes, size); + + storage + .delete_object(DeleteObjectDTO { + bucket_name: "test-bucket".to_string(), + key: "test-object".to_string(), + }) + .await + .expect("Failed to delete object"); + + black_box(metadata); + }); + }); + }, + ); + } + + group.finish(); +} + +fn bench_small_objects(c: &mut Criterion) { + let single_threaded_rt = create_single_threaded_runtime(); + let multi_threaded_rt = create_multi_threaded_runtime(); + + let sizes = [1024]; + + bench_put_operation( + c, + "small_objects", + &sizes, + &single_threaded_rt, + "single_threaded", + ); + bench_get_operation( + c, + "small_objects", + &sizes, + &single_threaded_rt, + "single_threaded", + ); + bench_put_get_delete_operation( + c, + "small_objects", + &sizes, + &single_threaded_rt, + "single_threaded", + ); + + bench_put_operation( + c, + "small_objects", + &sizes, + &multi_threaded_rt, + "multi_threaded", + ); + bench_get_operation( + c, + "small_objects", + &sizes, + &multi_threaded_rt, + "multi_threaded", + ); + bench_put_get_delete_operation( + c, + "small_objects", + &sizes, + &multi_threaded_rt, + "multi_threaded", + ); +} + +fn bench_average_objects(c: &mut Criterion) { + let single_threaded_rt = create_single_threaded_runtime(); + let multi_threaded_rt = create_multi_threaded_runtime(); + + let sizes = [1048576]; + bench_put_operation( + c, + "average_objects", + &sizes, + &single_threaded_rt, + "single_threaded", + ); + bench_get_operation( + c, + "average_objects", + &sizes, + &single_threaded_rt, + "single_threaded", + ); + bench_put_get_delete_operation( + c, + "average_objects", + &sizes, + &single_threaded_rt, + "single_threaded", + ); + + bench_put_operation( + c, + "average_objects", + &sizes, + &multi_threaded_rt, + "multi_threaded", + ); + bench_get_operation( + c, + "average_objects", + &sizes, + &multi_threaded_rt, + "multi_threaded", + ); + bench_put_get_delete_operation( + c, + "average_objects", + &sizes, + &multi_threaded_rt, + "multi_threaded", + ); +} + +criterion_group!(benches, bench_small_objects, bench_average_objects,); +criterion_main!(benches);