profiling
Matthew Fluet
Matthew Fluet <fluet@CS.Cornell.EDU>
Fri, 18 Jan 2002 17:45:31 -0500 (EST)
I finished updating the profiling information in the docs.
I think the MLton.Profile structure is nice, but we can probably do
better. Here's an ambitious signature:
signature MLTON_PROFILE =
sig
val profile: bool
type data
val equals: data * data -> bool
val get: unit -> data
val new: unit -> data
val reset: data -> unit
val set: data -> unit
val write: data * string -> unit
end
Here's the vision: data is really an int array. We calculate the size of
the array by externing the card variable in prof.c. (I thought we could
actually calculate it just using nullary _ffi's, but the semantics of
_ffi "x": int is to fetch the integer at label x, not to fetch the address
of label x. Hacking a new _addr variant could accomplish this, but I
don't see much other use for it.) Now, equals, reset and write can be
pure ML functions, just working on the array. Atomicity is tricky, but I
don't think a big deal.
That leaves get and set. Obviously, we need to have the timer handler be
a C function, not an ML function, because we don't want to wait for the ML
signal handler to be invoked. That means we need to register the array
with the profiler -- scary, because the array is going to move around
during GC's. One solution would be to ensure that the array the profiler
touches is global; hence a pointer to it will be in GC_state.globals. If
we can get the address of that pointer (???) and pass that to the
profiler startup code, then we should always be able to get to the array
via a dereference. (globals[n] will always point to the array, so
(*(globals[n]))[pc] should be the slot we want to update. We pass
&(globals[n]) to the profiler as buffP, so (*buffP)[pc] is the slot to
update.)
Better, would be to globalize an int array ref, but _ffi's can't have that
type. Although, I don't see any obvious problems with:
_ffi "name": ty;
ty ::= u | s * ... * s -> u
t ::= u | t array | t ref | t vector
u ::= bool | char | int | real | string | unit | word | word8
(We don't currently allow recursion in t's.)
Then, I think we could give prof.c a function
static **unit buffP = NULL;
void setBuff(***uint p)
{ buffP = *p; }
Now, in the basis library implementation of MLton.Profile we have (I'm
mixing in some stuff that would be in primitive.sml; and some of these
should be predicated on the profile boolean):
val getCard = _ffi "MLton_Profile_getCard": void -> int;
val card = getCard ()
fun new () = Array.array(card, 0)
val dataR = ref (new ())
val dataRR = ref dataR
val setBuff = _ffi "MLton_Profile_setBuff": int array ref ref -> unit;
val _ = setBuff dataRR
val set = fn data => dataR := data
val get = fn () => !dataR
(Only trickery I see here is that we'd have to prevent localRef from
deglobalizing dataRR, but we could do that just by looking for
the MLton_Profile_setBuff FFI primitive.)
Anyways, maybe this needs a little more thought. What really bugs me
about the current interface is that I can't easily profile a function that
is called multiple times. What I really want to be able to write is a
function like:
val profBatch: string * ('a -> 'b) -> (('a -> 'b) * (unit -> unit))
So I can sum up over all executions of a function. Right now, I'd have to
settle for:
val profBatch: string * ('a -> 'b) -> ('a -> 'b) =
fn (f, string) =>
let
val r = ref 0
in
fn a =>
let
val _ = r := !r + 1
val _ = MLton.Profile.reset ()
val b = f a
val _ = MLton.Profile.write (f ^ (Int.toString (!r)))
in
b
end
end
and run mlprof on all of the mlmon.out* files.