mirror of
https://github.com/mikedilger/nip44.git
synced 2026-07-04 05:57:29 +00:00
Added a benchmark.
This commit is contained in:
@@ -1 +1,8 @@
|
||||
Nostr NIP-44 encryption code
|
||||
|
||||
|
||||
To test and benchmark:
|
||||
|
||||
````
|
||||
cargo test --release -- --nocapture
|
||||
```
|
||||
|
||||
@@ -490,3 +490,78 @@ const PADDING: [(usize, usize); 24] = [
|
||||
(1020, 1024),
|
||||
(74123, 81920),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn bench_encryption_inner() {
|
||||
const SEC1HEX: &'static str =
|
||||
"dc4b57c5fe856584b01aab34dad7454b0f715bdfab091bf0dbbe12f65c778838";
|
||||
const SEC2HEX: &'static str =
|
||||
"3072ab28ed7d5c2e4f5efbdcde5fb11455ab7f976225d1779a1751eb6400411a";
|
||||
|
||||
let sec1bytes = hex::decode(SEC1HEX).unwrap();
|
||||
let sec1 = SecretKey::from_slice(&sec1bytes).unwrap();
|
||||
|
||||
let sec2bytes = hex::decode(SEC2HEX).unwrap();
|
||||
let sec2 = SecretKey::from_slice(&sec2bytes).unwrap();
|
||||
|
||||
let (pub2, _) = sec2.x_only_public_key(&SECP256K1);
|
||||
|
||||
let shared = get_conversation_key(sec1, pub2);
|
||||
|
||||
let message = r##"
|
||||
The compiler could theoretically make optimizations like the following:
|
||||
|
||||
needle and haystack are always the same, move the call to contains outside the loop and delete the loop
|
||||
Inline contains
|
||||
needle and haystack have values known at compile time, contains is always true. Remove the call and replace with true
|
||||
Nothing is done with the result of contains: delete this function call entirely
|
||||
benchmark now has no purpose: delete this function
|
||||
|
||||
It is not likely that all of the above happens, but the compiler is definitely able to make some optimizations that could result in a very inaccurate benchmark. This is where black_box comes in:
|
||||
|
||||
use std::hint::black_box;
|
||||
|
||||
// Same `contains` function
|
||||
fn contains(haystack: &[&str], needle: &str) -> bool {
|
||||
haystack.iter().any(|x| x == &needle)
|
||||
}
|
||||
|
||||
pub fn benchmark() {
|
||||
let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
|
||||
let needle = "ghi";
|
||||
for _ in 0..10 {
|
||||
// Adjust our benchmark loop contents
|
||||
black_box(contains(black_box(&haystack), black_box(needle)));
|
||||
}
|
||||
}
|
||||
|
||||
Run
|
||||
|
||||
This essentially tells the compiler to block optimizations across any calls to black_box. So, it now:
|
||||
|
||||
Treats both arguments to contains as unpredictable: the body of contains can no longer be optimized based on argument values
|
||||
Treats the call to contains and its result as volatile: the body of benchmark cannot optimize this away
|
||||
|
||||
This makes our benchmark much more realistic to how the function would be used in situ, where arguments are usually not known at compile time and the result is used in some way.
|
||||
"##;
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let rounds = 32768;
|
||||
for _ in 0..rounds {
|
||||
std::hint::black_box({
|
||||
let encrypted = encrypt(&shared, message).unwrap();
|
||||
let _decrypted = decrypt(&shared, &*encrypted).unwrap();
|
||||
});
|
||||
}
|
||||
let elapsed = start.elapsed();
|
||||
let total_nanos = elapsed.as_nanos();
|
||||
let nanos_per_encryption_and_decryption = total_nanos / rounds as u128;
|
||||
let nanos_per_encryption_and_decryption_per_char =
|
||||
10 * nanos_per_encryption_and_decryption / message.len() as u128;
|
||||
|
||||
println!(
|
||||
"{}.{} nanoseconds per character (encrypt and decrypt)",
|
||||
nanos_per_encryption_and_decryption_per_char / 10,
|
||||
nanos_per_encryption_and_decryption_per_char % 10,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user