profiling

Stephen Weeks MLton@sourcelight.com
Mon, 21 Jan 2002 13:38:09 -0800


> > Actually, equals and current could be implemented at SML level if the
> > basis library put a wrapper around the raw profile array and were
> > responsible for the initialization and writing at exit of the
> > "mlmon.out" main profile array.  I have no problems with that.
> 
> I don't understand the last paragraph.

Hopefully the following code clarifies what I had in mind.

--------------------------------------------------------------------------------

signature MLTON_PROFILE =
   sig
      val profile: bool

      structure Data:
	 sig
	    type t

	    val equals: t * t -> bool
	    val free: t -> unit
	    val malloc: unit -> t
	    val reset: t -> unit
	    val write: t * string -> unit
	 end
      
      val current: unit -> Data.t
      val setCurrent: Data.t -> unit
   end

functor MLtonProfile
   (structure Cleaner:
       sig
	  type t

	  val addNew: t * (unit -> unit) -> unit
	  val atExit: t
       end
    structure Profile:
       sig
	  val profile: bool
	     
	  structure Data:
	     sig
		type t (* = pointer *)

		val free: t -> unit
		val malloc: unit -> t
		val reset: t -> unit
		val write: t * string -> unit
	     end
	  val setCurrent: Data.t -> unit
       end): MLTON_PROFILE =
struct

val profile = Profile.profile
   
structure Data =
   struct
      datatype t = T of {array: Profile.Data.t,
			 isFreed: bool ref}

      val all: t list ref = ref []
	 
      local
	 fun make f (T r) = f r
      in
	 val array = make #array
	 val isFreed = make #isFreed
      end

      fun equals (d, d') = isFreed d = isFreed d'

      fun free (d as T {array, isFreed, ...}) =
	 if !isFreed
	    then raise Fail "duplicate free"
	 else
	    (all := List.filter (fn d' => not (equals (d, d'))) (!all)
	     ; Profile.Data.free array
	     ; isFreed := true)

      fun malloc () =
	 let
	    val d = T {array = Profile.Data.malloc (),
		       isFreed = ref false}
	    val _ = all := d :: !all
	 in
	    d
	 end

      fun reset (T {array, isFreed, ...}) =
	 if !isFreed
	    then raise Fail "reset of freed data"
	 else Profile.Data.reset array

      fun write (T {array, isFreed, ...}, file) =
	 if !isFreed
	    then raise Fail "write of freed data"
	 else Profile.Data.write (array, file)
   end

val d = Data.malloc ()
val r = ref d

fun current () = !r
  
fun setCurrent (d as Data.T {array, isFreed, ...}) =
   if !isFreed
      then raise Fail "setCurrent of freed data"
   else (r := d
	 ; Profile.setCurrent array)

val _ = setCurrent d

val _ = Cleaner.addNew (Cleaner.atExit, fn () =>
			let
			   val d = current ()
			   val _ = Data.write (d, "mlmon.out")
			   val _ = Data.free d
			in
			   case !Data.all of
			      [] => ()
			    | _ => raise Fail "unfreed data at program exit"
			end)

end