Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,13 +359,14 @@ The Criterion suite in `benches/hot_path.rs` needs the `encryption` feature (`ma
| Case | What one call does |
|:-----|:-------------------|
| `store/<size>`, `retrieve/<size>` | `ByteStorage::store` / `retrieve` on the realistic msgpack payload `hot_path` uses |
| `retrieve_data/<size>` | `ByteStorage::retrieve_data`, the bindings' call, on the same envelope as `retrieve` |
| `prescan/<size>` | `check_msgpack_structure` on the envelope, the pre-scan `retrieve` runs first |
| `encrypt/<size>`, `decrypt/<size>` | `ZeroKnowledgeEncryptor` AES-256-GCM |
| `keyring_decrypt/<size>` | `Keyring::decrypt_indexed`, which re-derives the tenant key (HKDF) on every call |
| `tenant_keyring_decrypt/<size>` | `TenantKeyring::decrypt_indexed`, key derived once at `for_tenant` |
| `hkdf` | `derive_domain_key` (takes no payload, so it has no size) |

Sizes are 64 B, 1 KiB and 64 KiB. `store` and `retrieve` also run at 40 B, below the 64-byte literals-only block threshold, and at 1 MiB. `ByteStorage` switches to its copying allocation pattern once the envelope or the compressed payload reaches 64 KiB. The 64 KiB payload compresses to a 24,702-byte envelope and never gets there; the 1 MiB one compresses to 383,893 bytes. One case at a time: `make perf-ir ARGS="--case store/1024"`.
Sizes are 64 B, 1 KiB and 64 KiB. `store`, `retrieve` and `retrieve_data` also run at 40 B, below the 64-byte literals-only block threshold, and at 1 MiB. `ByteStorage` switches to its copying allocation pattern once the envelope or the compressed payload reaches 64 KiB. The 64 KiB payload compresses to a 24,702-byte envelope and never gets there; the 1 MiB one compresses to 383,893 bytes. One case at a time: `make perf-ir ARGS="--case store/1024"`. The bench binary is built with fat LTO, so its cases shape each other's code. Because both `retrieve` and `retrieve_data` call the envelope decode, it stays out of line, and `retrieve` costs 68 to 77 Ir/op more at 40 B to 1 KiB (2.6% at 40 B) than in a binary that calls `retrieve` alone. A consumer that calls only one of the two gets the decode inlined.

**Method** (`benches/perf_ir.rs`): each case runs in its own process under `valgrind --tool=cachegrind --cache-sim=no`, once with N calls and once with none, after the same setup and one warm-up call. N is 1,000, or 20 for a 1 MiB case. Ir/op is `(Ir[N] - Ir[0]) / N`, so process start, setup and exit cancel. The counts are exact, so N sets the run time, not the precision. The measured process gets an empty environment, so neither the shell nor a `VALGRIND_OPTS` changes what is counted.

Expand Down
12 changes: 10 additions & 2 deletions benches/perf_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ const BASELINES: &str = concat!(
const MIB: usize = 1024 * 1024;
/// A small value, a typical value, a large object (as `hot_path`).
const SIZES: &[usize] = &[64, 1024, 64 * 1024];
/// ByteStorage picks its strategy by size, so store and retrieve also measure
/// ByteStorage picks its strategy by size, so store and retrieve (and
/// retrieve_data, which decodes the same envelope) also measure
/// 40 B, under the literals-only block threshold (64 B), and 1 MiB. Its
/// large-payload switch compares the envelope (store) or the compressed
/// payload (retrieve) with 64 KiB: the 64 KiB payload stores as a
Expand All @@ -85,9 +86,10 @@ const STORAGE_SIZES: &[usize] = &[40, 64, 1024, 64 * 1024, MIB];

/// Operations and their payload sizes. HKDF derives from the master key and
/// tenant id alone, so it has one case with no size.
const OPS: [(&str, &[usize]); 8] = [
const OPS: [(&str, &[usize]); 9] = [
("store", STORAGE_SIZES),
("retrieve", STORAGE_SIZES),
("retrieve_data", STORAGE_SIZES),
("prescan", SIZES),
("encrypt", SIZES),
("decrypt", SIZES),
Expand Down Expand Up @@ -152,6 +154,12 @@ fn operation(case: &str) -> Box<dyn FnMut() -> usize> {
match op {
"store" => Box::new(move || storage.store(black_box(&payload), None).unwrap().len()),
"retrieve" => Box::new(move || storage.retrieve(black_box(&envelope)).unwrap().0.len()),
// What the bindings call: retrieve without the format String. Under
// fat LTO this second caller keeps decode_envelope out of line, which
// costs retrieve 68 to 77 Ir/op at 40 B to 1 KiB (README).
"retrieve_data" => {
Box::new(move || storage.retrieve_data(black_box(&envelope)).unwrap().len())
}
"prescan" => Box::new(move || {
check_msgpack_structure(black_box(&envelope), ENVELOPE_MAX_DEPTH).is_ok() as usize
}),
Expand Down
17 changes: 12 additions & 5 deletions benches/perf_ir_baselines.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@
"prescan/1024": 985,
"prescan/64": 883,
"prescan/65536": 935,
"retrieve/1024": 7323,
"retrieve/1048576": 6676538,
"retrieve/40": 2560,
"retrieve/64": 2552,
"retrieve/65536": 391697,
"retrieve/1024": 7409,
"retrieve/1048576": 6679353,
"retrieve/40": 2637,
"retrieve/64": 2630,
"retrieve/65536": 391975,
"retrieve_data/1024": 7243,
"retrieve_data/1048576": 6679184,
"retrieve_data/40": 2471,
"retrieve_data/64": 2464,
"retrieve_data/65536": 391809,
"store/1024": 22934,
"store/1048576": 11912874,
"store/40": 1186,
Expand All @@ -39,6 +44,8 @@
"prescan/65536": 0,
"retrieve/1048576": 0,
"retrieve/65536": 0,
"retrieve_data/1048576": 0,
"retrieve_data/65536": 0,
"store/1048576": 0,
"store/65536": 0,
"tenant_keyring_decrypt/65536": 0
Expand Down
Loading