Skip to content

Commit d53159a

Browse files
committed
auto merge of #10070 : alexcrichton/rust/fewer-missiles, r=brson
This optimizes the `home_for_io` code path by requiring fewer scheduler operations in some situtations. When moving to your home scheduler, this no longer forces a context switch if you're already on the home scheduler. Instead, the homing code now simply pins you to your current scheduler (making it so you can't be stolen away). If you're not on your home scheduler, then we context switch away, sending you to your home scheduler. When the I/O operation is done, then we also no longer forcibly trigger a context switch. Instead, the action is cased on whether the task is homed or not. If a task does not have a home, then the task is re-flagged as not having a home and no context switch is performed. If a task is homed to the current scheduler, then we don't do anything, and if the task is homed to a foreign scheduler, then it's sent along its merry way. I verified that there are about a third as many `write` syscalls done in print operations now. Libuv uses write to implement async handles, and the homing before and after each I/O operation was triggering a write on these async handles. Additionally, using the terrible benchmark of printing 10k times in a loop, this drives the runtime from 0.6s down to 0.3s (yay!).
2 parents c5074ae + e4c6523 commit d53159a

File tree

2 files changed

+49
-36
lines changed

2 files changed

+49
-36
lines changed

src/libstd/rt/sched.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,12 @@ impl Scheduler {
338338
this.process_task(task, Scheduler::resume_task_immediately_cl);
339339
return None;
340340
}
341+
Some(RunOnce(task)) => {
342+
// bypass the process_task logic to force running this task once
343+
// on this home scheduler. This is often used for I/O (homing).
344+
Scheduler::resume_task_immediately_cl(this, task);
345+
return None;
346+
}
341347
Some(Wake) => {
342348
this.sleepy = false;
343349
Local::put(this);
@@ -797,7 +803,8 @@ pub enum SchedMessage {
797803
Wake,
798804
Shutdown,
799805
PinnedTask(~Task),
800-
TaskFromFriend(~Task)
806+
TaskFromFriend(~Task),
807+
RunOnce(~Task),
801808
}
802809

803810
pub struct SchedHandle {

src/libstd/rt/uv/uvio.rs

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rt::local::Local;
2929
use rt::rtio::*;
3030
use rt::sched::{Scheduler, SchedHandle};
3131
use rt::tube::Tube;
32-
use rt::task::SchedHome;
32+
use rt::task::Task;
3333
use rt::uv::*;
3434
use rt::uv::idle::IdleWatcher;
3535
use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr};
@@ -59,66 +59,72 @@ trait HomingIO {
5959

6060
fn home<'r>(&'r mut self) -> &'r mut SchedHandle;
6161

62-
/* XXX This will move pinned tasks to do IO on the proper scheduler
63-
* and then move them back to their home.
64-
*/
65-
fn go_to_IO_home(&mut self) -> SchedHome {
66-
use rt::sched::PinnedTask;
62+
/// This function will move tasks to run on their home I/O scheduler. Note
63+
/// that this function does *not* pin the task to the I/O scheduler, but
64+
/// rather it simply moves it to running on the I/O scheduler.
65+
fn go_to_IO_home(&mut self) -> uint {
66+
use rt::sched::RunOnce;
6767

68-
do task::unkillable { // FIXME(#8674)
69-
let mut old = None;
70-
{
71-
let ptr = &mut old;
68+
let current_sched_id = do Local::borrow |sched: &mut Scheduler| {
69+
sched.sched_id()
70+
};
71+
72+
// Only need to invoke a context switch if we're not on the right
73+
// scheduler.
74+
if current_sched_id != self.home().sched_id {
75+
do task::unkillable { // FIXME(#8674)
7276
let scheduler: ~Scheduler = Local::take();
7377
do scheduler.deschedule_running_task_and_then |_, task| {
7478
/* FIXME(#8674) if the task was already killed then wake
75-
* will return None. In that case, the home pointer will never be set.
79+
* will return None. In that case, the home pointer will
80+
* never be set.
7681
*
77-
* RESOLUTION IDEA: Since the task is dead, we should just abort the IO action.
82+
* RESOLUTION IDEA: Since the task is dead, we should
83+
* just abort the IO action.
7884
*/
79-
do task.wake().map |mut task| {
80-
*ptr = Some(task.take_unwrap_home());
81-
self.home().send(PinnedTask(task));
85+
do task.wake().map |task| {
86+
self.home().send(RunOnce(task));
8287
};
8388
}
8489
}
85-
old.expect("No old home because task had already been killed.")
8690
}
91+
92+
self.home().sched_id
8793
}
8894

89-
// XXX dummy self param
90-
fn restore_original_home(_dummy_self: Option<Self>, old: SchedHome) {
91-
use rt::sched::TaskFromFriend;
95+
// XXX: dummy self parameter
96+
fn restore_original_home(_: Option<Self>, io_home: uint) {
97+
// It would truly be a sad day if we had moved off the home I/O
98+
// scheduler while we were doing I/O.
99+
assert_eq!(Local::borrow(|sched: &mut Scheduler| sched.sched_id()),
100+
io_home);
92101

93-
let old = Cell::new(old);
94-
do task::unkillable { // FIXME(#8674)
95-
let scheduler: ~Scheduler = Local::take();
96-
do scheduler.deschedule_running_task_and_then |scheduler, task| {
97-
/* FIXME(#8674) if the task was already killed then wake
98-
* will return None. In that case, the home pointer will never be restored.
99-
*
100-
* RESOLUTION IDEA: Since the task is dead, we should just abort the IO action.
101-
*/
102-
do task.wake().map |mut task| {
103-
task.give_home(old.take());
104-
scheduler.make_handle().send(TaskFromFriend(task));
105-
};
102+
// If we were a homed task, then we must send ourselves back to the
103+
// original scheduler. Otherwise, we can just return and keep running
104+
if !Task::on_appropriate_sched() {
105+
do task::unkillable { // FIXME(#8674)
106+
let scheduler: ~Scheduler = Local::take();
107+
do scheduler.deschedule_running_task_and_then |_, task| {
108+
do task.wake().map |task| {
109+
Scheduler::run_task(task);
110+
};
111+
}
106112
}
107113
}
108114
}
109115

110116
fn home_for_io<A>(&mut self, io: &fn(&mut Self) -> A) -> A {
111117
let home = self.go_to_IO_home();
112118
let a = io(self); // do IO
113-
HomingIO::restore_original_home(None::<Self> /* XXX dummy self */, home);
119+
HomingIO::restore_original_home(None::<Self>, home);
114120
a // return the result of the IO
115121
}
116122

117123
fn home_for_io_consume<A>(self, io: &fn(Self) -> A) -> A {
118124
let mut this = self;
119125
let home = this.go_to_IO_home();
120126
let a = io(this); // do IO
121-
HomingIO::restore_original_home(None::<Self> /* XXX dummy self */, home);
127+
HomingIO::restore_original_home(None::<Self>, home);
122128
a // return the result of the IO
123129
}
124130

@@ -128,7 +134,7 @@ trait HomingIO {
128134
let scheduler: ~Scheduler = Local::take();
129135
io_sched(self, scheduler) // do IO and scheduling action
130136
};
131-
HomingIO::restore_original_home(None::<Self> /* XXX dummy self */, home);
137+
HomingIO::restore_original_home(None::<Self>, home);
132138
a // return result of IO
133139
}
134140
}

0 commit comments

Comments
 (0)