introduce SweepStrategy

This commit is contained in:
Niko Matsakis 2018-10-25 05:47:25 -04:00
parent 74cecb6ea5
commit c21ea47cfc
7 changed files with 55 additions and 21 deletions

View file

@ -11,6 +11,7 @@ use crate::runtime::Runtime;
use crate::runtime::RuntimeId;
use crate::runtime::StampedValue;
use crate::Database;
use crate::SweepStrategy;
use log::{debug, info};
use parking_lot::Mutex;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
@ -790,7 +791,7 @@ where
DB: Database,
MP: MemoizationPolicy<DB, Q>,
{
fn sweep(&self, db: &DB) {
fn sweep(&self, db: &DB, strategy: SweepStrategy) {
let mut map_write = self.map.write();
let revision_now = db.salsa_runtime().current_revision();
map_write.retain(|key, query_state| {
@ -820,6 +821,10 @@ where
// when we read `revision_now`.
assert!(memo.verified_at <= revision_now);
if !strategy.keep_values {
memo.value = None;
}
memo.verified_at == revision_now
}
}

View file

@ -8,6 +8,7 @@ use crate::runtime::Revision;
use crate::runtime::StampedValue;
use crate::Database;
use crate::Query;
use crate::SweepStrategy;
use log::debug;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use rustc_hash::FxHashMap;
@ -215,7 +216,7 @@ where
DB: Database,
Q::Value: Default,
{
fn sweep(&self, _db: &DB) {}
fn sweep(&self, _db: &DB, _strategy: SweepStrategy) {}
}
impl<DB, Q> InputQueryStorageOps<DB, Q> for InputStorage<DB, Q>

View file

@ -47,8 +47,8 @@ pub trait Database: plumbing::DatabaseStorageTypes + plumbing::DatabaseOps {
/// threads that would be performing a `set`).
///
/// [`lock_revision`]: struct.Runtime.html#method.lock_revision
fn sweep_all(&self) {
self.salsa_runtime().sweep_all(self);
fn sweep_all(&self, strategy: SweepStrategy) {
self.salsa_runtime().sweep_all(self, strategy);
}
/// Get access to extra methods pertaining to a given query,
@ -63,6 +63,33 @@ pub trait Database: plumbing::DatabaseStorageTypes + plumbing::DatabaseOps {
}
}
/// The sweep strategy controls what data we will keep/discard when we
/// do a GC-sweep. The default (`SweepStrategy::default`) is to keep
/// all memoized values used in the current revision.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SweepStrategy {
keep_values: bool,
}
impl SweepStrategy {
/// Causes us to discard memoized *values* but keep the
/// *dependencies*. This means you will have to recompute the
/// results from any queries you execute but does permit you to
/// quickly determine if a value is still up to date.
pub fn discard_values(self) -> SweepStrategy {
SweepStrategy {
keep_values: false,
..self
}
}
}
impl Default for SweepStrategy {
fn default() -> Self {
SweepStrategy { keep_values: true }
}
}
/// Indicates a database that also supports parallel query
/// evaluation. All of Salsa's base query support is capable of
/// parallel execution, but for it to work, your query key/value types
@ -108,11 +135,11 @@ where
})
}
pub fn sweep(&self)
pub fn sweep(&self, strategy: SweepStrategy)
where
Q::Storage: plumbing::QueryStorageMassOps<DB>,
{
self.storage.sweep(self.db);
self.storage.sweep(self.db, strategy);
}
/// Assign a value to an "input query". Must be used outside of

View file

@ -1,6 +1,7 @@
use crate::Database;
use crate::Query;
use crate::QueryTable;
use crate::SweepStrategy;
use std::fmt::Debug;
use std::hash::Hash;
@ -41,7 +42,7 @@ pub trait DatabaseOps: Sized {
/// query, unlike `QueryStorageOps`).
pub trait QueryStorageMassOps<DB: Database> {
/// Discards memoized values that are not up to date with the current revision.
fn sweep(&self, db: &DB);
fn sweep(&self, db: &DB, strategy: SweepStrategy);
}
pub trait QueryDescriptor<DB>: Clone + Debug + Eq + Hash + Send + Sync {

View file

@ -1,4 +1,4 @@
use crate::Database;
use crate::{Database, SweepStrategy};
use lock_api::RawRwLock;
use log::debug;
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard};
@ -94,13 +94,13 @@ where
}
/// Default implementation for `Database::sweep_all`.
pub fn sweep_all(&self, db: &DB) {
pub fn sweep_all(&self, db: &DB, strategy: SweepStrategy) {
// Note that we do not acquire the query lock (or any locks)
// here. Each table is capable of sweeping itself atomically
// and there is no need to bring things to a halt. That said,
// users may wish to guarantee atomicity.
db.for_each_query(|query_storage| query_storage.sweep(db));
db.for_each_query(|query_storage| query_storage.sweep(db, strategy));
}
/// Indicates that a derived query has begun to execute; if this is the

View file

@ -1,7 +1,7 @@
use crate::db;
use crate::group::*;
use salsa::debug::DebugQueryTable;
use salsa::Database;
use salsa::{Database, SweepStrategy};
macro_rules! assert_keys {
($db:expr, $($query:expr => ($($key:expr),*),)*) => {
@ -34,7 +34,7 @@ fn compute_one() {
// Memoized, but will compute fibonacci(5) again
db.compute(5);
db.sweep_all();
db.sweep_all(SweepStrategy::default());
assert_keys! {
db,
@ -72,7 +72,7 @@ fn compute_switch() {
Max => (),
}
db.sweep_all();
db.sweep_all(SweepStrategy::default());
// Now we just have `Triangular` and not `Fibonacci`
assert_keys! {
@ -88,7 +88,7 @@ fn compute_switch() {
// Now run `compute` *again* in next revision.
db.salsa_runtime().next_revision();
assert_eq!(db.compute(5), 15);
db.sweep_all();
db.sweep_all(SweepStrategy::default());
// We keep triangular, but just the outermost one.
assert_keys! {
@ -117,7 +117,7 @@ fn compute_all() {
db.compute_all();
db.salsa_runtime().next_revision();
db.compute_all();
db.sweep_all();
db.sweep_all(SweepStrategy::default());
assert_keys! {
db,
@ -145,7 +145,7 @@ fn compute_all() {
Max => (()),
}
db.sweep_all();
db.sweep_all(SweepStrategy::default());
// We no longer used `Compute(5)` and `Triangular(5)`; note that
// `UseTriangular(5)` is not collected, as it is an input.

View file

@ -1,7 +1,7 @@
use crate::db;
use crate::group::{Fibonacci, GcDatabase};
use salsa::debug::DebugQueryTable;
use salsa::Database;
use salsa::{Database, SweepStrategy};
// For constant values (like `fibonacci`), we only keep the values
// that were used in the latest revision, not the sub-values that
@ -18,7 +18,7 @@ fn one_rev() {
// Everything was used in this revision, so
// nothing gets collected.
db.sweep_all();
db.sweep_all(SweepStrategy::default());
assert_eq!(k.len(), 6);
}
@ -35,7 +35,7 @@ fn two_rev_nothing() {
// Nothing was used in this revision, so
// everything gets collected.
db.sweep_all();
db.sweep_all(SweepStrategy::default());
let k: Vec<_> = db.query(Fibonacci).keys();
assert_eq!(k.len(), 0);
@ -56,7 +56,7 @@ fn two_rev_one_use() {
// fibonacci is a constant, so it will not be invalidated,
// hence we keep `fibonacci(5)` but remove 0..=4.
db.sweep_all();
db.sweep_all(SweepStrategy::default());
let k: Vec<_> = db.query(Fibonacci).keys();
assert_eq!(k, vec![5]);
@ -78,7 +78,7 @@ fn two_rev_two_uses() {
// fibonacci is a constant, so it will not be invalidated,
// hence we keep 3 and 5 but remove the rest.
db.sweep_all();
db.sweep_all(SweepStrategy::default());
let mut k: Vec<_> = db.query(Fibonacci).keys();
k.sort();