Add simplisitc global modification caching

This commit is contained in:
Aleksey Kladov 2018-09-10 21:53:33 +03:00
parent 3ae3b3eb06
commit db14b4270c
3 changed files with 111 additions and 31 deletions

View file

@ -2,50 +2,95 @@ use std::{
hash::Hash, hash::Hash,
sync::Arc, sync::Arc,
cell::RefCell, cell::RefCell,
fmt::Debug,
}; };
use parking_lot::Mutex;
use libsyntax2::{File}; use libsyntax2::{File};
use im; use im;
use { use {
FileId, FileId,
imp::{FileResolverImp}, imp::{FileResolverImp},
module_map_db::ModuleDescr,
}; };
#[derive(Clone)] #[derive(Debug)]
pub(crate) struct Db { pub(crate) struct DbHost {
file_resolver: FileResolverImp, db: Arc<Db>,
files: im::HashMap<FileId, Arc<String>>,
} }
impl Db { impl DbHost {
pub(crate) fn new() -> Db { pub(crate) fn new() -> DbHost {
Db { let db = Db {
file_resolver: FileResolverImp::default(), file_resolver: FileResolverImp::default(),
files: im::HashMap::new(), files: im::HashMap::new(),
} cache: Mutex::new(Cache::new())
};
DbHost { db: Arc::new(db) }
} }
pub(crate) fn change_file(&mut self, file_id: FileId, text: Option<String>) { pub(crate) fn change_file(&mut self, file_id: FileId, text: Option<String>) {
let db = self.db_mut();
match text { match text {
None => { None => {
self.files.remove(&file_id); db.files.remove(&file_id);
} }
Some(text) => { Some(text) => {
self.files.insert(file_id, Arc::new(text)); db.files.insert(file_id, Arc::new(text));
} }
} }
} }
pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) {
self.file_resolver = file_resolver let db = self.db_mut();
db.file_resolver = file_resolver
} }
pub(crate) fn query_ctx(&self) -> QueryCtx { pub(crate) fn query_ctx(&self) -> QueryCtx {
QueryCtx { QueryCtx {
db: self.clone(), db: Arc::clone(&self.db),
trace: RefCell::new(Vec::new()), trace: RefCell::new(Vec::new()),
} }
} }
fn db_mut(&mut self) -> &mut Db {
// NB: this "forks" the database & clears the cache
let db = Arc::make_mut(&mut self.db);
*db.cache.get_mut() = Default::default();
db
}
}
#[derive(Debug)]
pub(crate) struct Db {
file_resolver: FileResolverImp,
files: im::HashMap<FileId, Arc<String>>,
cache: Mutex<Cache>,
}
impl Clone for Db {
fn clone(&self) -> Db {
Db {
file_resolver: self.file_resolver.clone(),
files: self.files.clone(),
cache: Mutex::new(Cache::new()),
}
}
}
#[derive(Clone, Default, Debug)]
pub(crate) struct Cache {
pub(crate) module_descr: QueryCache<ModuleDescr>
}
#[allow(type_alias_bounds)]
pub(crate) type QueryCache<Q: Query> = im::HashMap<
<Q as Query>::Params,
<Q as Query>::Output
>;
impl Cache {
fn new() -> Cache {
Default::default()
}
} }
pub(crate) struct QueryCtx { pub(crate) struct QueryCtx {
db: Db, db: Arc<Db>,
pub(crate) trace: RefCell<Vec<TraceEvent>>, pub(crate) trace: RefCell<Vec<TraceEvent>>,
} }
@ -62,9 +107,7 @@ pub(crate) enum TraceEventKind {
impl QueryCtx { impl QueryCtx {
pub(crate) fn get<Q: Get>(&self, params: &Q::Params) -> Q::Output { pub(crate) fn get<Q: Get>(&self, params: &Q::Params) -> Q::Output {
self.trace(TraceEvent { query_id: Q::ID, kind: TraceEventKind::Start });
let res = Q::get(self, params); let res = Q::get(self, params);
self.trace(TraceEvent { query_id: Q::ID, kind: TraceEventKind::Finish });
res res
} }
fn trace(&self, event: TraceEvent) { fn trace(&self, event: TraceEvent) {
@ -74,26 +117,55 @@ impl QueryCtx {
pub(crate) trait Query { pub(crate) trait Query {
const ID: u32; const ID: u32;
type Params: Hash; type Params: Hash + Eq + Debug;
type Output; type Output: Debug;
} }
pub(crate) trait Get: Query { pub(crate) trait Get: Query {
fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output; fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output;
} }
impl<T: Eval> Get for T { impl<T: Eval> Get for T
where
T::Params: Clone,
T::Output: Clone,
{
fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output { fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output {
Self::eval(ctx, params) {
let mut cache = ctx.db.cache.lock();
if let Some(cache) = Self::cache(&mut cache) {
if let Some(res) = cache.get(params) {
return res.clone();
}
}
}
ctx.trace(TraceEvent { query_id: Self::ID, kind: TraceEventKind::Start });
let res = Self::eval(ctx, params);
ctx.trace(TraceEvent { query_id: Self::ID, kind: TraceEventKind::Finish });
let mut cache = ctx.db.cache.lock();
if let Some(cache) = Self::cache(&mut cache) {
cache.insert(params.clone(), res.clone());
}
res
} }
} }
pub(crate) trait Eval: Query { pub(crate) trait Eval: Query
where
Self::Params: Clone,
Self::Output: Clone,
{
fn cache(_cache: &mut Cache) -> Option<&mut QueryCache<Self>> {
None
}
fn eval(ctx: &QueryCtx, params: &Self::Params) -> Self::Output; fn eval(ctx: &QueryCtx, params: &Self::Params) -> Self::Output;
} }
#[derive(Debug)]
pub(crate) struct DbFiles { pub(crate) struct DbFiles {
db: Db, db: Arc<Db>,
} }
impl DbFiles { impl DbFiles {
@ -113,7 +185,7 @@ impl Query for Files {
} }
impl Get for Files { impl Get for Files {
fn get(ctx: &QueryCtx, _params: &()) -> DbFiles { fn get(ctx: &QueryCtx, _params: &()) -> DbFiles {
DbFiles { db: ctx.db.clone() } DbFiles { db: Arc::clone(&ctx.db) }
} }
} }

View file

@ -13,7 +13,7 @@ extern crate im;
mod symbol_index; mod symbol_index;
mod module_map; mod module_map;
mod module_map_db; pub(crate) mod module_map_db;
mod imp; mod imp;
mod job; mod job;
mod roots; mod roots;

View file

@ -1,11 +1,14 @@
use std::sync::Arc; use std::sync::Arc;
use { use {
FileId, FileId,
db::{Query, Eval, QueryCtx, FileSyntax, Files}, db::{
Query, Eval, QueryCtx, FileSyntax, Files,
Cache, QueryCache,
},
module_map::resolve_submodule, module_map::resolve_submodule,
}; };
enum ModuleDescr {} pub(crate) enum ModuleDescr {}
impl Query for ModuleDescr { impl Query for ModuleDescr {
const ID: u32 = 30; const ID: u32 = 30;
type Params = FileId; type Params = FileId;
@ -27,6 +30,9 @@ impl Query for ParentModule {
} }
impl Eval for ModuleDescr { impl Eval for ModuleDescr {
fn cache(cache: &mut Cache) -> Option<&mut QueryCache<Self>> {
Some(&mut cache.module_descr)
}
fn eval(ctx: &QueryCtx, file_id: &FileId) -> Arc<descr::ModuleDescr> { fn eval(ctx: &QueryCtx, file_id: &FileId) -> Arc<descr::ModuleDescr> {
let file = ctx.get::<FileSyntax>(file_id); let file = ctx.get::<FileSyntax>(file_id);
Arc::new(descr::ModuleDescr::new(file.ast())) Arc::new(descr::ModuleDescr::new(file.ast()))
@ -66,6 +72,7 @@ mod descr {
ast::{self, NameOwner}, ast::{self, NameOwner},
}; };
#[derive(Debug)]
pub struct ModuleDescr { pub struct ModuleDescr {
pub submodules: Vec<Submodule> pub submodules: Vec<Submodule>
} }
@ -85,7 +92,7 @@ mod descr {
ModuleDescr { submodules } } ModuleDescr { submodules } }
} }
#[derive(Clone, Hash)] #[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct Submodule { pub struct Submodule {
pub name: SmolStr, pub name: SmolStr,
} }
@ -98,7 +105,7 @@ mod tests {
use im; use im;
use relative_path::{RelativePath, RelativePathBuf}; use relative_path::{RelativePath, RelativePathBuf};
use { use {
db::{Query, Db, TraceEventKind}, db::{Query, DbHost, TraceEventKind},
imp::FileResolverImp, imp::FileResolverImp,
FileId, FileResolver, FileId, FileResolver,
}; };
@ -122,7 +129,7 @@ mod tests {
struct Fixture { struct Fixture {
next_file_id: u32, next_file_id: u32,
fm: im::HashMap<FileId, RelativePathBuf>, fm: im::HashMap<FileId, RelativePathBuf>,
db: Db, db: DbHost,
} }
impl Fixture { impl Fixture {
@ -130,7 +137,7 @@ mod tests {
Fixture { Fixture {
next_file_id: 1, next_file_id: 1,
fm: im::HashMap::new(), fm: im::HashMap::new(),
db: Db::new(), db: DbHost::new(),
} }
} }
fn add_file(&mut self, path: &str, text: &str) -> FileId { fn add_file(&mut self, path: &str, text: &str) -> FileId {
@ -185,10 +192,11 @@ mod tests {
fn test_parent_module() { fn test_parent_module() {
let mut f = Fixture::new(); let mut f = Fixture::new();
let foo = f.add_file("/foo.rs", ""); let foo = f.add_file("/foo.rs", "");
f.check_parent_modules(foo, &[], &[(FileSyntax::ID, 1)]); f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 1)]);
let lib = f.add_file("/lib.rs", "mod foo;"); let lib = f.add_file("/lib.rs", "mod foo;");
f.check_parent_modules(foo, &[lib], &[(FileSyntax::ID, 2)]); f.check_parent_modules(foo, &[lib], &[(ModuleDescr::ID, 2)]);
f.check_parent_modules(foo, &[lib], &[(ModuleDescr::ID, 0)]);
f.change_file(lib, ""); f.change_file(lib, "");
f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 2)]); f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 2)]);