?

Log in

No account? Create an account

Previous Entry | Next Entry

mysteries of TCO

So I've learned that gcc/g++ do tail call optimization with the -O2 option; supposedly MS Visual Studio and clang do it when they can as well. I have tested this:


void b();
void c();

void a() {
    b();
}
void b() {
    c();
}
void c() {
    a();
}

//segfaults
void d(int &vin) {
    int v = vin;
    d(v);
}
int main() {
    //a();

    int v=0;
    d(v);
}


The 3 mutual recursive functions happily loop, when not commented out, while d() segfaults, as there are references back into the calling stack frame, so it can't be eliminated. Makes sense.

But then I tried replicating d() in ocaml:

let rec d vin =
  let v = ref (!vin) in
  d v
  ;;

d (ref 0);;


and it happily chews up CPU without any growth in memory usage, despite my attempt to break the preconditions for TCO. I wonder how. Trampolining? Or, ocaml is garbage collected, maybe it doesn't have a notion of a function stack? Or the analyzer can tell it's never going to need return values? Hmm.

I'm told (by mdl) ocaml has uniform representation, so everything's on the heap.

let rec e vin n =
  match n with
  | 0 -> !vin
  | _ -> (
    incr vin;
    let v = ref (!vin) in
    e v (n - 1);
(* v *)
    )
  ;;

print_int (e (ref 0) 2000000000);;


This still works, without even growing in memory usage. (But if you uncomment the commented line, the stack overflows right aways, as it should -- no longer a tail call.) Oh, I guess a smart GC could tell that the 'vin' won't be needed any more, and collect it in short order. Hmm, neat.

So, C++ with optimizing compilers: more functional than Python, but still outmatched.

mdl said that exception handling could cause overflow. I haven't see it yet, but

let rec d vin =
  try
    let v = ref (!vin) in
    incr vin;
    d v;
  with x -> x
  ;;

d (ref 0);;


simply returns right away, rather than looping. I don't see why. If I add print_int !vin in the middle, 116,643 1s get printed out, before it quits, with 0 exit code and no printed error.

mdl submitted

let rec definitely_kaboom () =
  try definitely_kaboom () with
    Invalid_argument _ (* won't be caught *) -> print_endline "whew";;


which does overflow.

Oh, apparently my try is catching the stack overflow exception itself! Haha. Whereas definitely_kaboom introduces the stack overhead of exception handling but doesn't catch the overflow exception.



See the comment count unavailable DW comments at http://mindstalk.dreamwidth.org/461499.html#comments

Profile

Phoenix
mindstalk
Damien Sullivan
Website

Latest Month

September 2017
S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930

Tags

Powered by LiveJournal.com
Designed by Lilia Ahner