Skip to content

Commit 1f95bd7

Browse files
committed
Lazily initialize 'leaf node' taskgroups for unlinked spawns, for an apparent 11% speedup.
1 parent 47e82c8 commit 1f95bd7

File tree

2 files changed

+63
-45
lines changed

2 files changed

+63
-45
lines changed

src/libstd/rt/kill.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ impl BlockedTask {
193193

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

src/libstd/task/spawn.rs

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ impl RuntimeGlue {
568568
let me = Local::unsafe_borrow::<Task>();
569569
blk(match (*me).taskgroup {
570570
None => {
571-
// Main task, doing first spawn ever. Lazily initialize.
571+
// First task in its (unlinked/unsupervised) taskgroup.
572+
// Lazily initialize.
572573
let mut members = TaskSet::new();
573574
let my_handle = (*me).death.kill_handle.get_ref().clone();
574575
members.insert(NewTask(my_handle));
@@ -591,37 +592,46 @@ impl RuntimeGlue {
591592
}
592593
}
593594

595+
// Returns 'None' in the case where the child's TG should be lazily initialized.
594596
fn gen_child_taskgroup(linked: bool, supervised: bool)
595-
-> (TaskGroupArc, AncestorList, bool) {
596-
do RuntimeGlue::with_my_taskgroup |spawner_group| {
597-
let ancestors = AncestorList(spawner_group.ancestors.map(|x| x.clone()));
598-
if linked {
599-
// Child is in the same group as spawner.
600-
// Child's ancestors are spawner's ancestors.
601-
// Propagate main-ness.
602-
(spawner_group.tasks.clone(), ancestors, spawner_group.is_main)
603-
} else {
604-
// Child is in a separate group from spawner.
605-
let g = Exclusive::new(Some(TaskGroupData {
606-
members: TaskSet::new(),
607-
descendants: TaskSet::new(),
608-
}));
609-
let a = if supervised {
610-
let new_generation = incr_generation(&ancestors);
611-
assert!(new_generation < uint::max_value);
612-
// Child's ancestors start with the spawner.
613-
// Build a new node in the ancestor list.
614-
AncestorList(Some(Exclusive::new(AncestorNode {
615-
generation: new_generation,
616-
parent_group: spawner_group.tasks.clone(),
617-
ancestors: ancestors,
618-
})))
597+
-> Option<(TaskGroupArc, AncestorList, bool)> {
598+
// FIXME(#7544): Not safe to lazily initialize in the old runtime. Remove
599+
// this context check once 'spawn_raw_oldsched' is gone.
600+
if context() == OldTaskContext || linked || supervised {
601+
// with_my_taskgroup will lazily initialize the parent's taskgroup if
602+
// it doesn't yet exist. We don't want to call it in the unlinked case.
603+
do RuntimeGlue::with_my_taskgroup |spawner_group| {
604+
let ancestors = AncestorList(spawner_group.ancestors.map(|x| x.clone()));
605+
if linked {
606+
// Child is in the same group as spawner.
607+
// Child's ancestors are spawner's ancestors.
608+
// Propagate main-ness.
609+
Some((spawner_group.tasks.clone(), ancestors, spawner_group.is_main))
619610
} else {
620-
// Child has no ancestors.
621-
AncestorList(None)
622-
};
623-
(g, a, false)
611+
// Child is in a separate group from spawner.
612+
let g = Exclusive::new(Some(TaskGroupData {
613+
members: TaskSet::new(),
614+
descendants: TaskSet::new(),
615+
}));
616+
let a = if supervised {
617+
let new_generation = incr_generation(&ancestors);
618+
assert!(new_generation < uint::max_value);
619+
// Child's ancestors start with the spawner.
620+
// Build a new node in the ancestor list.
621+
AncestorList(Some(Exclusive::new(AncestorNode {
622+
generation: new_generation,
623+
parent_group: spawner_group.tasks.clone(),
624+
ancestors: ancestors,
625+
})))
626+
} else {
627+
// Child has no ancestors.
628+
AncestorList(None)
629+
};
630+
Some((g, a, false))
631+
}
624632
}
633+
} else {
634+
None
625635
}
626636
}
627637

@@ -670,20 +680,24 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
670680

671681
let child_wrapper: ~fn() = || {
672682
// Child task runs this code.
673-
let child_data = Cell::new(child_data.take()); // :(
674-
let enlist_success = do Local::borrow::<Task, bool> |me| {
675-
let (child_tg, ancestors, is_main) = child_data.take();
676-
let mut ancestors = ancestors;
677-
// FIXME(#7544): Optimize out the xadd in this clone, somehow.
678-
let handle = me.death.kill_handle.get_ref().clone();
679-
// Atomically try to get into all of our taskgroups.
680-
if enlist_many(NewTask(handle), &child_tg, &mut ancestors) {
681-
// Got in. We can run the provided child body, and can also run
682-
// the taskgroup's exit-time-destructor afterward.
683-
me.taskgroup = Some(Taskgroup(child_tg, ancestors, is_main, None));
684-
true
685-
} else {
686-
false
683+
684+
// If child data is 'None', the enlist is vacuously successful.
685+
let enlist_success = do child_data.take().map_consume_default(true) |child_data| {
686+
let child_data = Cell::new(child_data); // :(
687+
do Local::borrow::<Task, bool> |me| {
688+
let (child_tg, ancestors, is_main) = child_data.take();
689+
let mut ancestors = ancestors;
690+
// FIXME(#7544): Optimize out the xadd in this clone, somehow.
691+
let handle = me.death.kill_handle.get_ref().clone();
692+
// Atomically try to get into all of our taskgroups.
693+
if enlist_many(NewTask(handle), &child_tg, &mut ancestors) {
694+
// Got in. We can run the provided child body, and can also run
695+
// the taskgroup's exit-time-destructor afterward.
696+
me.taskgroup = Some(Taskgroup(child_tg, ancestors, is_main, None));
697+
true
698+
} else {
699+
false
700+
}
687701
}
688702
};
689703
// Should be run after the local-borrowed task is returned.
@@ -749,7 +763,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
749763
let join_task = join_task_cell.take();
750764

751765
let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool) || {
752-
rtdebug!("boostraping a 1:1 scheduler");
766+
rtdebug!("bootstrapping a 1:1 scheduler");
753767
};
754768
new_sched.bootstrap(bootstrap_task);
755769

@@ -793,7 +807,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
793807
fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
794808

795809
let (child_tg, ancestors, is_main) =
796-
gen_child_taskgroup(opts.linked, opts.supervised);
810+
gen_child_taskgroup(opts.linked, opts.supervised).expect("old runtime needs TG");
797811

798812
unsafe {
799813
let child_data = Cell::new((child_tg, ancestors, f));

0 commit comments

Comments
 (0)