Lazily initialize 'leaf node' taskgroups for unlinked spawns, for an apparent 11% speedup.

This commit is contained in:
Ben Blum
2013-08-05 16:55:08 -04:00
parent 47e82c8555
commit 1f95bd7684
2 changed files with 63 additions and 45 deletions

View File

@@ -193,6 +193,10 @@ impl BlockedTask {
/// Create a blocked task, unless the task was already killed. /// Create a blocked task, unless the task was already killed.
pub fn try_block(mut task: ~Task) -> Either<~Task, BlockedTask> { pub fn try_block(mut task: ~Task) -> Either<~Task, BlockedTask> {
// NB: As an optimization, we could give a free pass to being unkillable
// to tasks whose taskgroups haven't been initialized yet, but that
// introduces complications with select() and with the test cases below,
// and it's not clear the uncommon performance boost is worth it.
if task.death.unkillable > 0 { if task.death.unkillable > 0 {
Right(Unkillable(task)) Right(Unkillable(task))
} else { } else {

View File

@@ -568,7 +568,8 @@ impl RuntimeGlue {
let me = Local::unsafe_borrow::<Task>(); let me = Local::unsafe_borrow::<Task>();
blk(match (*me).taskgroup { blk(match (*me).taskgroup {
None => { None => {
// Main task, doing first spawn ever. Lazily initialize. // First task in its (unlinked/unsupervised) taskgroup.
// Lazily initialize.
let mut members = TaskSet::new(); let mut members = TaskSet::new();
let my_handle = (*me).death.kill_handle.get_ref().clone(); let my_handle = (*me).death.kill_handle.get_ref().clone();
members.insert(NewTask(my_handle)); members.insert(NewTask(my_handle));
@@ -591,37 +592,46 @@ impl RuntimeGlue {
} }
} }
// Returns 'None' in the case where the child's TG should be lazily initialized.
fn gen_child_taskgroup(linked: bool, supervised: bool) fn gen_child_taskgroup(linked: bool, supervised: bool)
-> (TaskGroupArc, AncestorList, bool) { -> Option<(TaskGroupArc, AncestorList, bool)> {
do RuntimeGlue::with_my_taskgroup |spawner_group| { // FIXME(#7544): Not safe to lazily initialize in the old runtime. Remove
let ancestors = AncestorList(spawner_group.ancestors.map(|x| x.clone())); // this context check once 'spawn_raw_oldsched' is gone.
if linked { if context() == OldTaskContext || linked || supervised {
// Child is in the same group as spawner. // with_my_taskgroup will lazily initialize the parent's taskgroup if
// Child's ancestors are spawner's ancestors. // it doesn't yet exist. We don't want to call it in the unlinked case.
// Propagate main-ness. do RuntimeGlue::with_my_taskgroup |spawner_group| {
(spawner_group.tasks.clone(), ancestors, spawner_group.is_main) let ancestors = AncestorList(spawner_group.ancestors.map(|x| x.clone()));
} else { if linked {
// Child is in a separate group from spawner. // Child is in the same group as spawner.
let g = Exclusive::new(Some(TaskGroupData { // Child's ancestors are spawner's ancestors.
members: TaskSet::new(), // Propagate main-ness.
descendants: TaskSet::new(), Some((spawner_group.tasks.clone(), ancestors, spawner_group.is_main))
}));
let a = if supervised {
let new_generation = incr_generation(&ancestors);
assert!(new_generation < uint::max_value);
// Child's ancestors start with the spawner.
// Build a new node in the ancestor list.
AncestorList(Some(Exclusive::new(AncestorNode {
generation: new_generation,
parent_group: spawner_group.tasks.clone(),
ancestors: ancestors,
})))
} else { } else {
// Child has no ancestors. // Child is in a separate group from spawner.
AncestorList(None) let g = Exclusive::new(Some(TaskGroupData {
}; members: TaskSet::new(),
(g, a, false) descendants: TaskSet::new(),
}));
let a = if supervised {
let new_generation = incr_generation(&ancestors);
assert!(new_generation < uint::max_value);
// Child's ancestors start with the spawner.
// Build a new node in the ancestor list.
AncestorList(Some(Exclusive::new(AncestorNode {
generation: new_generation,
parent_group: spawner_group.tasks.clone(),
ancestors: ancestors,
})))
} else {
// Child has no ancestors.
AncestorList(None)
};
Some((g, a, false))
}
} }
} else {
None
} }
} }
@@ -670,20 +680,24 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
let child_wrapper: ~fn() = || { let child_wrapper: ~fn() = || {
// Child task runs this code. // Child task runs this code.
let child_data = Cell::new(child_data.take()); // :(
let enlist_success = do Local::borrow::<Task, bool> |me| { // If child data is 'None', the enlist is vacuously successful.
let (child_tg, ancestors, is_main) = child_data.take(); let enlist_success = do child_data.take().map_consume_default(true) |child_data| {
let mut ancestors = ancestors; let child_data = Cell::new(child_data); // :(
// FIXME(#7544): Optimize out the xadd in this clone, somehow. do Local::borrow::<Task, bool> |me| {
let handle = me.death.kill_handle.get_ref().clone(); let (child_tg, ancestors, is_main) = child_data.take();
// Atomically try to get into all of our taskgroups. let mut ancestors = ancestors;
if enlist_many(NewTask(handle), &child_tg, &mut ancestors) { // FIXME(#7544): Optimize out the xadd in this clone, somehow.
// Got in. We can run the provided child body, and can also run let handle = me.death.kill_handle.get_ref().clone();
// the taskgroup's exit-time-destructor afterward. // Atomically try to get into all of our taskgroups.
me.taskgroup = Some(Taskgroup(child_tg, ancestors, is_main, None)); if enlist_many(NewTask(handle), &child_tg, &mut ancestors) {
true // Got in. We can run the provided child body, and can also run
} else { // the taskgroup's exit-time-destructor afterward.
false me.taskgroup = Some(Taskgroup(child_tg, ancestors, is_main, None));
true
} else {
false
}
} }
}; };
// Should be run after the local-borrowed task is returned. // Should be run after the local-borrowed task is returned.
@@ -749,7 +763,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
let join_task = join_task_cell.take(); let join_task = join_task_cell.take();
let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool) || { let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool) || {
rtdebug!("boostraping a 1:1 scheduler"); rtdebug!("bootstrapping a 1:1 scheduler");
}; };
new_sched.bootstrap(bootstrap_task); new_sched.bootstrap(bootstrap_task);
@@ -793,7 +807,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) { fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
let (child_tg, ancestors, is_main) = let (child_tg, ancestors, is_main) =
gen_child_taskgroup(opts.linked, opts.supervised); gen_child_taskgroup(opts.linked, opts.supervised).expect("old runtime needs TG");
unsafe { unsafe {
let child_data = Cell::new((child_tg, ancestors, f)); let child_data = Cell::new((child_tg, ancestors, f));