On Tuesday 22 April 2008 12:49:16 Antonio Cuni wrote:
> Jon Harrop wrote:
> > You generated code that turned out to be less efficient on the CLR in
> > this particular case but you cannot validly generalize that to all
> > "non-standard code".
>
> right, I can't generalize to all non-standard code, but it's surely true
> for the kind of non standard code pypy generates :-).

Sure. Generating exceptions unless absolutely necessary will be a very bad 
idea on the CLR but it will also be a bad idea on the JVM because its 
exception handling is slow.

> The exception inlining was only an example, there are other areas where
> the CLR jit was worse, like code that makes an heavy use of temp
> variables instead of leaving the values on the stack.

That's interesting.

> > Why didn't you use tail calls instead?
>
> I honestly don't see how tail calls could help here; could you show me
> an example please?

Sure. Consider the loop:

  void run() {
    for (int i=0; i<3; ++i)
      if (foo(i) == 0) break;
    bar();
    baz();
  }

Sounds like you were translating that into something like (F# code):

  exception StopIteration

  let run() =
    try
      for i=0 to 2 do
        if foo i=0 then raise StopIteration
    with StopIteration ->
      ()
    bar()
    baz()

But you could have translated it into:

  let rec run_1 i =
    if foo i=0 then run_2() else
      if i<3 then run_1 (i + 1) else run_2()
  and run_2() =
    bar()
    baz()

  let run() =
    run_1 0

Where both calls to the continuation "run_2" inside the body of the "run_1" 
function are tail calls.

Tail calls have lots of advantages here. The JIT is likely to generate a 
simple branch but it may well spot that the code blocks can be rearraged to 
avoid even the branch! For example, it might rewrite the code into:

  let rec run_1 i =
    if foo i<>0 && i<3 then run_1 (i + 1) else
      bar()
      baz()

You can pass as many values as arguments to a continuation as you like and 
they are highly likely to be kept in registers wherever your control flow 
takes you (what were the exceptional and non-exceptional routes are now 
symmetric) for the best possible performance. This facilitates lots of 
subsequent optimizations by the JIT.

Doing a quick benchmark on this code, I find that 10^6 iterations using your 
exception-based technique gives:

CLR: 24s
JVM:  1.3s

Holy smokes, the JVM is 18x faster!

Now try the tail calls (only available on the CLR):

CLR:  0.025s

Holy smokes, the CLR is 52x faster!

Optimizing exception handling in the JVM before implementing tail calls was 
premature optimization, IMHO.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "JVM 
Languages" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/jvm-languages?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to