mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-09 06:39:25 +00:00
perf(ecstore): cache modern erasure codec construction (#5824)
Co-authored-by: heihutu <heihutu@gmail.com> Co-authored-by: zhi22915 <qiuzgang@gmail.com>
This commit is contained in:
@@ -20,12 +20,21 @@ use bytes::{Bytes, BytesMut};
|
||||
use reed_solomon_erasure::galois_8::ReedSolomon;
|
||||
use reed_solomon_simd;
|
||||
use smallvec::SmallVec;
|
||||
use std::io;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
sync::{Arc, OnceLock, RwLock},
|
||||
};
|
||||
use tokio::io::AsyncRead;
|
||||
use tracing::warn;
|
||||
use uuid::Uuid;
|
||||
|
||||
const MODERN_MAX_TOTAL_SHARDS: usize = <reed_solomon_erasure::galois_8::Field as reed_solomon_erasure::Field>::ORDER;
|
||||
const MODERN_REED_SOLOMON_CACHE_MAX_ENTRIES: usize = 64;
|
||||
|
||||
type ModernReedSolomonCache = RwLock<HashMap<(usize, usize), Arc<ReedSolomon>>>;
|
||||
|
||||
static MODERN_REED_SOLOMON_CACHE: OnceLock<ModernReedSolomonCache> = OnceLock::new();
|
||||
|
||||
/// Errors returned when constructing an [`Erasure`] codec.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -275,7 +284,7 @@ impl LegacyReedSolomonEncoder {
|
||||
pub struct ReedSolomonEncoder {
|
||||
data_shards: usize,
|
||||
parity_shards: usize,
|
||||
encoder: Option<ReedSolomon>,
|
||||
encoder: Option<Arc<ReedSolomon>>,
|
||||
}
|
||||
|
||||
impl Clone for ReedSolomonEncoder {
|
||||
@@ -291,7 +300,7 @@ impl Clone for ReedSolomonEncoder {
|
||||
impl ReedSolomonEncoder {
|
||||
fn try_new_typed(data_shards: usize, parity_shards: usize) -> Result<Self, reed_solomon_erasure::Error> {
|
||||
let encoder = if parity_shards > 0 {
|
||||
Some(ReedSolomon::new(data_shards, parity_shards)?)
|
||||
Some(cached_modern_reed_solomon(data_shards, parity_shards)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -362,6 +371,30 @@ impl ReedSolomonEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
fn cached_modern_reed_solomon(data_shards: usize, parity_shards: usize) -> Result<Arc<ReedSolomon>, reed_solomon_erasure::Error> {
|
||||
let key = (data_shards, parity_shards);
|
||||
let cache = MODERN_REED_SOLOMON_CACHE.get_or_init(|| RwLock::new(HashMap::new()));
|
||||
|
||||
if let Some(encoder) = cache
|
||||
.read()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner())
|
||||
.get(&key)
|
||||
.cloned()
|
||||
{
|
||||
return Ok(encoder);
|
||||
}
|
||||
|
||||
let encoder = Arc::new(ReedSolomon::new(data_shards, parity_shards)?);
|
||||
let mut cache = cache.write().unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||
if let Some(existing) = cache.get(&key) {
|
||||
return Ok(Arc::clone(existing));
|
||||
}
|
||||
if cache.len() < MODERN_REED_SOLOMON_CACHE_MAX_ENTRIES {
|
||||
cache.insert(key, Arc::clone(&encoder));
|
||||
}
|
||||
Ok(encoder)
|
||||
}
|
||||
|
||||
fn encode_parity_shards<F>(shards: &mut [Option<Vec<u8>>], data_shards: usize, parity_shards: usize, encode: F) -> io::Result<()>
|
||||
where
|
||||
F: FnOnce(SmallVec<[&mut [u8]; 16]>) -> io::Result<()>,
|
||||
@@ -1272,6 +1305,16 @@ mod tests {
|
||||
assert!(legacy.legacy_encoder.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modern_encoder_construction_reuses_cached_codec() {
|
||||
let first = ReedSolomonEncoder::try_new_typed(31, 7).expect("modern codec should construct");
|
||||
let second = ReedSolomonEncoder::try_new_typed(31, 7).expect("modern codec should construct");
|
||||
|
||||
let first = first.encoder.as_ref().expect("modern codec should initialize an encoder");
|
||||
let second = second.encoder.as_ref().expect("modern codec should initialize an encoder");
|
||||
assert!(Arc::ptr_eq(first, second));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construction_errors_preserve_encoder_sources() {
|
||||
let modern = ErasureConstructionError::ModernEncoder {
|
||||
|
||||
Reference in New Issue
Block a user