Make the time function re-entrant.
[SXSI/xpathcomp.git] / src / utils.ml
1 module String =
2 struct
3   include String
4
5   let explode s sep =
6     let len = length s in
7     let buff = Buffer.create 40 in
8     let rec loop i =
9       if i >= len then
10         [ Buffer.contents buff ]
11       else
12         let c = s.[i] in
13         if c == sep then
14           let ss = Buffer.contents buff in
15           Buffer.clear buff;
16           ss :: loop (i+1)
17         else begin
18           Buffer.add_char buff c;
19           loop (i+1);
20         end
21     in
22     loop 0
23 end
24 ;;
25
26 module System =
27 struct
28   let status () =
29     let pid = Unix.getpid() in
30     let cin = open_in (Printf.sprintf "/proc/%i/status" pid) in
31     let h = Hashtbl.create 17 in
32     try
33       while true do
34         let s = input_line cin in
35         Scanf.sscanf s "%s@: %s@\n" (fun k v ->
36           Hashtbl.replace h k v)
37       done;
38       (* never reached *)
39       h
40     with
41       End_of_file -> begin
42         close_in cin;
43         h
44       end
45   let get_status s = Hashtbl.find (status()) s
46
47   let pr_mem_status fmt h =
48     Format.fprintf fmt "[";
49     List.iter (fun k ->
50       let v = Hashtbl.find h k in
51       Format.fprintf fmt "%s: %s " k v
52     ) [ "VmStk"; "VmRSS"; "VmPeak" ];
53     Format.fprintf fmt "]"
54
55 end
56
57 let time f ?(count=1) ?(msg="") x =
58   if not !Config.verbose then f x
59   else
60   let rec loop i =
61     Gc.compact();
62     let oldmem = System.status () in
63     let t1 = Unix.gettimeofday () in
64     let r = f x in
65     let t2 = Unix.gettimeofday () in
66     let newmem = System.status () in
67     let t = (1000. *. (t2 -. t1)) in
68     Logger.verbose Format.err_formatter "@[%s: [" msg;
69     if (count != 1) then Logger.verbose Format.err_formatter "run %i/%i,  "  i count;
70     begin
71       Format.pp_open_vbox Format.err_formatter (2 + String.length msg);
72       Logger.verbose
73         Format.err_formatter
74         "@\n| Time: %fms@\n| Memory before: %a@\n| Memory after:  %a@\n]@]@]@\n" t System.pr_mem_status oldmem System.pr_mem_status newmem;
75     end;
76     if i >= count then r
77     else loop (i+1)
78   in
79   loop 1
80 ;;
81
82 module Timing =
83   struct
84     let _timings = Hashtbl.create 43
85     let _t_queue = Queue.create ()
86
87     let get_timing s =
88       try
89         Hashtbl.find _timings s
90       with
91         Not_found -> []
92
93     let set_timing s v =
94       Hashtbl.replace _timings s v
95
96
97     let display_result fmt msg sub l =
98       let h = Hashtbl.create 0 in
99       let tmin, tmax, ttotal, len, memo, memn =
100         List.fold_left (fun
101         (atmin, atmax, attotal, alen, _, _)
102         (t, om, nm) ->
103           (min t atmin,
104            max atmax t,
105            attotal +. t,
106            alen + 1,
107            om, nm))
108           (infinity, 0., 0., 0, h, h) l
109       in
110       Logger.verbose fmt "@[%s%s: [" sub msg;
111       Format.pp_open_vbox fmt (2 + String.length msg + String.length sub);
112       Logger.verbose fmt
113         "@\n\
114 | Number of runs: %i@\n\
115 | Average time:   %fms@\n\
116 | Minimum time:   %fms@\n\
117 | Maximum time:   %fms@\n\
118 | Memory before:  %a@\n\
119 | Memory after:   %a@\n]@]@]@\n"
120         len
121         (ttotal /. (float_of_int len))
122         tmin
123         tmax
124         System.pr_mem_status memo
125         System.pr_mem_status memn
126
127
128 let time f ?(count=1) ?(msg="") x =
129   if not !Config.verbose then f x
130   else
131   let rec loop i =
132     let oldmem = System.status () in
133     let t1 = Unix.gettimeofday () in
134     set_timing msg ((t1, oldmem, oldmem)::(get_timing msg));
135     let r = f x in
136     let t2 = Unix.gettimeofday () in
137     let newmem = System.status () in
138     let t1, oldmem, l =
139       match get_timing msg with
140         (a, b, _) :: l -> a,b,l
141       | _ -> assert false
142     in
143     let t = (1000. *. (t2 -. t1)) in
144     set_timing msg ((t, oldmem, newmem)::l);
145     if i >= count then r
146     else loop (i+1)
147   in
148   Queue.push msg _t_queue;
149   let r = loop 1 in
150   begin
151     if (Queue.peek _t_queue) = msg then
152     let pr_stack = Queue.fold (fun a e -> e::a)  [] _t_queue in
153     Queue.clear _t_queue;
154     List.iter (fun msg' ->
155       let sub =
156         if msg' <> msg then "(sub-timing) " else ""
157       in
158       display_result Format.err_formatter msg' sub (get_timing msg')) pr_stack;
159   end;
160   r
161
162
163   end
164 let time = Timing.time