fun append (l1, l2) = case l1 of [] => l2 | x :: l1 => x :: append (l1, l2) fun rev l = case l of [] => [] | x :: l => append (rev l, [x]) val l = List.tabulate (1000, fn i => i) val _ = 1 + hd (rev l)
Compile with stack profiling and then run the program.
% mlton -profile alloc -profile-stack true list-rev.sml % ./list-rev
Display the profiling data.
% mlprof -show-line true list-rev mlmon.out 6,030,136 bytes allocated (108,336 bytes by GC) function cur stack GC ----------------------- ----- ----- ---- append list-rev.sml: 1 97.6% 97.6% 1.4% <gc> 1.8% 0.0% 1.8% <main> 0.4% 98.2% 1.8% rev list-rev.sml: 6 0.2% 97.6% 1.8%
In the above table, we see that rev, defined on line 6 of list-rev.sml, is only responsible for 0.2% of the allocation, but is on the stack while 97.6% of the allocation is done by the user program and while 1.8% of the allocation is done by the garbage collector.
The run-time performance impact of -profile-stack true can be noticeable since there is some extra bookkeeping at every nontail call and return.