Compare commits

...

1 commit

Author SHA1 Message Date
Orion Henry
684cd7a46c insert query caching 2022-06-11 15:43:33 +02:00
6 changed files with 143 additions and 22 deletions

View file

@ -160,6 +160,30 @@ impl OpSetInternal {
self.length
}
pub(crate) fn hint_clear(&mut self, obj: &ObjId) {
if let Some(tree) = self.trees.get_mut(obj) {
tree.internal.cache.clear();
}
}
pub(crate) fn hint_delete(&mut self, pos: usize, obj: &ObjId) {
if let Some(tree) = self.trees.get_mut(obj) {
tree.internal.cache.delete(pos);
}
}
pub(crate) fn hint_shift(&mut self, index: Option<usize>, pos: usize, obj: &ObjId) {
if let Some(tree) = self.trees.get_mut(obj) {
tree.internal.cache.shift(index, pos);
}
}
pub(crate) fn hint_insert(&mut self, index: usize, pos: usize, obj: &ObjId, element: &Op) {
if let Some(tree) = self.trees.get_mut(obj) {
tree.internal.cache.insert(index, pos, element);
}
}
pub(crate) fn insert(&mut self, index: usize, obj: &ObjId, element: Op) {
if let OpType::Make(typ) = element.action {
self.trees.insert(
@ -180,6 +204,8 @@ impl OpSetInternal {
}
pub(crate) fn insert_op(&mut self, obj: &ObjId, op: Op) -> Op {
self.hint_clear(obj);
let q = self.search(obj, query::SeekOp::new(&op));
let succ = q.succ;
@ -201,6 +227,9 @@ impl OpSetInternal {
op: Op,
observer: &mut Obs,
) -> Op {
// FIXME
self.hint_clear(obj);
let q = self.search(obj, query::SeekOpWithPatch::new(&op));
let query::SeekOpWithPatch {

View file

@ -11,16 +11,77 @@ use crate::{
query::{self, Index, QueryResult, ReplaceArgs, TreeQuery},
};
use crate::{
types::{ObjId, Op, OpId},
types::{Key, ObjId, Op, OpId},
ObjType,
};
use std::collections::HashSet;
use std::collections::{HashSet, VecDeque};
pub(crate) const B: usize = 16;
mod iter;
pub(crate) use iter::OpTreeIter;
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ListCachePoint {
pub(crate) index: usize, // list index
pub(crate) pos: usize, // op tree position
pub(crate) key: Key,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub(crate) struct QueryCache {
index: VecDeque<ListCachePoint>,
}
const CACHE_MAX: usize = 4;
impl QueryCache {
pub(crate) fn find(&self, index: usize) -> Option<&ListCachePoint> {
self.index.iter().find(|c| c.index == index)
}
pub(crate) fn insert(&mut self, index: usize, pos: usize, op: &Op) {
for c in &mut self.index {
if c.pos >= pos {
c.pos += 1;
c.index += 1;
}
}
if self.index.len() >= CACHE_MAX {
self.index.pop_front();
}
self.index.push_back(ListCachePoint {
index,
pos,
key: op.elemid_or_key(),
});
}
pub(crate) fn clear(&mut self) {
self.index.truncate(0)
}
pub(crate) fn delete(&mut self, pos: usize) {
for c in &mut self.index {
if c.pos >= pos {
c.index -= 1;
}
}
self.index.retain(|c| c.pos + 1 != pos);
}
pub(crate) fn shift(&mut self, index: Option<usize>, pos: usize) {
for c in &mut self.index {
if c.pos >= pos {
c.pos += 1;
}
}
if let Some(index) = index {
self.index.retain(|c| c.index != index);
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct OpTree {
pub(crate) internal: OpTreeInternal,
@ -46,6 +107,7 @@ impl OpTree {
#[derive(Clone, Debug)]
pub(crate) struct OpTreeInternal {
pub(crate) root_node: Option<OpTreeNode>,
pub(crate) cache: QueryCache,
}
#[derive(Clone, Debug)]
@ -59,7 +121,10 @@ pub(crate) struct OpTreeNode {
impl OpTreeInternal {
/// Construct a new, empty, sequence.
pub(crate) fn new() -> Self {
Self { root_node: None }
Self {
root_node: None,
cache: Default::default(),
}
}
/// Get the length of the sequence.
@ -121,13 +186,15 @@ impl OpTreeInternal {
where
Q: TreeQuery<'a>,
{
self.root_node
.as_ref()
.map(|root| match query.query_node_with_metadata(root, m) {
QueryResult::Descend => root.search(&mut query, m, None),
QueryResult::Skip(skip) => root.search(&mut query, m, Some(skip)),
_ => true,
});
if !query.read_cache(&self.cache) {
self.root_node
.as_ref()
.map(|root| match query.query_node_with_metadata(root, m) {
QueryResult::Descend => root.search(&mut query, m, None),
QueryResult::Skip(skip) => root.search(&mut query, m, Some(skip)),
_ => true,
});
}
query
}

View file

@ -1,4 +1,4 @@
use crate::op_tree::{OpSetMetadata, OpTreeNode};
use crate::op_tree::{OpSetMetadata, OpTreeNode, QueryCache};
use crate::types::{Clock, Counter, Key, Op, OpId, OpType, ScalarValue};
use fxhash::FxBuildHasher;
use std::cmp::Ordering;
@ -85,6 +85,12 @@ pub(crate) trait TreeQuery<'a> {
fn query_element(&mut self, _element: &'a Op) -> QueryResult {
panic!("invalid element query")
}
fn read_cache(&mut self, _cache: &QueryCache) -> bool {
false
}
fn update_cache(&mut self, _cache: &mut QueryCache) {}
}
#[derive(Debug, Clone, PartialEq)]

View file

@ -1,6 +1,6 @@
use crate::error::AutomergeError;
use crate::op_tree::OpTreeNode;
use crate::query::{QueryResult, TreeQuery};
use crate::query::{QueryCache, QueryResult, TreeQuery};
use crate::types::{ElemId, Key, Op, HEAD};
use std::fmt::Debug;
@ -46,16 +46,6 @@ impl InsertNth {
pub(crate) fn key(&self) -> Result<Key, AutomergeError> {
self.last_valid_insert
.ok_or(AutomergeError::InvalidIndex(self.target))
//if self.target == 0 {
/*
if self.last_insert.is_none() {
Ok(HEAD.into())
} else if self.seen == self.target && self.last_insert.is_some() {
Ok(Key::Seq(self.last_insert.unwrap()))
} else {
Err(AutomergeError::InvalidIndex(self.target))
}
*/
}
}
@ -110,4 +100,18 @@ impl<'a> TreeQuery<'a> for InsertNth {
self.n += 1;
QueryResult::Next
}
// AXIOM: ListCachePoint is only for single item inserts
// remove cache points on update
fn read_cache(&mut self, cache: &QueryCache) -> bool {
if self.target > 0 {
if let Some(c) = cache.find(self.target - 1) {
self.last_valid_insert = Some(c.key);
self.valid = Some(c.pos + 1);
return true;
}
}
false
}
}

View file

@ -76,6 +76,7 @@ impl TransactionInner {
let num = self.pending_ops();
// remove in reverse order so sets are removed before makes etc...
for (obj, _prop, op) in self.operations.into_iter().rev() {
doc.ops.hint_clear(&obj);
for pred_id in &op.pred {
if let Some(p) = doc.ops.search(&obj, OpIdSearch::new(*pred_id)).index() {
doc.ops.replace(&obj, p, |o| o.remove_succ(&op));
@ -169,7 +170,10 @@ impl TransactionInner {
}
if !op.is_delete() {
doc.ops.hint_shift((&prop).into(), pos, &obj);
doc.ops.insert(pos, &obj, op.clone());
} else {
doc.ops.hint_delete(pos, &obj)
}
self.operations.push((obj, prop, op));
@ -223,6 +227,8 @@ impl TransactionInner {
insert: true,
};
let pos = query.pos();
doc.ops.hint_insert(index, pos, &obj, &op);
doc.ops.insert(query.pos(), &obj, op.clone());
self.operations.push((obj, Prop::Seq(index), op));

View file

@ -575,3 +575,12 @@ impl From<Prop> for wasm_bindgen::JsValue {
}
}
}
impl From<&Prop> for Option<usize> {
fn from(prop: &Prop) -> Self {
match prop {
Prop::Map(_) => None,
Prop::Seq(index) => Some(*index),
}
}
}