On 2013-04-16 00:45:19, Artella Coding wrote:
> Hi Felix,
> 
> Thanks for your response. That is an interest observation you made about
> the println functon.
> 
> Upon playing with the code a bit more, I noticed that the strange behaviour
> seems to be limited to the function "second". The distinguishing feature of
> this function is that it involves a nested function.
> 
> I have rewritten the codes to only involve a single function in each source
> file (in order to illustrate this better) & the source and results are
> below (to call the functions twice just uncomment the last few lines I have
> commented out in each program) :
> 
> firstAndFirst.rs
> 1)Call "first" once : 4.5s
> 2)Call "first" twice : 9.4s
> 
> secondAndSecond.rs (nested function call_
> 1)Call "second" once : 9.5s
> 2)Call "second" twice : 43.8s (--> I would have expected this to be 19s <--)
> 
> *********************************************************************************
> *********************************************************************************
> //firstAndFirst.rs
> static g_r: float = 3.569956;
> static g_x0:float = 0.53;
> 
> fn first(n: i64, x0 : float, r: float) -> float {
>     let mut x = x0;
>     let mut i = 0i64;
>     loop {
>         x = r*x*(1.-x);
>         i += 1;
>         if i == n { break }
>     }
>     x
> }
> 
> fn main(){
>     let l = 400000000i64;
> 
>     let result = first(l,g_x0,g_r);
>     io::println(float::to_str(result));
> 
>     //let result2 = first(l,g_x0,g_r);
>     //io::println(float::to_str(result2));
> }
> *********************************************************************************
> *********************************************************************************
> //secondAndSecond.rs
> static g_r: float = 3.569956;
> static g_x0:float = 0.53;
> 
> fn second(n: i64, x0 : float, r: float) -> float {
> 
>     fn calc(x : float, r: float) -> float {
>         let mut xm = x;
>         xm = r*xm*(1.-xm);
>         xm
>     }
> 
>     let mut x = x0;
>     let mut i = 0i64;
>     loop {
>         x = calc(x,r);
>         i += 1;
>         if i == n { break }
>     }
>     x
> 
> }
> 
> fn main(){
>     let l = 400000000i64;
> 
>     let result = second(l,g_x0,g_r);
>     io::println(float::to_str(result));
> 
>     //let result2 = second(l,g_x0,g_r);
>     //io::println(float::to_str(result2));
> }
> *********************************************************************************
> *********************************************************************************

Hi Artella,

I was curious about this and did some investigation. There are a few things 
that are 
causing the issues you are seeing.

rustc doesn't produce particularly good LLVM IR. There is nothing technically 
wrong
with it, it is just very bloated. This is fine because the LLVM optimizer is 
pretty good 
at taking that code down to a reasonable level.

I did some tests compiling with all 4 levels of optimization:

Level 0: (default)
    Time: 10.0s
    Binary Size: 118k
    LLVM IR Size: 16512 lines

Level 1:
    Time: 1.38s
    Binary Size: 62k
    LLVM IR Size: 6902 lines

Level 2: (default level with -O)
    Time: 1.37s
    Binary Size: 22k
    LLVM IR Size: 1949 lines

Level 3:
    Time: 1.37s
    Binary Size: 22k
    LLVM IR Size: 1949 lines

The jump from 0 to 1 is the biggest. In fact calling second twice produces a 
difference of
50s vs 2.7 for level 0 vs level 1 optimization.

As to why it produces so much code, well for one, all the intrinsics and a few 
other
things are included, even at opt-level 1 they are removed as being unused. That 
doesn't
explain the speed of the code though. That is likely to do with the fact that 
the call to
the inner function isn't inlined, and therefore you are paying for that 
function call.
While normally function calls are fast, when you are calling it 400,000,000 
times, it adds
up.

As for the exponetial slow-down, it's related to the fact that io is 
asynchronous and
handled by the scheduler task, but tasks are scheduled co-operative meaning
a CPU-intensive task can block other tasks in the same thread. I haven't yet 
figured out
the specific reasons, but wrapping each call to `second` in it's own task 
causes the time
to drop from ~1 minute to 7 seconds. If you account for parallelism from 
running in
multiple threads, then `time` reports ~15 seconds. Which is double.

Again, I am unclear on the exact reason why this happens, but it is fairly 
clear that it
is the IO that is causing the issue. Fortunately there is a new scheduler on 
the way that
will hopefully sort some of these issues.

--
James Miller
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to