(* The following functions are implemented in perfcount.c *) (* Returns true if we have the performance counters *) external has_performance_counters: unit -> bool = "has_performance_counters" (* Initializes the CPU speed and returns true if we have the performance counters *) external reset_performance_counters: unit -> bool = "reset_performance_counters" (* Returns number of seconds since the first read *) external read_pentium_perfcount : unit -> float = "read_pentium_perfcount" (* Returns current cycle counter, divided by 1^20, and truncated to 30 bits *) external sample_pentium_perfcount_20 : unit -> int = "sample_pentium_perfcount_20" (* Returns current cycle counter, divided by 1^10, and truncated to 30 bits *) external sample_pentium_perfcount_10 : unit -> int = "sample_pentium_perfcount_10" (** Whether to use the performance counters (on Pentium only) *) type timerModeEnum = | Disabled (** Do not collect timing information *) | SoftwareTimer (** Use OCaml's [Unix.time] for timing information *) | HardwareTimer (** Use the Pentium's cycle counter to time code *) | HardwareIfAvail (** Use the hardware cycle counter if availible; otherwise use SoftwareTimer *) (* The performance counters are disabled by default. This will always be one of Disabled | SoftwareTimer | HardwareTimer. HardwareIfAvail is handled in reset. *) let timerMode = ref Disabled (* Flag for counting number of calls *) let countCalls = ref false (* A hierarchy of timings *) type t = { name : string; mutable time : float; (* In seconds *) mutable ncalls : int; mutable sub : t list} (* Create the top level *) let top = { name = "TOTAL"; time = 0.0; ncalls = 0; sub = []; } (* The stack of current path through * the hierarchy. The first is the * leaf. *) let current : t list ref = ref [top] exception NoPerfCount let reset (mode: timerModeEnum) : unit = top.sub <- []; match mode with Disabled | SoftwareTimer -> timerMode := mode | HardwareTimer -> if not (reset_performance_counters ()) then begin timerMode := SoftwareTimer; raise NoPerfCount end; timerMode := mode | HardwareIfAvail -> if (reset_performance_counters ()) then timerMode := HardwareTimer else timerMode := SoftwareTimer let print chn msg = (* Total up *) top.time <- List.fold_left (fun sum f -> sum +. f.time) 0.0 top.sub; let rec prTree ind node = begin if !timerMode = HardwareTimer then (Printf.fprintf chn "%s%-25s %8.5f s" (String.make ind ' ') node.name node.time) else (Printf.fprintf chn "%s%-25s %6.3f s" (String.make ind ' ') node.name node.time) end; begin if node.ncalls <= 0 then output_string chn "\n" else if node.ncalls = 1 then output_string chn " (1 call)\n" else (Printf.fprintf chn " (%d calls)\n" node.ncalls) end; List.iter (prTree (ind + 2)) (List.rev node.sub) in Printf.fprintf chn "%s" msg; List.iter (prTree 0) [ top ]; Printf.fprintf chn "Timing used %s\n" (if !timerMode = HardwareTimer then "Pentium performance counters" else "Unix.time"); let gc = Gc.quick_stat () in let printM (w: float) : string = Printf.sprintf "%.2fMb" (w *. 4.0 /. 1000000.0) in Printf.fprintf chn "Memory statistics: total=%s, max=%s, minor=%s, major=%s, promoted=%s\n minor collections=%d major collections=%d compactions=%d\n" (printM (gc.Gc.minor_words +. gc.Gc.major_words -. gc.Gc.promoted_words)) (printM (float_of_int gc.Gc.top_heap_words)) (printM gc.Gc.minor_words) (printM gc.Gc.major_words) (printM gc.Gc.promoted_words) gc.Gc.minor_collections gc.Gc.major_collections gc.Gc.compactions; () (* Get the current time, in seconds *) let get_current_time () : float = if !timerMode = HardwareTimer then read_pentium_perfcount () else (Unix.times ()).Unix.tms_utime let repeattime limit str f arg = (* Find the right stat *) let stat : t = let curr = match !current with h :: _ -> h | [] -> assert false in let rec loop = function h :: _ when h.name = str -> h | _ :: rest -> loop rest | [] -> let nw = {name = str; time = 0.0; ncalls = 0; sub = []} in curr.sub <- nw :: curr.sub; nw in loop curr.sub in let oldcurrent = !current in current := stat :: oldcurrent; let start = get_current_time () in let rec repeatf count = let finish diff = (* count each call to repeattime once *) if !countCalls then stat.ncalls <- stat.ncalls + 1; stat.time <- stat.time +. (diff /. float(count)); current := oldcurrent; (* Pop the current stat *) () in let res = try f arg with e -> let diff = get_current_time () -. start in finish diff; raise e in let diff = get_current_time () -. start in if diff < limit then repeatf (count + 1) else begin finish diff; res (* Return the function result *) end in repeatf 1 let time str f arg = if !timerMode = Disabled then f arg else repeattime 0.0 str f arg let lastTime = ref 0.0 let timethis (f: 'a -> 'b) (arg: 'a) : 'b = let start = get_current_time () in let res = f arg in lastTime := get_current_time () -. start; res (** Return the cumulative time of all calls to {!Stats.time} and {!Stats.repeattime} with the given label. *) (* Usually there will be only one occurence in the tree, but summing them all makes more sense than choosing one arbitrarily *) let lookupTime (label:string) : float = let time : float ref = ref 0.0 in let rec search (x:t) : unit = if x.name = label then time := !time +. x.time; List.iter search x.sub in search top; !time