Skip to content

Commit 4032584

Browse files
authored
perf(crypto): CRP-1827 Add a cache for G2Prepared (#7967)
This improves the benchmark results of the verify_multi_sig_individual and verify_multi_sig_combined benchmarks by approximately 5%. This also corrects an error introduced when the G2 point cache was introduced; it reported its cache statistics into the BLS signature verification cache instead of the metrics for the point cache.
1 parent be87a6a commit 4032584

File tree

5 files changed

+134
-4
lines changed

5 files changed

+134
-4
lines changed

rs/crypto/internal/crypto_lib/bls12_381/type/src/cache.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::G2Affine;
1+
use crate::{G2Affine, G2Prepared};
22
use cached::{Cached, SizedCache};
33
use parking_lot::Mutex;
44
use std::sync::LazyLock;
@@ -86,3 +86,71 @@ impl G2PublicKeyCache {
8686
G2PublicKeyCacheStatistics::new(cache_size, hits, misses)
8787
}
8888
}
89+
90+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
91+
pub struct G2PreparedCacheStatistics {
92+
pub size: usize,
93+
pub hits: u64,
94+
pub misses: u64,
95+
}
96+
97+
impl G2PreparedCacheStatistics {
98+
fn new(size: usize, hits: u64, misses: u64) -> Self {
99+
Self { size, hits, misses }
100+
}
101+
}
102+
103+
/// A cache for G2 Public Keys
104+
pub(crate) struct G2PreparedCache {
105+
cache: Mutex<SizedCache<[u8; G2Affine::BYTES], G2Prepared>>,
106+
}
107+
108+
static GLOBAL_G2PREP_CACHE: LazyLock<G2PreparedCache> =
109+
LazyLock::new(|| G2PreparedCache::new(G2PreparedCache::SIZE_OF_GLOBAL_CACHE));
110+
111+
impl G2PreparedCache {
112+
/// Specify the size of the global cache used for public keys
113+
///
114+
/// This cache is kept small because G2Prepared is quite large,
115+
/// just under 19 KiB.
116+
pub const SIZE_OF_GLOBAL_CACHE: usize = 50;
117+
118+
/// Create a new signature cache with the specified maximum size
119+
fn new(max_size: usize) -> Self {
120+
let cache = Mutex::<SizedCache<[u8; G2Affine::BYTES], G2Prepared>>::new(
121+
SizedCache::with_size(max_size),
122+
);
123+
Self { cache }
124+
}
125+
126+
/// Return a reference to the global signature cache
127+
pub(crate) fn global() -> &'static Self {
128+
&GLOBAL_G2PREP_CACHE
129+
}
130+
131+
/// Check the cache for an already prepared G2 element
132+
pub(crate) fn get(&self, bytes: &[u8; G2Affine::BYTES]) -> Option<G2Prepared> {
133+
let mut cache = self.cache.lock();
134+
cache.cache_get(bytes).cloned()
135+
}
136+
137+
/// Insert a new G2Prepared into the cache
138+
pub(crate) fn insert(&self, bytes: [u8; G2Affine::BYTES], prep: G2Prepared) {
139+
let mut cache = self.cache.lock();
140+
cache.cache_set(bytes, prep);
141+
}
142+
143+
/// Return statistics about the cache
144+
///
145+
/// Returns the size of the cache, the number of cache hits, and
146+
/// the number of cache misses
147+
pub(crate) fn cache_statistics(&self) -> G2PreparedCacheStatistics {
148+
let cache = self.cache.lock();
149+
150+
let cache_size = cache.cache_size();
151+
let hits = cache.cache_hits().unwrap_or(0);
152+
let misses = cache.cache_misses().unwrap_or(0);
153+
154+
G2PreparedCacheStatistics::new(cache_size, hits, misses)
155+
}
156+
}

rs/crypto/internal/crypto_lib/bls12_381/type/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2443,11 +2443,23 @@ impl G2Prepared {
24432443
pub fn neg_generator() -> &'static Self {
24442444
&G2PREPARED_NEG_G
24452445
}
2446+
2447+
/// Return statistics related to the G2Prepared cache
2448+
pub fn cache_statistics() -> crate::cache::G2PreparedCacheStatistics {
2449+
crate::cache::G2PreparedCache::global().cache_statistics()
2450+
}
24462451
}
24472452

24482453
impl From<&G2Affine> for G2Prepared {
24492454
fn from(v: &G2Affine) -> Self {
2450-
Self::new((*v.inner()).into())
2455+
let bytes = v.serialize();
2456+
if let Some(prep) = crate::cache::G2PreparedCache::global().get(&bytes) {
2457+
prep
2458+
} else {
2459+
let prep = Self::new((*v.inner()).into());
2460+
crate::cache::G2PreparedCache::global().insert(bytes, prep.clone());
2461+
prep
2462+
}
24512463
}
24522464
}
24532465

rs/crypto/internal/logmon/src/metrics.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Metrics exported by crypto
22
3+
mod bls12_381_g2_prep_cache;
34
mod bls12_381_point_cache;
45
mod bls12_381_sig_cache;
56

@@ -283,7 +284,7 @@ impl CryptoMetrics {
283284
}
284285
}
285286

286-
/// Observes the cache statistics for the verification of threshold BLS12-381 signatures.
287+
/// Observes the cache statistics for parsing of BLS12-381 points
287288
pub fn observe_bls12_381_point_cache_stats(&self, size: usize, hits: u64, misses: u64) {
288289
if let Some(metrics) = &self.metrics {
289290
let m = &metrics.crypto_bls12_381_point_cache_metrics;
@@ -299,6 +300,22 @@ impl CryptoMetrics {
299300
}
300301
}
301302

303+
/// Observes the cache statistics for parsing of BLS12-381 G2Prepared
304+
pub fn observe_bls12_381_g2_prep_cache_stats(&self, size: usize, hits: u64, misses: u64) {
305+
if let Some(metrics) = &self.metrics {
306+
let m = &metrics.crypto_bls12_381_g2_prep_cache_metrics;
307+
m.cache_size.set(size as i64);
308+
309+
let prev_hits = m.cache_hits.get();
310+
debug_assert!(prev_hits <= hits);
311+
m.cache_hits.inc_by(hits - prev_hits);
312+
313+
let prev_misses = m.cache_misses.get();
314+
debug_assert!(prev_misses <= misses);
315+
m.cache_misses.inc_by(misses - prev_misses);
316+
}
317+
}
318+
302319
/// Observes the minimum epoch in active NI-DKG transcripts
303320
pub fn observe_minimum_epoch_in_active_nidkg_transcripts(&self, epoch: u32) {
304321
if let Some(metrics) = &self.metrics {
@@ -609,6 +626,9 @@ struct Metrics {
609626
/// Metrics for the cache of successfully decoded BLS12-381 points
610627
pub crypto_bls12_381_point_cache_metrics: bls12_381_point_cache::Metrics,
611628

629+
/// Metrics for the cache of successfully created BLS12-381 G2Prepared
630+
pub crypto_bls12_381_g2_prep_cache_metrics: bls12_381_g2_prep_cache::Metrics,
631+
612632
/// Gauge for the minimum epoch in active NI-DKG transcripts.
613633
observe_minimum_epoch_in_active_nidkg_transcripts: Gauge,
614634

@@ -774,6 +794,18 @@ impl Metrics {
774794
"crypto_bls12_381_point_cache_misses",
775795
"Number of cache misses for successfully decoded BLS12-381 points"),
776796
},
797+
crypto_bls12_381_g2_prep_cache_metrics: bls12_381_g2_prep_cache::Metrics {
798+
cache_size: r.int_gauge(
799+
"crypto_bls12_381_g2_prep_cache_size",
800+
"Size of cache of BLS12-381 G2Prepared",
801+
),
802+
cache_hits: r.int_counter(
803+
"crypto_bls12_381_g2_prep_cache_hits",
804+
"Number of cache hits of BLS12-381 G2Prepared cache"),
805+
cache_misses: r.int_counter(
806+
"crypto_bls12_381_g2_prep_cache_misses",
807+
"Number of cache misses of BLS12-381 G2Prepared cache"),
808+
},
777809
observe_minimum_epoch_in_active_nidkg_transcripts: r.gauge(
778810
"crypto_minimum_epoch_in_active_nidkg_transcripts",
779811
"Minimum epoch in active NI-DKG transcripts"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use prometheus::{IntCounter, IntGauge};
2+
3+
/// Metrics for the cache of successfully created BLS12-381 G2Prepared
4+
pub struct Metrics {
5+
/// [`IntGauge`] for tracking the size of the cache. The size is expected
6+
/// to increase with the time and remain at its max value until the process ends.
7+
/// But if it happens that the cache size decreases, we want to know about that,
8+
/// therefore we use an [`IntGauge`] and not a [`Counter`].
9+
pub cache_size: IntGauge,
10+
/// [`Counter`] for tracking the cache hits.
11+
pub cache_hits: IntCounter,
12+
/// [`Counter`] for tracking the cache misses.
13+
pub cache_misses: IntCounter,
14+
}

rs/crypto/src/sign/threshold_sig/ni_dkg.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,11 @@ impl<C: CryptoServiceProvider> NiDkgAlgorithm for CryptoComponentImpl<C> {
175175
// locking congestion or similar, we should be able to notice that.
176176
let stats = ic_crypto_internal_bls12_381_type::G2Affine::deserialize_cached_statistics();
177177
self.metrics
178-
.observe_bls12_381_sig_cache_stats(stats.size, stats.hits, stats.misses);
178+
.observe_bls12_381_point_cache_stats(stats.size, stats.hits, stats.misses);
179+
180+
let stats = ic_crypto_internal_bls12_381_type::G2Prepared::cache_statistics();
181+
self.metrics
182+
.observe_bls12_381_g2_prep_cache_stats(stats.size, stats.hits, stats.misses);
179183

180184
self.metrics.observe_parameter_size(
181185
MetricsDomain::NiDkgAlgorithm,

0 commit comments

Comments
 (0)