Lazily initialize 'leaf node' taskgroups for unlinked spawns, for an apparent 11% speedup.
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user