Skip to content

Commit

Permalink
fix store caching
Browse files Browse the repository at this point in the history
  • Loading branch information
slandymani committed Jul 13, 2024
1 parent e8e0bcf commit 515b349
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 73 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "Apache-2.0"
name = "owasm-vm"
readme = "README.md"
repository = "https://github.com/ODIN-PROTOCOL/owasm/tree/master/packages/vm"
version = "0.4.0"
version = "0.4.1"

[dependencies]
assert_matches = "1.3.0"
Expand Down
134 changes: 97 additions & 37 deletions packages/vm/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,36 @@ use std::{
sync::{Arc, RwLock},
};

use clru::CLruCache;
use wasmer::{Engine, Instance, Module, Store};

use crate::checksum::Checksum;
use crate::error::Error;
use crate::imports::create_import_object;
use crate::vm::{Environment, Querier};

use clru::CLruCache;
use wasmer::{Instance, Module, Store};
#[derive(Debug, Clone)]
pub struct CachedModule {
pub module: Module,
pub engine: Engine,
}

/// An in-memory module cache
pub struct InMemoryCache {
modules: CLruCache<Checksum, Module>,
modules: CLruCache<Checksum, CachedModule>,
}

impl InMemoryCache {
pub fn new(max_entries: u32) -> Self {
InMemoryCache { modules: CLruCache::new(max_entries as usize) }
}

pub fn store(&mut self, checksum: &Checksum, module: Module) -> Option<Module> {
pub fn store(&mut self, checksum: &Checksum, module: CachedModule) -> Option<CachedModule> {
self.modules.put(*checksum, module)
}

/// Looks up a module in the cache and creates a new module
pub fn load(&mut self, checksum: &Checksum) -> Option<Module> {
pub fn load(&mut self, checksum: &Checksum) -> Option<CachedModule> {
self.modules.get(checksum).cloned()
}
}
Expand Down Expand Up @@ -54,39 +62,90 @@ impl Cache {
callback(in_memory_cache)
}

pub fn get_instance(
pub fn get_instance<Q>(
&mut self,
wasm: &[u8],
store: &mut Store,
import_object: &wasmer::Imports,
) -> Result<(wasmer::Instance, bool), Error> {
engine: Engine,
owasm_env: Environment<Q>,
) -> Result<(Instance, Store, bool), Error>
where
Q: Querier + 'static,
{
let checksum = Checksum::generate(wasm);
self.with_in_memory_cache(|in_memory_cache| {
// lookup cache
if let Some(module) = in_memory_cache.load(&checksum) {
return Ok((Instance::new(store, &module, &import_object).unwrap(), true));
if let Some(cached_module) = in_memory_cache.load(&checksum) {
let mut store = Store::new(cached_module.engine);
let import_object = create_import_object(&mut store, owasm_env);
return Ok((Instance::new(&mut store, &cached_module.module, &import_object).unwrap(), store, true));
}

// recompile
let module = Module::new(store, &wasm).map_err(|_| Error::InstantiationError)?;
let mut store = Store::new(engine.clone());
let import_object = create_import_object(&mut store, owasm_env);
let module = Module::new(&mut store, &wasm).map_err(|_| Error::InstantiationError)?;
let instance =
Instance::new(store, &module, &import_object).map_err(|_| Error::InstantiationError)?;
Instance::new(&mut store, &module, &import_object).map_err(|_| Error::InstantiationError)?;

in_memory_cache.store(&checksum, module);
in_memory_cache.store(&checksum, CachedModule{
module,
engine,
});

Ok((instance, false))
Ok((instance, store, false))
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, Write};
use std::process::Command;

use tempfile::NamedTempFile;
use wasmer::imports;
use crate::store::make_store;

use crate::store::make_engine;
use crate::vm::Querier;

use super::*;

pub struct MockQuerier {}

impl Querier for MockQuerier {
fn get_span_size(&self) -> i64 {
300
}
fn get_calldata(&self) -> Result<Vec<u8>, Error> {
Ok(vec![1])
}
fn set_return_data(&self, _: &[u8]) -> Result<(), Error> {
Ok(())
}
fn get_ask_count(&self) -> i64 {
10
}
fn get_min_count(&self) -> i64 {
8
}
fn get_prepare_time(&self) -> i64 {
100_000
}
fn get_execute_time(&self) -> Result<i64, Error> {
Ok(100_000)
}
fn get_ans_count(&self) -> Result<i64, Error> {
Ok(8)
}
fn ask_external_data(&self, _: i64, _: i64, _: &[u8]) -> Result<(), Error> {
Ok(())
}
fn get_external_data_status(&self, _: i64, _: i64) -> Result<i64, Error> {
Ok(1)
}
fn get_external_data(&self, _: i64, _: i64) -> Result<Vec<u8>, Error> {
Ok(vec![1])
}
}

fn wat2wasm(wat: impl AsRef<[u8]>) -> Vec<u8> {
let mut input_file = NamedTempFile::new().unwrap();
Expand All @@ -105,12 +164,13 @@ mod tests {
wasm
}

fn get_instance_without_err(cache: &mut Cache, wasm: &[u8]) -> (wasmer::Instance, bool) {
let mut store = make_store();
let import_object = imports! {};
fn get_instance_without_err(cache: &mut Cache, wasm: &[u8]) -> (Instance, Store, bool) {
let engine = make_engine();
let querier = MockQuerier {};
let env = Environment::new(querier);

match cache.get_instance(&wasm, &mut store, &import_object) {
Ok((instance, is_hit)) => (instance, is_hit),
match cache.get_instance(&wasm, engine, env) {
Ok((instance, store, is_hit)) => (instance, store, is_hit),
Err(_) => panic!("Fail to get instance"),
}
}
Expand All @@ -133,19 +193,19 @@ mod tests {
)"#,
);

let (instance1, is_hit) = get_instance_without_err(&mut cache, &wasm);
let (instance1, _, is_hit) = get_instance_without_err(&mut cache, &wasm);
assert_eq!(false, is_hit);

let (instance2, is_hit) = get_instance_without_err(&mut cache, &wasm);
let (instance2, _, is_hit) = get_instance_without_err(&mut cache, &wasm);
assert_eq!(true, is_hit);

let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm2);
assert_eq!(false, is_hit);

let (_, is_hit) = get_instance_without_err(&mut cache, &wasm);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm);
assert_eq!(true, is_hit);

let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm2);
assert_eq!(true, is_hit);

let ser1 = match instance1.module().serialize() {
Expand Down Expand Up @@ -189,39 +249,39 @@ mod tests {
);

// miss [_ _] => [1 _]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm1);
assert_eq!(false, is_hit);

// miss [1 _] => [2 1]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm2);
assert_eq!(false, is_hit);

// miss [2 1] => [3 2]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm3);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm3);
assert_eq!(false, is_hit);

// hit [3 2] => [2 3]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm2);
assert_eq!(true, is_hit);

// miss [2 3] => [1 2]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm1);
assert_eq!(false, is_hit);

// hit [1 2] => [2 1]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm2);
assert_eq!(true, is_hit);

// miss [2 1] => [3 2]
let (_, is_hit) = get_instance_without_err(&mut cache, &wasm3);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm3);
assert_eq!(false, is_hit);

cache = Cache::new(CacheOptions { cache_size: 0 });

let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm1);
assert_eq!(false, is_hit);

let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
let (_, _, is_hit) = get_instance_without_err(&mut cache, &wasm1);
assert_eq!(false, is_hit);
}
}
25 changes: 13 additions & 12 deletions packages/vm/src/calls.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::ptr::NonNull;

use wasmer_middlewares::metering::{get_remaining_points, MeteringPoints};

use crate::cache::Cache;
use crate::error::Error;
use crate::imports::create_import_object;
use crate::store::make_store;
use crate::store::make_engine;
use crate::vm::{Environment, Querier};

use std::ptr::NonNull;
use wasmer_middlewares::metering::{get_remaining_points, MeteringPoints};

pub fn run<Q>(
cache: &mut Cache,
code: &[u8],
Expand All @@ -18,10 +18,9 @@ where
Q: Querier + 'static,
{
let owasm_env = Environment::new(querier);
let mut store = make_store();
let import_object = create_import_object(&mut store, owasm_env.clone());
let engine = make_engine();

let (instance, _) = cache.get_instance(code, &mut store, &import_object)?;
let (instance, mut store, _) = cache.get_instance(code, engine, owasm_env.clone())?;
let instance_ptr = NonNull::from(&instance);
owasm_env.set_wasmer_instance(Some(instance_ptr));
owasm_env.set_gas_left(&mut store, gas_limit);
Expand Down Expand Up @@ -54,14 +53,16 @@ where

#[cfg(test)]
mod tests {
use crate::cache::CacheOptions;

use super::*;
use crate::compile::compile;
use std::io::{Read, Write};
use std::process::Command;

use tempfile::NamedTempFile;

use crate::cache::CacheOptions;
use crate::compile::compile;

use super::*;

pub struct MockQuerier {}

impl Querier for MockQuerier {
Expand Down
9 changes: 3 additions & 6 deletions packages/vm/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ mod test {

use crate::cache::{Cache, CacheOptions};
use crate::compile::compile;
use crate::store::make_store;
use crate::store::{make_engine, make_store};

use std::io::{Read, Write};
use std::process::Command;
Expand Down Expand Up @@ -411,12 +411,9 @@ mod test {

let querier = MockQuerier {};
let owasm_env = Environment::new(querier);
let mut store = make_store();
let import_object = create_import_object(&mut store, owasm_env.clone());
let engine = make_engine();
let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
let (instance, _) = cache.get_instance(&code, &mut store, &import_object).unwrap();


let (instance, store, _) = cache.get_instance(&code, engine, owasm_env.clone()).unwrap();

return (owasm_env, instance, store);
}
Expand Down
8 changes: 8 additions & 0 deletions packages/vm/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ pub fn make_store() -> Store {
let engine: Engine = compiler.into();
Store::new(engine)
}

pub fn make_engine() -> Engine {
let mut compiler = Singlepass::new();
let metering = Arc::new(Metering::new(0, cost));
compiler.push_middleware(metering);
let engine: Engine = compiler.into();
engine
}
Loading

0 comments on commit 515b349

Please sign in to comment.