From 71bc57a32f261ca83cc0ced8eb569a382bb5cb55 Mon Sep 17 00:00:00 2001 From: Zachary Dremann Date: Tue, 19 Mar 2024 23:00:14 -0400 Subject: [PATCH] Add a benchmark to see how to access resource forks quickly --- Cargo.lock | 290 ++++++++++++++++++ crates/resource-fork/Cargo.toml | 5 + crates/resource-fork/benches/compare_impls.rs | 111 +++++++ 3 files changed, 406 insertions(+) create mode 100644 crates/resource-fork/benches/compare_impls.rs diff --git a/Cargo.lock b/Cargo.lock index bb4cf04..d13e416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ dependencies = [ "memchr", ] +[[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.7" @@ -149,6 +155,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.79" @@ -161,6 +179,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[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.4.18" @@ -247,6 +292,42 @@ dependencies = [ "cfg-if", ] +[[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", + "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", +] + [[package]] name = "crossbeam-channel" version = "0.5.11" @@ -289,6 +370,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -309,6 +396,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -389,12 +482,28 @@ dependencies = [ "regex-syntax 0.8.2", ] +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "humansize" version = "2.1.3" @@ -442,12 +551,41 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -555,6 +693,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -576,6 +723,12 @@ dependencies = [ "loom", ] +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "overload" version = "0.1.1" @@ -594,6 +747,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + [[package]] name = "portable-atomic" version = "1.3.3" @@ -654,6 +835,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -728,6 +929,7 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" name = "resource-fork" version = "0.2.0" dependencies = [ + "criterion", "libc", "tempfile", ] @@ -783,6 +985,20 @@ name = "serde" version = "1.0.170" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56657f512baabca8f840542f9ca8152aecf182c473c26e46e58d6aab4f6e439" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d477848e6b23adba0db397777d5aad864555bc17fd9c89abb3b8009788b7b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" @@ -881,6 +1097,16 @@ dependencies = [ "tikv-jemalloc-sys", ] +[[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 = "tracing" version = "0.1.40" @@ -1005,6 +1231,70 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/resource-fork/Cargo.toml b/crates/resource-fork/Cargo.toml index c7e1834..013f496 100644 --- a/crates/resource-fork/Cargo.toml +++ b/crates/resource-fork/Cargo.toml @@ -8,8 +8,13 @@ repository = "https://github.com/Dr-Emann/applesauce" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bench]] +name = "compare_impls" +harness = false + [dependencies] libc = "0.2" [dev-dependencies] tempfile = "3.9.0" +criterion = { version = "0.5.1", features = ["html_reports"] } diff --git a/crates/resource-fork/benches/compare_impls.rs b/crates/resource-fork/benches/compare_impls.rs new file mode 100644 index 0000000..df8587a --- /dev/null +++ b/crates/resource-fork/benches/compare_impls.rs @@ -0,0 +1,111 @@ +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use resource_fork::ResourceFork; +use std::fs::File; +use std::io::{Read, Write}; +use tempfile::NamedTempFile; + +fn criterion_benchmark(c: &mut Criterion) { + let data = random_data(); + let mut g = c.benchmark_group("write"); + g.throughput(criterion::Throughput::Bytes(data.len() as u64)); + g.bench_function("rsrc_path", |b| { + b.iter_batched_ref( + || NamedTempFile::new().unwrap(), + |file| { + let mut rsrc_file = File::create(file.path().join("..namedfork/rsrc")).unwrap(); + rsrc_file.write_all(&data).unwrap(); + rsrc_file.flush().unwrap(); + drop(rsrc_file); + }, + // Limit the number of open files + BatchSize::LargeInput, + ); + }); + g.bench_function("xattr", |b| { + b.iter_batched_ref( + || NamedTempFile::new().unwrap(), + |file| { + let mut rsrc_file = ResourceFork::new(file.as_file()); + rsrc_file.write_all(&data).unwrap(); + rsrc_file.flush().unwrap(); + drop(rsrc_file); + }, + // Limit the number of open files + BatchSize::LargeInput, + ); + }); + g.finish(); + + let mut output_data = vec![0; data.len()]; + let mut g = c.benchmark_group("read"); + g.throughput(criterion::Throughput::Bytes(data.len() as u64)); + g.bench_function("rsrc_path", |b| { + b.iter_batched_ref( + || { + let file = NamedTempFile::new().unwrap(); + let mut rsrc_file = File::create(file.path().join("..namedfork/rsrc")).unwrap(); + rsrc_file.write_all(&data).unwrap(); + file + }, + |file| { + let mut rsrc_file = File::open(file.path().join("..namedfork/rsrc")).unwrap(); + rsrc_file.read_exact(&mut output_data).unwrap(); + }, + // Limit the number of open files + BatchSize::LargeInput, + ); + }); + g.bench_function("xattr", |b| { + b.iter_batched_ref( + || { + let file = NamedTempFile::new().unwrap(); + let mut rsrc_file = ResourceFork::new(file.as_file()); + rsrc_file.write_all(&data).unwrap(); + file + }, + |file| { + let mut rsrc_file = ResourceFork::new(file.as_file()); + rsrc_file.read_exact(&mut output_data).unwrap(); + }, + // Limit the number of open files + BatchSize::LargeInput, + ); + }); + g.finish(); +} + +fn random_data() -> Vec { + let mut data = vec![0; 8 * 1024 * 1024]; + let mut rng = Xorshift32 { state: 0x193a6754 }; + rng.fill_bytes(&mut data); + data +} + +struct Xorshift32 { + state: u32, +} + +impl Xorshift32 { + fn next(&mut self) -> u32 { + self.state ^= self.state << 13; + self.state ^= self.state >> 17; + self.state ^= self.state << 5; + self.state + } + + fn fill_bytes(&mut self, buf: &mut [u8]) { + let mut iter = buf.chunks_exact_mut(4); + for chunk in &mut iter { + let value = self.next(); + chunk.copy_from_slice(&value.to_le_bytes()); + } + let remainder = iter.into_remainder(); + if remainder.is_empty() { + return; + } + remainder.copy_from_slice(&self.next().to_le_bytes()[..remainder.len()]); + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches);