-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy patholly_gc_stats.5.3.ml
More file actions
431 lines (405 loc) · 14.8 KB
/
Copy patholly_gc_stats.5.3.ml
File metadata and controls
431 lines (405 loc) · 14.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
module H = Hdr_histogram
module Ts = Runtime_events.Timestamp
type ts = { mutable start_time : float; mutable end_time : float }
(* Maximum number of domains that can be active concurrently.
Defaults to 128 on 64-bit platforms and 16 on 32-bit platforms.
This can be user configurable with OCAMLRUNPARAM=d=XXX
*)
let number_domains = 128
let wall_time = { start_time = 0.; end_time = 0. }
let domain_elapsed_times = Array.make number_domains 0.
let domain_gc_times = Array.make number_domains 0
let domain_minor_words = Array.make number_domains 0
let domain_promoted_words = Array.make number_domains 0
let domain_major_words = Array.make number_domains 0
let minor_collections = ref 0
let major_collections = ref 0
let forced_major_collections = ref 0
let compactions = ref 0
let lifecycle domain_id ts lifecycle_event _data =
let ts = float_of_int Int64.(to_int @@ Ts.to_int64 ts) /. 1_000_000_000. in
match lifecycle_event with
| Runtime_events.EV_RING_START ->
wall_time.start_time <- ts;
domain_elapsed_times.(domain_id) <- ts
| Runtime_events.EV_RING_STOP ->
wall_time.end_time <- ts;
domain_elapsed_times.(domain_id) <- ts -. domain_elapsed_times.(domain_id)
| Runtime_events.EV_DOMAIN_SPAWN -> domain_elapsed_times.(domain_id) <- ts
| Runtime_events.EV_DOMAIN_TERMINATE ->
domain_elapsed_times.(domain_id) <- ts -. domain_elapsed_times.(domain_id)
| _ -> ()
let print_table oc (data : string list list) =
let column_widths =
List.fold_left
(fun widths row -> List.map2 max widths (List.map String.length row))
(List.map String.length (List.hd data))
(List.tl data)
in
let print_row row =
let formatted_row =
List.map2 (fun s w -> Printf.sprintf "%-*s" w s) row column_widths
in
Printf.fprintf oc "%s \n" (String.concat " " formatted_row)
in
List.iter print_row data
let print_global_allocation_stats oc =
Printf.fprintf oc "GC allocations (in words): \n";
let minor_words = ref 0.0 in
let major_words = ref 0.0 in
let promoted_words = ref 0.0 in
Array.iteri
(fun i v ->
minor_words := !minor_words +. float_of_int v;
major_words := !major_words +. float_of_int domain_major_words.(i);
promoted_words :=
!promoted_words +. float_of_int domain_promoted_words.(i))
domain_minor_words;
Printf.fprintf oc "Total heap:\t %.0f\n" (!minor_words -. !promoted_words);
Printf.fprintf oc "Total heap:\t %.0f\n"
(!minor_words -. !promoted_words +. !major_words);
Printf.fprintf oc "Minor heap:\t %.0f\n" !minor_words;
Printf.fprintf oc "Major heap:\t %.0f\n" !major_words;
Printf.fprintf oc "Promoted words:\t %.0f (%.2f%%)\n" !promoted_words
(!promoted_words /. !minor_words *. 100.0);
Printf.fprintf oc "\n"
let print_per_domain_stats oc =
Printf.fprintf oc "Per domain stats: \n";
let data =
ref [ [ "Domain"; "Total"; "Minor"; "Promoted"; "Major"; "Promoted(%)" ] ]
in
Array.iteri
(fun i (domain_major_word, (domain_minor_word, domain_promoted_word)) ->
if domain_major_word > 0 then
data :=
List.append !data
[
[
string_of_int i;
string_of_int
(domain_minor_word - domain_promoted_word + domain_major_word);
string_of_int domain_minor_word;
string_of_int domain_promoted_word;
string_of_int domain_major_word;
Printf.sprintf "%.2f"
(float_of_int domain_promoted_word
/. float_of_int domain_minor_word
*. 100.0);
];
])
(Array.combine domain_minor_words domain_promoted_words
|> Array.combine domain_major_words);
print_table oc !data
let print_percentiles json output hist =
let to_sec x = float_of_int x /. 1_000_000_000. in
let ms ns = ns /. 1_000_000. in
let mean_latency = H.mean hist |> ms
and max_latency = float_of_int (H.max hist) |> ms in
let percentiles =
[|
25.0;
50.0;
60.0;
70.0;
75.0;
80.0;
85.0;
90.0;
95.0;
96.0;
97.0;
98.0;
99.0;
99.9;
99.99;
99.999;
99.9999;
100.0;
|]
in
let oc = match output with Some s -> open_out s | None -> stderr in
let real_time = wall_time.end_time -. wall_time.start_time in
let total_gc_time = to_sec @@ Array.fold_left ( + ) 0 domain_gc_times in
let total_cpu_time = ref 0. in
let ap = Array.combine domain_elapsed_times domain_gc_times in
Array.iteri
(fun i (cpu_time, gc_time) ->
if gc_time > 0 && cpu_time = 0. then
Printf.fprintf stderr
"[Olly] Warning: Domain %d has GC time but no CPU time\n" i
else total_cpu_time := !total_cpu_time +. cpu_time)
ap;
let gc_overhead = total_gc_time /. !total_cpu_time *. 100. in
let stddev_latency = H.stddev hist |> ms in
let min_latency = float_of_int (H.min hist) |> ms in
let minor_words = ref 0.0 in
let major_words = ref 0.0 in
let promoted_words = ref 0.0 in
Array.iteri
(fun i v ->
minor_words := !minor_words +. float_of_int v;
major_words := !major_words +. float_of_int domain_major_words.(i);
promoted_words :=
!promoted_words +. float_of_int domain_promoted_words.(i))
domain_minor_words;
let total_heap = !minor_words -. !promoted_words +. !major_words in
let promoted_pct = !promoted_words /. !minor_words *. 100.0 in
if json then
let distribs =
List.init (Array.length percentiles) (fun i ->
let percentile = percentiles.(i) in
let value =
H.value_at_percentile hist percentiles.(i)
|> float_of_int |> ms |> string_of_float
in
Printf.sprintf "\"%.4f\": %s" percentile value)
|> String.concat ","
in
let domain_stats =
let buf = Buffer.create 256 in
Array.iteri
(fun i (c, g) ->
if c > 0. then (
if Buffer.length buf > 0 then Buffer.add_char buf ',';
Buffer.add_string buf
(Printf.sprintf
{|"%d": {"wall_time": %.2f, "gc_time": %.2f, "gc_overhead": %.2f}|}
i c (to_sec g)
(to_sec g *. 100. /. c))))
(Array.combine domain_elapsed_times domain_gc_times);
Buffer.contents buf
in
let domain_alloc_stats =
let buf = Buffer.create 256 in
Array.iteri
(fun i (domain_major_word, (domain_minor_word, domain_promoted_word)) ->
if domain_major_word > 0 then (
if Buffer.length buf > 0 then Buffer.add_char buf ',';
Buffer.add_string buf
(Printf.sprintf
{|"%d": {"total": %d, "minor": %d, "promoted": %d, "major": %d, "promoted_pct": %.2f}|}
i
(domain_minor_word - domain_promoted_word + domain_major_word)
domain_minor_word domain_promoted_word domain_major_word
(float_of_int domain_promoted_word
/. float_of_int domain_minor_word
*. 100.0))))
(Array.combine domain_minor_words domain_promoted_words
|> Array.combine domain_major_words);
Buffer.contents buf
in
Printf.fprintf oc
{|{"version": 1, "wall_time": %.2f, "cpu_time": %.2f, "gc_time": %.2f, "gc_overhead": %.2f, "domain_stats": {%s}, "mean_latency": %f, "stddev_latency": %f, "min_latency": %.2f, "max_latency": %f, "distr_latency": {%s}, "allocations": {"total_heap": %.0f, "minor_heap": %.0f, "major_heap": %.0f, "promoted_words": %.0f, "promoted_pct": %.2f}, "domain_alloc_stats": {%s}, "collections": {"minor": %i, "major": %i, "forced_major": %i, "compactions": %i}}|}
real_time !total_cpu_time total_gc_time gc_overhead domain_stats
mean_latency stddev_latency min_latency max_latency distribs total_heap
!minor_words !major_words !promoted_words promoted_pct domain_alloc_stats
!minor_collections !major_collections !forced_major_collections
!compactions
else (
Printf.fprintf oc "\n";
Printf.fprintf oc "Execution times:\n";
Printf.fprintf oc "Wall time (s):\t%.2f\n" real_time;
Printf.fprintf oc "CPU time (s):\t%.2f\n" !total_cpu_time;
Printf.fprintf oc "GC time (s):\t%.2f\n" total_gc_time;
Printf.fprintf oc "GC overhead (%% of CPU time):\t%.2f%%\n" gc_overhead;
Printf.fprintf oc "\n";
Printf.fprintf oc "Per domain stats:\n";
let data = ref [ [ "Domain"; "Wall"; "GC(s)"; "GC(%)" ] ] in
Array.iteri
(fun i (c, g) ->
if c > 0. then
data :=
List.append !data
[
[
string_of_int i;
Printf.sprintf "%.2f" c;
Printf.sprintf "%.2f" (to_sec g);
Printf.sprintf "%.2f" (to_sec g *. 100. /. c);
];
])
(Array.combine domain_elapsed_times domain_gc_times);
print_table oc !data;
Printf.fprintf oc "\n";
Printf.fprintf oc "GC latency profile:\n";
Printf.fprintf oc "#[Mean (ms):\t%.2f,\t Stddev (ms):\t%.2f]\n" mean_latency
stddev_latency;
Printf.fprintf oc "#[Min (ms):\t%.2f,\t max (ms):\t%.2f]\n" min_latency
max_latency;
Printf.fprintf oc "\n";
Printf.fprintf oc "Percentile \t Latency (ms)\n";
Fun.flip Array.iter percentiles (fun p ->
Printf.fprintf oc "%.4f \t %.2f\n" p
(float_of_int (H.value_at_percentile hist p) |> ms));
Printf.fprintf oc "\n";
print_global_allocation_stats oc;
print_per_domain_stats oc;
Printf.fprintf oc "Minor Gen: %i collections\n" !minor_collections;
Printf.fprintf oc "Major Gen: %i collections %i forced collections\n"
!major_collections !forced_major_collections;
Printf.fprintf oc "Compactions: %i\n" !compactions)
let print_latency_only json output hist =
let ms ns = ns /. 1_000_000. in
let mean_latency = H.mean hist |> ms
and max_latency = float_of_int (H.max hist) |> ms in
let percentiles =
[|
25.0;
50.0;
60.0;
70.0;
75.0;
80.0;
85.0;
90.0;
95.0;
96.0;
97.0;
98.0;
99.0;
99.9;
99.99;
99.999;
99.9999;
100.0;
|]
in
let oc = match output with Some s -> open_out s | None -> stderr in
if json then
let distribs =
List.init (Array.length percentiles) (fun i ->
let percentile = percentiles.(i) in
let value =
H.value_at_percentile hist percentiles.(i)
|> float_of_int |> ms |> string_of_float
in
Printf.sprintf "\"%.4f\": %s" percentile value)
|> String.concat ","
in
Printf.fprintf oc
{|{"mean_latency": %f, "max_latency": %f, "distr_latency": {%s}}|}
mean_latency max_latency distribs
else (
Printf.fprintf oc "\n";
Printf.fprintf oc "GC latency profile:\n";
Printf.fprintf oc "#[Mean (ms):\t%.2f,\t Stddev (ms):\t%.2f]\n" mean_latency
(H.stddev hist |> ms);
Printf.fprintf oc "#[Min (ms):\t%.2f,\t max (ms):\t%.2f]\n"
(float_of_int (H.min hist) |> ms)
max_latency;
Printf.fprintf oc "\n";
Printf.fprintf oc "Percentile \t Latency (ms)\n";
Fun.flip Array.iter percentiles (fun p ->
Printf.fprintf oc "%.4f \t %.2f\n" p
(float_of_int (H.value_at_percentile hist p) |> ms)))
let latency poll_sleep json output runtime_events_dir exec_args =
let current_event = Hashtbl.create 13 in
let hist =
H.init ~lowest_discernible_value:10 ~highest_trackable_value:10_000_000_000
~significant_figures:3
in
let is_gc_phase phase =
match phase with
| Runtime_events.EV_MAJOR | Runtime_events.EV_STW_LEADER
| Runtime_events.EV_INTERRUPT_REMOTE ->
true
| _ -> false
in
let runtime_begin ring_id ts phase =
if is_gc_phase phase then
match Hashtbl.find_opt current_event ring_id with
| None -> Hashtbl.add current_event ring_id (phase, Ts.to_int64 ts)
| _ -> ()
in
let runtime_end ring_id ts phase =
match Hashtbl.find_opt current_event ring_id with
| Some (saved_phase, saved_ts) when saved_phase = phase ->
Hashtbl.remove current_event ring_id;
let latency = Int64.to_int (Int64.sub (Ts.to_int64 ts) saved_ts) in
assert (H.record_value hist latency)
| _ -> ()
in
let init = Fun.id in
let cleanup () = print_latency_only json output hist in
let open Olly_common.Launch in
try
`Ok
(olly
{
empty_config with
runtime_begin;
runtime_end;
init;
cleanup;
poll_sleep;
runtime_events_dir;
}
exec_args)
with Fail msg -> `Error (false, msg)
let gc_stats poll_sleep json output runtime_events_dir runtime_events_log_wsize
exec_args =
let current_event = Hashtbl.create 13 in
let hist =
H.init ~lowest_discernible_value:10 ~highest_trackable_value:10_000_000_000
~significant_figures:3
in
let is_gc_phase phase =
match phase with
| Runtime_events.EV_MAJOR | Runtime_events.EV_STW_LEADER
| Runtime_events.EV_INTERRUPT_REMOTE ->
true
| _ -> false
in
let runtime_begin ring_id ts phase =
if phase == Runtime_events.EV_EXPLICIT_GC_COMPACT && ring_id == 0 then
incr compactions;
if phase == Runtime_events.EV_MINOR && ring_id == 0 then
incr minor_collections;
(* Runtime_events.EV_MAJOR seems to correspond to any GC collection,
be more specific and use stop-the-world phase done at the end of
a major GC cycle *)
if phase == Runtime_events.EV_MAJOR_GC_STW && ring_id == 0 then
incr major_collections;
if
(phase == Runtime_events.EV_EXPLICIT_GC_MAJOR
|| phase == Runtime_events.EV_EXPLICIT_GC_FULL_MAJOR)
&& ring_id == 0
then incr forced_major_collections;
if is_gc_phase phase then
match Hashtbl.find_opt current_event ring_id with
| None -> Hashtbl.add current_event ring_id (phase, Ts.to_int64 ts)
| _ -> ()
in
let runtime_end ring_id ts phase =
match Hashtbl.find_opt current_event ring_id with
| Some (saved_phase, saved_ts) when saved_phase = phase ->
Hashtbl.remove current_event ring_id;
let latency = Int64.to_int (Int64.sub (Ts.to_int64 ts) saved_ts) in
assert (H.record_value hist latency);
domain_gc_times.(ring_id) <- domain_gc_times.(ring_id) + latency
| _ -> ()
in
let runtime_counter =
Gc_counters_shim.runtime_counter ~domain_minor_words ~domain_promoted_words
~domain_major_words
in
let init = Fun.id in
let cleanup () = print_percentiles json output hist in
let open Olly_common.Launch in
try
`Ok
(olly
{
empty_config with
runtime_begin;
runtime_end;
runtime_counter;
lifecycle;
init;
cleanup;
poll_sleep;
runtime_events_dir;
runtime_events_log_wsize;
}
exec_args)
with Fail msg -> `Error (false, msg)