[rust-dev] RFC: Explicit stack switching

Patrick Walton pwalton at mozilla.com
Thu Jan 31 14:37:33 PST 2013


Hi everyone,

With the revamp of the scheduler underway, I'd like to propose a change 
to the way C functions work.

Currently, we generate a shim and a stack switch for every function call 
from Rust to C and likewise from C to Rust, except for functions 
annotated with `#[rust_stack]`. These wrappers result in a significant 
performance overhead. For some workloads this performance overhead is 
acceptable in order to maintain small stacks. For some workloads the 
performance overhead is undesirable.

For instance, the DOM in Servo requires lots of very small calls from 
JavaScript to Rust. The overhead of stack switching swamps most of the 
time here. Popular Web benchmarks will do things like 
`someElement.clientX;` over and over, which require calls from 
JavaScript to Rust to retrieve a cached value. So we must carefully 
consider every CPU cycle spent in the C-to-Rust transition.

To address these issues I would like to propose a somewhat radical 
change: don't have the compiler generate stack switching stubs at all. 
Instead, the scheduler can expose a primitive that generates the stack 
switch, and it's the programmer's responsibility to perform the stack 
switch to call out to C functions. To avoid the obvious footgun here, I 
propose a lint pass, on by default, that ensures that functions not 
annotated with `#[rust_stack]` are called inside a stack switching helper.

The rationale here is as follows:

1. It should be possible to group many C calls under a single stack 
switching operation. For example:

     do stackswitch {
         c_function_1();
         c_function_2();
         c_function_3();
     }

This amortizes the cost of the stack switch over many native function calls.

2. It should be possible to have sections of Rust code that run on a big 
C stack and do not use segmented stacks; for example, the new Rust 
scheduler (which is to be written in Rust), or the Servo DOM as 
mentioned above.

3. If (2) is possible, the Rust compiler never knows whether there's 
enough stack space available to safely call a C function. Therefore, 
performing the stack switch ought to be under the programmer's control.

4. We should have a lint pass that ensures that stack switches are 
performed properly, because we do not want programmers to accidentally 
shoot themselves in the foot.

5. Because C functions are always unsafe in the Rust sense, Rust code 
will almost always wrap functionality provided by foreign libraries into 
safe Rust abstractions. The stack switch can be moved into these 
abstractions.

6. C functions are always unsafe, so this does not, formally, add any 
new unsafety.

Whatever decision we come to, we should make this decision soon (before 
0.6), because this will break code. Thoughts?

Patrick


More information about the Rust-dev mailing list