Skip to content

Commit 7208095

Browse files
committed
auto merge of #8317 : bblum/rust/fast-spawn-unlinked, r=brson
This lazily initializes the taskgroup structs for ```spawn_unlinked``` tasks. If such a task never spawns another task linked to it (or a descendant of it), its taskgroup is simply never initialized at all. Also if an unlinked task spawns another unlinked task, neither of them will need to initialize their taskgroups. This works for the main task too. I benchmarked this with the following test case and observed a ~~21% speedup (average over 4 runs: 7.85 sec -> 6.20 sec, 2.5 GHz)~~ 11% speedup, see comment below. ``` use std::task; use std::cell::Cell; use std::rt::comm; static NUM: uint = 1024*256; fn run(f: ~fn()) { let mut t = task::task(); t.unlinked(); t.spawn(f); } fn main() { do NUM.times { let (p,c) = comm::oneshot(); let c = Cell::new(c); do run { c.take().send(()); } p.recv(); } } ```
2 parents 3dfb55a + 1f95bd7 commit 7208095

File tree

2 files changed

+67
-50
lines changed

2 files changed

+67
-50
lines changed

src/libstd/rt/kill.rs

Lines changed: 8 additions & 5 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 {
@@ -205,11 +209,10 @@ impl BlockedTask {
205209
let flag_arc = match task.death.spare_kill_flag.take() {
206210
Some(spare_flag) => spare_flag,
207211
None => {
208-
// FIXME(#7544): Uncomment this when terminate_current_task
209-
// stops being *terrible*. That's the only place that violates
210-
// the assumption of "becoming unkillable will fail if the
211-
// task was killed".
212-
// rtassert!(task.unwinder.unwinding);
212+
// A task that kills us won't have a spare kill flag to
213+
// give back to us, so we restore it ourselves here. This
214+
// situation should only arise when we're already failing.
215+
rtassert!(task.unwinder.unwinding);
213216
(*task.death.kill_handle.get_ref().get()).killed.clone()
214217
}
215218
};

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)