AccountSharedData: optimize set_data_from_slice
Remove memcmp and try to avoid allocations by trying to reuse or extend existing capacity.
This commit is contained in:
parent
e4affb9fea
commit
bad09812e3
|
@ -0,0 +1,152 @@
|
||||||
|
#![feature(test)]
|
||||||
|
#![allow(clippy::integer_arithmetic)]
|
||||||
|
|
||||||
|
use solana_sdk::{entrypoint::MAX_PERMITTED_DATA_INCREASE, pubkey::Pubkey};
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
use {solana_sdk::account::AccountSharedData, test::Bencher};
|
||||||
|
|
||||||
|
fn bench_unchanged(bencher: &mut Bencher, size: usize) {
|
||||||
|
let mut account = AccountSharedData::new(42, 0, &Pubkey::new_unique());
|
||||||
|
let new_data = vec![42; size];
|
||||||
|
account.set_data(new_data.clone());
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
account.set_data_from_slice(&new_data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_changed(bencher: &mut Bencher, size: usize) {
|
||||||
|
let mut account = AccountSharedData::new(42, 0, &Pubkey::new_unique());
|
||||||
|
let initial_data = vec![42; size];
|
||||||
|
account.set_data(initial_data);
|
||||||
|
|
||||||
|
let new_data = (0..10)
|
||||||
|
.map(|i| {
|
||||||
|
let mut data = vec![42; size];
|
||||||
|
data[size / 10 * i] = 43;
|
||||||
|
data
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut new_data = new_data.iter().cycle();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
account.set_data_from_slice(new_data.next().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_grow(bencher: &mut Bencher, size: usize) {
|
||||||
|
let mut account = AccountSharedData::new(42, 0, &Pubkey::new_unique());
|
||||||
|
let initial_data = vec![42; size];
|
||||||
|
account.set_data(initial_data);
|
||||||
|
|
||||||
|
let new_data = (0..10)
|
||||||
|
.map(|i| {
|
||||||
|
let mut data = vec![42; size];
|
||||||
|
data.resize(size + (i * MAX_PERMITTED_DATA_INCREASE), 42);
|
||||||
|
data
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut new_data = new_data.iter().cycle();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
account.set_data_from_slice(new_data.next().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_shrink(bencher: &mut Bencher, size: usize) {
|
||||||
|
let mut account = AccountSharedData::new(42, 0, &Pubkey::new_unique());
|
||||||
|
let initial_data = vec![42; size];
|
||||||
|
account.set_data(initial_data);
|
||||||
|
|
||||||
|
let new_data = (0..10)
|
||||||
|
.map(|i| {
|
||||||
|
let mut data = vec![42; size];
|
||||||
|
data.resize(size + (i * MAX_PERMITTED_DATA_INCREASE), 42);
|
||||||
|
data
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut new_data = new_data.iter().rev().cycle();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
account.set_data_from_slice(new_data.next().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_unchanged_1k(b: &mut Bencher) {
|
||||||
|
bench_unchanged(b, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_unchanged_100k(b: &mut Bencher) {
|
||||||
|
bench_unchanged(b, 1024 * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_unchanged_1mb(b: &mut Bencher) {
|
||||||
|
bench_unchanged(b, 1024 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_unchanged_10mb(b: &mut Bencher) {
|
||||||
|
bench_unchanged(b, 1024 * 1024 * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_changed_1k(b: &mut Bencher) {
|
||||||
|
bench_changed(b, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_changed_100k(b: &mut Bencher) {
|
||||||
|
bench_changed(b, 1024 * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_changed_1mb(b: &mut Bencher) {
|
||||||
|
bench_changed(b, 1024 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_changed_10mb(b: &mut Bencher) {
|
||||||
|
bench_changed(b, 1024 * 1024 * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_grow_1k(b: &mut Bencher) {
|
||||||
|
bench_grow(b, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_grow_100k(b: &mut Bencher) {
|
||||||
|
bench_grow(b, 1024 * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_grow_1mb(b: &mut Bencher) {
|
||||||
|
bench_grow(b, 1024 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_grow_10mb(b: &mut Bencher) {
|
||||||
|
bench_grow(b, 1024 * 1024 * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_shrink_100k(b: &mut Bencher) {
|
||||||
|
bench_shrink(b, 1024 * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_shrink_1mb(b: &mut Bencher) {
|
||||||
|
bench_shrink(b, 1024 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_set_data_from_slice_shrink_10mb(b: &mut Bencher) {
|
||||||
|
bench_shrink(b, 1024 * 1024 * 10)
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ use {
|
||||||
solana_program::{account_info::AccountInfo, debug_account_data::*, sysvar::Sysvar},
|
solana_program::{account_info::AccountInfo, debug_account_data::*, sysvar::Sysvar},
|
||||||
std::{
|
std::{
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
fmt,
|
fmt, ptr,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
},
|
},
|
||||||
|
@ -538,17 +538,50 @@ impl Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountSharedData {
|
impl AccountSharedData {
|
||||||
pub fn set_data_from_slice(&mut self, data: &[u8]) {
|
pub fn set_data_from_slice(&mut self, new_data: &[u8]) {
|
||||||
let len = self.data.len();
|
let data = match Arc::get_mut(&mut self.data) {
|
||||||
let len_different = len != data.len();
|
// The buffer isn't shared, so we're going to memcpy in place.
|
||||||
let different = len_different || data != &self.data[..];
|
Some(data) => data,
|
||||||
if different {
|
// If the buffer is shared, the cheapest thing to do is to clone the
|
||||||
self.data = Arc::new(data.to_vec());
|
// incoming slice and replace the buffer.
|
||||||
}
|
None => return self.set_data(new_data.to_vec()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_len = new_data.len();
|
||||||
|
|
||||||
|
// Reserve additional capacity if needed. Here we make the assumption
|
||||||
|
// that growing the current buffer is cheaper than doing a whole new
|
||||||
|
// allocation to make `new_data` owned.
|
||||||
|
//
|
||||||
|
// This assumption holds true during CPI, especially when the account
|
||||||
|
// size doesn't change but the account is only changed in place. And
|
||||||
|
// it's also true when the account is grown by a small margin (the
|
||||||
|
// realloc limit is quite low), in which case the allocator can just
|
||||||
|
// update the allocation metadata without moving.
|
||||||
|
//
|
||||||
|
// Shrinking and copying in place is always faster than making
|
||||||
|
// `new_data` owned, since shrinking boils down to updating the Vec's
|
||||||
|
// length.
|
||||||
|
|
||||||
|
data.reserve(new_len.saturating_sub(data.len()));
|
||||||
|
|
||||||
|
// Safety:
|
||||||
|
// We just reserved enough capacity. We set data::len to 0 to avoid
|
||||||
|
// possible UB on panic (dropping uninitialized elements), do the copy,
|
||||||
|
// finally set the new length once everything is initialized.
|
||||||
|
#[allow(clippy::uninit_vec)]
|
||||||
|
// this is a false positive, the lint doesn't currently special case set_len(0)
|
||||||
|
unsafe {
|
||||||
|
data.set_len(0);
|
||||||
|
ptr::copy_nonoverlapping(new_data.as_ptr(), data.as_mut_ptr(), new_len);
|
||||||
|
data.set_len(new_len);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_data(&mut self, data: Vec<u8>) {
|
pub fn set_data(&mut self, data: Vec<u8>) {
|
||||||
self.data = Arc::new(data);
|
self.data = Arc::new(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
|
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
|
||||||
shared_new(lamports, space, owner)
|
shared_new(lamports, space, owner)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue