Skip to content

Commit 9fb844f

Browse files
gabeleviFacebook Github Bot
authored andcommitted
Include CPU times in profiling
Summary: Our automated performance profiling thus far has been using wall times. Wall times are somewhat sensitive to environmental factors, though. The hope is that CPU times will be more reliable. This is a little tricky, since each worker needs to record its own CPU times and send them back to the master process. Luckily, we already have the `Measure` library which does this :) Reviewed By: samwgoldman Differential Revision: D4282968 fbshipit-source-id: c8a989c0b97d501d48d032852d40a5c91da4d9bd
1 parent 572563c commit 9fb844f

5 files changed

Lines changed: 110 additions & 13 deletions

File tree

hack/heap/sharedMem.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ external hh_check_heap_overflow: unit -> bool = "hh_check_heap_overflow"
199199

200200
let init_done () =
201201
hh_init_done ();
202-
Measure.print_stats ();
202+
if hh_log_level() > 0 then Measure.print_stats ();
203203
EventLogger.sharedmem_init_done (heap_size ())
204204

205205
type table_stats = {

hack/procs/worker.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,15 @@ and 'a slave = {
139139
*****************************************************************************)
140140

141141
let slave_main ic oc =
142+
let start_user_time = ref 0.0 in
143+
let start_system_time = ref 0.0 in
142144
let send_result data =
145+
let tm = Unix.times () in
146+
let end_user_time = tm.Unix.tms_utime +. tm.Unix.tms_cutime in
147+
let end_system_time = tm.Unix.tms_stime +. tm.Unix.tms_cstime in
148+
Measure.sample "worker_user_time" (end_user_time -. !start_user_time);
149+
Measure.sample "worker_system_time" (end_system_time -. !start_system_time);
150+
143151
let stats = Measure.serialize (Measure.pop_global ()) in
144152
let s = Marshal.to_string (data,stats) [Marshal.Closures] in
145153
let len = String.length s in
@@ -154,7 +162,11 @@ let slave_main ic oc =
154162
Daemon.output_string oc s;
155163
Daemon.flush oc in
156164
try
165+
Measure.push_global ();
157166
let Request do_process = Daemon.from_channel ic in
167+
let tm = Unix.times () in
168+
start_user_time := tm.Unix.tms_utime +. tm.Unix.tms_cutime;
169+
start_system_time := tm.Unix.tms_stime +. tm.Unix.tms_cstime;
158170
do_process { send = send_result };
159171
exit 0
160172
with

hack/utils/measure.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@ let time (type a) ?record name (f: unit -> a) : a =
239239
sample ~record name (end_time -. start_time);
240240
ret
241241

242+
let get_sum ?record name =
243+
let record = get_record record in
244+
match SMap.get name !record with
245+
| None -> None
246+
| Some { count; mean; _; } -> Some (float_of_int count *. mean)
247+
242248
let pretty_num f =
243249
if f > 1000000000.0
244250
then Printf.sprintf "%.3fG" (f /. 1000000000.0)

hack/utils/measure.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ val time: ?record:record -> string -> (unit -> 'a) -> 'a
2626

2727
val merge: ?record:record -> from:record -> unit
2828

29+
val get_sum: ?record:record -> string -> float option
30+
2931
val print_entry_stats: ?record:record -> string -> unit
3032
val print_stats: ?record:record -> unit -> unit
3133
val print_entry_distribution: ?record:record -> string -> unit

src/common/profiling_js.ml

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,63 @@
1616
val get_finished_timer: timer:string -> t -> (float * float) option
1717
val to_json: t -> Hh_json.json
1818
end = struct
19+
type time_measurement = {
20+
start_age: float;
21+
duration: float;
22+
}
23+
1924
type result = {
20-
start_wall_age: float;
21-
wall_duration: float;
25+
user: time_measurement;
26+
system: time_measurement;
27+
worker_user: time_measurement;
28+
worker_system: time_measurement;
29+
wall: time_measurement;
30+
}
31+
32+
type running_timer = {
33+
user_start: float;
34+
system_start: float;
35+
worker_user_start: float;
36+
worker_system_start: float;
37+
wall_start: float;
2238
}
2339

2440
type t = {
25-
running_timers: float SMap.t;
41+
running_timers: running_timer SMap.t;
2642
results: result SMap.t;
2743
}
2844

2945
let empty = { running_timers = SMap.empty; results = SMap.empty }
3046

47+
(* Returns the user cpu and system cpu times *)
48+
let times () = Unix.(
49+
let tm = times () in
50+
(* Warning - cutime and cstime (children times) don't work on Windows *)
51+
(tm.tms_utime +. tm.tms_cutime, tm.tms_stime +. tm.tms_cstime)
52+
)
53+
54+
let worker_times () =
55+
let worker_user_time = match Measure.get_sum "worker_user_time" with
56+
| None -> 0.0
57+
| Some time -> time in
58+
let worker_system_time = match Measure.get_sum "worker_system_time" with
59+
| None -> 0.0
60+
| Some time -> time in
61+
(worker_user_time, worker_system_time)
62+
3163
let start_timer ~timer { running_timers; results; } =
64+
let wall_start = Unix.gettimeofday () in
65+
let (user_start, system_start) = times () in
66+
let (worker_user_start, worker_system_start) = worker_times () in
67+
let running_timer = {
68+
user_start;
69+
system_start;
70+
worker_user_start;
71+
worker_system_start;
72+
wall_start;
73+
} in
3274
{
33-
running_timers = SMap.add timer (Unix.gettimeofday ()) running_timers;
75+
running_timers = SMap.add timer running_timer running_timers;
3476
results;
3577
}
3678

@@ -39,10 +81,31 @@
3981
let stop_timer ~timer { running_timers; results; } =
4082
match SMap.get timer running_timers with
4183
| None -> { running_timers; results; }
42-
| Some timer_start_time ->
84+
| Some running_timer ->
85+
let wall_end = Unix.gettimeofday () in
86+
let (user_end, system_end) = times () in
87+
let (worker_user_end, worker_system_end) = worker_times () in
4388
let result = {
44-
start_wall_age = timer_start_time -. flow_start_time;
45-
wall_duration = Unix.gettimeofday () -. timer_start_time;
89+
user = {
90+
start_age= running_timer.user_start;
91+
duration= user_end -. running_timer.user_start;
92+
};
93+
system = {
94+
start_age= running_timer.system_start;
95+
duration= system_end -. running_timer.system_start;
96+
};
97+
worker_user = {
98+
start_age= running_timer.worker_user_start;
99+
duration= worker_user_end -. running_timer.worker_user_start;
100+
};
101+
worker_system = {
102+
start_age= running_timer.worker_system_start;
103+
duration= worker_system_end -. running_timer.worker_system_start;
104+
};
105+
wall = {
106+
start_age = running_timer.wall_start -. flow_start_time;
107+
duration = wall_end -. running_timer.wall_start;
108+
}
46109
} in
47110
{
48111
running_timers = SMap.remove timer running_timers;
@@ -52,15 +115,29 @@
52115
let get_finished_timer ~timer { results; _; } =
53116
match SMap.get timer results with
54117
| None -> None
55-
| Some { start_wall_age; wall_duration; } ->
56-
Some (start_wall_age, wall_duration)
118+
| Some { wall = { start_age; duration; }; _; } ->
119+
Some (start_age, duration)
120+
121+
let json_of_time_measurement { start_age; duration; } =
122+
let open Hh_json in
123+
let open Utils_js in
124+
JSON_Object [
125+
"start_age", JSON_Number (string_of_float_trunc start_age);
126+
"duration", JSON_Number (string_of_float_trunc duration);
127+
]
57128

58-
let json_of_result { start_wall_age; wall_duration; } =
129+
let json_of_result { wall; user; system; worker_user; worker_system; } =
59130
let open Hh_json in
60131
let open Utils_js in
61132
JSON_Object [
62-
"start_wall_age", JSON_Number (string_of_float_trunc start_wall_age);
63-
"wall_duration", JSON_Number (string_of_float_trunc wall_duration);
133+
"wall", json_of_time_measurement wall;
134+
"user", json_of_time_measurement user;
135+
"system", json_of_time_measurement system;
136+
"worker_user", json_of_time_measurement worker_user;
137+
"worker_system", json_of_time_measurement worker_system;
138+
(* legacy fields *)
139+
"start_wall_age", JSON_Number (string_of_float_trunc wall.start_age);
140+
"wall_duration", JSON_Number (string_of_float_trunc wall.duration);
64141
]
65142

66143
let to_json { results; _; } =

0 commit comments

Comments
 (0)