Skip to content

Commit

Permalink
[topo] Cleanup syntax, more docs, better benches.
Browse files Browse the repository at this point in the history
  • Loading branch information
anp committed Jun 20, 2019
1 parent 88b79ce commit e9761ca
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 168 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ chashmap = "2"
futures-preview = { version = "0.3.0-alpha.16", features = [ "async-await", "nightly" ] }
once_cell = "0.2"
parking_lot = "0.8"
tokio-trace = "0.1"
topo = { path = "topo" }

[dev-dependencies]
Expand Down
29 changes: 16 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//!
//! TODO

#![deny(clippy::all, missing_docs)]
#![deny(clippy::all, missing_docs, intra_doc_link_resolution_failure)]
#![feature(async_await, gen_future)]

#[macro_use]
Expand All @@ -29,7 +29,7 @@ pub use {memo::*, state::*};

use {
std::ops::Deref,
topo::__trace::{field::debug, *},
tokio_trace::{field::debug, *},
};

/// Controls the iteration behavior of a runloop. The default of `OnWake` will leave the runloop
Expand Down Expand Up @@ -114,22 +114,25 @@ pub async fn runloop(mut root: impl FnMut(&state::Key<LoopBehavior>)) {
let task_waker = RunLoopWaker(std::future::get_task_context(|c| c.waker().clone()));

let mut current_revision = Revision(0);
let mut next_behavior = None;
let mut next_behavior;
loop {
current_revision.0 += 1;

topo::root!(|| {
let (_, behavior) = state!((), |()| LoopBehavior::default());
topo::root!(
{
let (_, behavior) = state!((), |()| LoopBehavior::default());

// CALLER'S CODE IS CALLED HERE
root(&behavior);
// CALLER'S CODE IS CALLED HERE
root(&behavior);

// stash the write key for ourselves for reading after exiting this call
next_behavior = behavior.flushed().read();
}, Env {
RunLoopWaker => task_waker.clone(),
Revision => current_revision
});
// stash the write key for ourselves for reading after exiting this call
next_behavior = behavior.flushed().read();
},
env! {
RunLoopWaker => task_waker.clone(),
Revision => current_revision,
}
);

match next_behavior.as_ref().unwrap().deref() {
LoopBehavior::OnWake => {
Expand Down
5 changes: 2 additions & 3 deletions src/memo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use {
any::{Any, TypeId},
sync::Arc,
},
topo::topo,
};

/// Memoize the provided function at the bound callsite, invalidating previous results only if
Expand All @@ -15,7 +14,7 @@ use {
/// it places a significant constraint on the initializers themselves to only capture `Clone` values
/// or to avoid mutating its captures to implement `Fn`. Instead we require that closures accept
/// the memoized argument by reference rather than by value.
#[topo]
#[topo::bound]
pub fn memo<Arg, Init, Output>(arg: Arg, initializer: Init) -> Output
where
Arg: PartialEq + Send + Sync + 'static,
Expand Down Expand Up @@ -55,7 +54,7 @@ where
mod tests {
use {
crate::{memo::*, LoopBehavior, Revision},
topo::__trace::*,
tokio_trace::*,
};

#[runtime::test]
Expand Down
9 changes: 5 additions & 4 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ use {
ops::Deref,
sync::{Arc, Weak},
},
topo::topo,
topo::bound,
};

// TODO state tests

/// Root a state variable at this callsite, returning an up-to-date [`Commit`] of its value and
/// a unique [`Key`] which can be used to commit new values to the variable.
#[topo]
// TODO: proc macro should allow topo functions to declare `arg` optional with a default to
// which the macro can desugar invocations, so you can pass a init closure only.
// TODO: move arg after initializer

/// Root a state variable at this callsite, returning an up-to-date [`Commit`] of its value and
/// a unique [`Key`] which can be used to commit new values to the variable.
#[bound]
pub fn state<Arg, Init, Output>(arg: Arg, initializer: Init) -> (Commit<Output>, Key<Output>)
where
Arg: PartialEq + Send + Sync + 'static,
Expand Down
1 change: 0 additions & 1 deletion topo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2018"

[dependencies]
owning_ref = "0.4"
tokio-trace = { version = "0.1", features = ["log"] }
topo-macro = { path = "macro" }

[dev-dependencies]
Expand Down
42 changes: 32 additions & 10 deletions topo/benches/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,46 @@ use criterion::{black_box, Criterion, ParameterizedBenchmark};

fn empty_env(c: &mut Criterion) {
c.bench_function("call empty env", |b| {
b.iter(|| black_box(topo::call!(|| topo::Id::current())))
b.iter(|| black_box(topo::call!(topo::Id::current())))
});
}

fn create_small_env(c: &mut Criterion) {
c.bench_function("call create small env", |b| {
b.iter(|| {
black_box(topo::call!(|| topo::Id::current(), Env {
u128 => 10
}))
black_box(topo::call!(
topo::Id::current(),
env! {
u128 => 10,
}
))
});
});
}

fn call_small_env(c: &mut Criterion) {
c.bench_function("call within small env", |b| {
topo::call!(
b.iter(|| {
black_box(topo::call!(topo::Id::current()));
}),
env! {
u128 => 10,
}
)
});
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn topo_bench(b: &mut criterion::Bencher, depth: &usize) {
macro_rules! mk {
(go $depth_spec:ident) => {
topo::call!(|| {
topo::call!({
mk!(pass $depth_spec 0);
}, Env { u128 => 10 });
}, env! { u128 => 10, });
};
(pass $depth_spec:ident $call_depth:expr) => {
topo::call!(|| {
topo::call!({
mk!(cur $depth_spec ($call_depth + 1));
});
};
Expand Down Expand Up @@ -91,7 +107,7 @@ fn topo_bench(b: &mut criterion::Bencher, depth: &usize) {
}
}

fn enter_small_env(c: &mut Criterion) {
fn from_small_env(c: &mut Criterion) {
c.bench(
"topo fns",
ParameterizedBenchmark::new(
Expand All @@ -102,5 +118,11 @@ fn enter_small_env(c: &mut Criterion) {
);
}

criterion_group!(benches, empty_env, create_small_env, enter_small_env);
criterion_main!(benches);
criterion::criterion_group!(
benches,
empty_env,
create_small_env,
from_small_env,
call_small_env
);
criterion::criterion_main!(benches);
19 changes: 3 additions & 16 deletions topo/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,13 @@
extern crate proc_macro;
use {proc_macro::TokenStream, syn::export::TokenStream2};

/// Transforms a function declaration into a topological function invoked with macro syntax.
/// Transforms a function declaration into a topological function invoked with macro syntax to
/// *bind* its call tree's (sub)topology to the parent topology.
///
/// A macro transformation is used to capture unique callsite information from the invoking
/// function. In the current implementation, we synthesize a unique [`std::any::TypeId`] at each
/// callsite which can be used to identify the chain of topological invocations.
///
/// ```
/// # use topo::topo;
/// #[topo]
/// fn print_id() {
/// // this will print something different depending
/// // on location in the call topology
/// println!("{:?}", topo::Id::current());
/// }
///
/// print_id!();
/// print_id!();
/// ```
///
/// ## Implications of using macros
///
/// Upside: Because topological function calls are a bit more expensive than normal function calls,
Expand All @@ -49,7 +37,7 @@ use {proc_macro::TokenStream, syn::export::TokenStream2};
///
/// TODO expand
#[proc_macro_attribute]
pub fn topo(_attrs: TokenStream, input: TokenStream) -> TokenStream {
pub fn bound(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let mut input_fn: syn::ItemFn = syn::parse_macro_input!(input);

let tmp = std::mem::replace(&mut input_fn.attrs, Vec::new());
Expand Down Expand Up @@ -105,7 +93,6 @@ fn docs_fn_signature(input_fn: &syn::ItemFn) -> TokenStream2 {
decl: input_fn.decl.clone(),
block: Box::new(syn::Block {
brace_token: syn::token::Brace {
// we
span: proc_macro::Span::call_site().into(),
},
stmts: vec![],
Expand Down
Loading

0 comments on commit e9761ca

Please sign in to comment.