On 02/01/2013 09:23 AM, Matthieu Monrocq wrote:


On Fri, Feb 1, 2013 at 12:09 PM, Michael Neumann <mneum...@ntecs.de <mailto:mneum...@ntecs.de>> wrote:

    Am 31.01.2013 23:37, schrieb Patrick Walton:

        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();
            }


    wouldn't it be possible for this case to just do:

    extern mod lib_c {
      #[rust_stack]
      fn c_function_1();

      #[rust_stack]
       fn c_function_2();

      #[rust_stack]
       fn c_function_3();
    }

    and then calling it like above with *one* "do stackswitch"?

    The default would still be to do a stack switch. If you need to
    call c_function_1 sometimes with a stack switch, and sometimes
    in a group of other functions (with just one stack switch for that
    group), we could have something like this:

    extern mod lib_c {

      // This is the default
      fn c_function_1();

      #[rust_stack]
      fn c_function_1() as rs_c_function1();

    }

    Then use lib_c::c_function_1() when you want a stack switch, or
    rs_c_function1() without. One could go further
    and auto generate for mod lib_c a sub module called "rs" (for rust
    stack), where each function has a #[rust_stack]
    directive in front of it, so you don't have to declare it twice.

    This woudl give use: lib_c::c_function_1() and
    lib_c::rs::c_function_1().

    Regards,

      Michael

    _______________________________________________
    Rust-dev mailing list
    Rust-dev@mozilla.org <mailto:Rust-dev@mozilla.org>
    https://mail.mozilla.org/listinfo/rust-dev


I would have a stupid proposal: what if the C function declaration was annotated not with #[rust_stack] but with #[stack 5k] instead. That is, instead of having a single blunt tool, let the programmer declare how much stack is necessary for the C function and let the compiler reason about it to guarantee that enough stack is available.

extern mod lib_c {
  #[stack 4k]
  fn c_function_1();

  #[stack unlimited]
   fn c_function_2();

  #[stack 16k]
   fn c_function_3();
}

I do imagine we will eventually want precise control to declare how much stack we need. I originally wanted this for optimizations in core, but as more of core is written in Rust this probably isn't going to matter much. There is an issue open on this subject: https://github.com/mozilla/rust/issues/4481



Then when the compiler sees a bunch of C functions:

fn func(x: int) {
    c_function_1();
    c_function_1();
    c_function_1();
    c_function_3();
}


=> if one is marked as "unlimited", then it performs stack switching (once); this can deferred to the branch the function is called in if judged "better". => otherwise, it evaluates the maximum amount of stack needed and prepares it at the beginning of the function, as usual

This sounds like it requires the compiler to have yet more knowledge about stack switching, and a goal of pcwalton's proposal is to get the stack switching logic out of the compiler (into a syntax extension).



Advantages:
+ Code is not invalidated, only the function declarations are, and it's easy enough to do a bulk replace #[rust_stack] -> #[stack 4k] + The information can bubble up at "func" automatically, so that caller of "func" can perform the switch/reservation themselves if it's judged profitable.

Disadvantages:
- Myth of the sufficiently smart compiler spotted.


-- Matthieu


_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to