Commit 8cd94a35 authored by Andrew Jones's avatar Andrew Jones

Merge branch 'master' into aj-extra-metadata

parents f13b888f 68ac2124
Pipeline #99816 failed with stages
in 5 minutes and 32 seconds
......@@ -74,6 +74,10 @@ std = [
]
ink-fuzz-tests = ["std"]
[[bench]]
name = "bench_lazy"
harness = false
[[bench]]
name = "bench_vec"
harness = false
......
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use criterion::{
black_box,
criterion_group,
criterion_main,
BenchmarkId,
Criterion,
};
use ink_core::{
env,
storage2::{
lazy::Lazy,
traits::{
KeyPtr,
SpreadLayout,
},
},
};
use ink_primitives::Key;
criterion_group!(populated_cache, bench_set_populated_cache);
criterion_group!(empty_cache, bench_set_empty_cache,);
criterion_main!(populated_cache, empty_cache);
mod populated_cache {
use super::*;
use core::ops::DerefMut;
pub fn set() {
let mut lazy = <Lazy<i32>>::new(1);
let lazy_mut = black_box(&mut lazy);
black_box(Lazy::set(lazy_mut, 17));
}
pub fn deref_mut() {
let mut lazy = <Lazy<i32>>::new(1);
let i32_mut = black_box(lazy.deref_mut());
black_box(*i32_mut = 17);
}
}
fn bench_set_populated_cache(c: &mut Criterion) {
let mut group = c.benchmark_group("Compare: `set` and `deref_mut` (populated cache)");
group.bench_function(BenchmarkId::new("set", 0), |b| {
b.iter(|| populated_cache::set())
});
group.bench_function(BenchmarkId::new("deref_mut", 0), |b| {
b.iter(|| populated_cache::deref_mut())
});
group.finish();
}
/// Pushes a value to contract storage and creates a `Lazy` pointing to it.
fn push_storage_lazy(value: i32) -> Lazy<i32> {
let root_key = Key::from([0x00; 32]);
SpreadLayout::push_spread(&Lazy::new(value), &mut KeyPtr::from(root_key));
<Lazy<i32>>::lazy(root_key)
}
mod empty_cache {
use super::*;
pub fn set() {
let mut lazy = push_storage_lazy(1);
black_box(Lazy::set(&mut lazy, 13));
}
pub fn deref_mut() {
let mut lazy = push_storage_lazy(1);
black_box(*lazy = 13);
}
}
fn bench_set_empty_cache(c: &mut Criterion) {
let _ = env::test::run_test::<env::DefaultEnvTypes, _>(|_| {
let mut group = c.benchmark_group("Compare: `set` and `deref_mut` (empty cache)");
group.bench_function(BenchmarkId::new("set", 0), |b| {
b.iter(|| empty_cache::set())
});
group.bench_function(BenchmarkId::new("deref_mut", 0), |b| {
b.iter(|| empty_cache::deref_mut())
});
group.finish();
Ok(())
})
.unwrap();
}
......@@ -307,6 +307,35 @@ where
pub fn get_mut(&mut self) -> Option<&mut T> {
self.load_entry_mut().value_mut().into()
}
/// Sets the value in this cell to `value`, without executing any reads.
///
/// # Note
///
/// No reads from contract storage will be executed.
///
/// This method should be preferred over dereferencing or `get_mut`
/// in case the returned value is of no interest to the caller.
///
/// # Panics
///
/// If accessing the inner value fails.
#[inline]
pub fn set(&mut self, new_value: T) {
// SAFETY: This is critical because we mutably access the entry.
let cache = unsafe { &mut *self.cache.get_ptr().as_ptr() };
if let Some(cache) = cache.as_mut() {
// Cache is already populated we simply overwrite its already existing value.
cache.put(Some(new_value));
} else {
// Cache is empty, so we simply set the cache to the value.
// The key does not need to exist for this to work, we only need to
// write the value into the cache and are done. Writing to contract
// storage happens during setup/teardown of a contract.
*cache = Some(Entry::new(Some(new_value), EntryState::Mutated));
}
debug_assert!(cache.is_some());
}
}
#[cfg(test)]
......@@ -319,9 +348,12 @@ mod tests {
use crate::{
env,
env::test::run_test,
storage2::traits::{
KeyPtr,
SpreadLayout,
storage2::{
traits::{
KeyPtr,
SpreadLayout,
},
Lazy,
},
};
use ink_primitives::Key;
......@@ -416,4 +448,57 @@ mod tests {
Ok(())
})
}
#[test]
fn set_works() {
let mut cell = <LazyCell<i32>>::new(Some(1));
cell.set(23);
assert_eq!(cell.get(), Some(&23));
}
#[test]
fn lazy_set_works() -> env::Result<()> {
run_test::<env::DefaultEnvTypes, _>(|_| {
let mut cell = <LazyCell<u8>>::lazy(Key::from([0x42; 32]));
let value = cell.get();
assert_eq!(value, None);
cell.set(13);
assert_eq!(cell.get(), Some(&13));
Ok(())
})
}
#[test]
fn lazy_set_works_with_spread_layout_push_pull() -> env::Result<()> {
run_test::<env::DefaultEnvTypes, _>(|_| {
type MaybeValue = Option<u8>;
// Initialize a LazyCell with None and push it to `k`
let k = Key::from([0x00; 32]);
let val: MaybeValue = None;
SpreadLayout::push_spread(&Lazy::new(val), &mut KeyPtr::from(k));
// Pull another instance `v` from `k`, check that it is `None`
let mut v =
<Lazy<MaybeValue> as SpreadLayout>::pull_spread(&mut KeyPtr::from(k));
assert_eq!(*v, None);
// Set `v` using `set` to an actual value
let actual_value: MaybeValue = Some(13);
Lazy::set(&mut v, actual_value);
// Push `v` to `k`
SpreadLayout::push_spread(&v, &mut KeyPtr::from(k));
// Load `v2` from `k`
let v2 =
<Lazy<MaybeValue> as SpreadLayout>::pull_spread(&mut KeyPtr::from(k));
// Check that V2 is the set value
assert_eq!(*v2, Some(13));
Ok(())
})
}
}
......@@ -147,7 +147,7 @@ where
///
/// # Note
///
/// This loads the value from the contract storage if this did not happed before.
/// This loads the value from the contract storage if this did not happen before.
///
/// # Panics
///
......@@ -156,6 +156,23 @@ where
pub fn get_mut(lazy: &mut Self) -> &mut T {
lazy.cell.get_mut().expect("encountered empty storage cell")
}
/// Sets the value to `value`, without executing any reads.
///
/// # Note
///
/// No reads from contract storage will be executed.
///
/// This method should be preferred over dereferencing or `get_mut`
/// in case the returned value is of no interest to the caller.
///
/// # Panics
///
/// If accessing the inner value fails.
#[inline]
pub fn set(lazy: &mut Self, new_value: T) {
lazy.cell.set(new_value);
}
}
impl<T> From<T> for Lazy<T>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment