Source file stats.ml
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
open! Import
(** Ensure lists are not growing indefinitely by dropping elements. *)
let limit_length_list = 10
let minimum_seconds_to_be_considered_long = 1.0
type counters = {
mutable contents : int;
mutable nodes : int;
mutable commits : int;
mutable branches : int;
mutable adds : int;
mutable skip_tests : int;
mutable skips : int;
mutable yields : int;
}
type freeze_profile = {
idx : int;
past_adds : int;
t0 : Mtime.t;
mutable t1 : Mtime.t;
mutable current_section : string;
mutable current_counters : counters;
mutable rev_timeline : (string * Mtime.t * float * counters) list;
mutable copy_newies_loops : int;
mutable outside_totlen : float;
mutable inside_totlen : float;
mutable outside_maxlen : string * float;
mutable inside_maxlen : string * float;
mutable rev_longest_yields : (string, int * float) Hashtbl.t;
mutable rev_longest_blocks : (string, int * float) Hashtbl.t;
}
let fresh_counters () =
{
contents = 0;
nodes = 0;
commits = 0;
branches = 0;
adds = 0;
skips = 0;
skip_tests = 0;
yields = 0;
}
let fresh_freeze_profile idx t0 initial_section past_adds =
{
idx;
past_adds;
t0;
t1 = t0;
current_section = initial_section;
current_counters = fresh_counters ();
rev_timeline = [];
copy_newies_loops = 0;
outside_totlen = 0.;
inside_totlen = 0.;
outside_maxlen = ("never", 0.);
inside_maxlen = ("never", 0.);
rev_longest_yields = Hashtbl.create 2;
rev_longest_blocks = Hashtbl.create 2;
}
let get_elapsed =
let c = ref (Mtime_clock.counter ()) in
fun ~reset ->
let elapsed = Mtime.Span.to_s (Mtime_clock.count !c) in
if reset then c := Mtime_clock.counter ();
elapsed
let freeze_start_counter =
let c = ref (-1) in
fun () ->
incr c;
!c
let freeze_profiles = ref []
let latest = ref (fresh_freeze_profile (-1) (Mtime_clock.now ()) "" 0)
let are_all_counters_zero c = c = fresh_counters ()
let reset_stats () =
freeze_profiles := [];
latest := fresh_freeze_profile (-1) (Mtime_clock.now ()) "" 0
let freeze_start t0 initial_section =
let past_adds = !latest.current_counters.adds in
let (_ : float) = get_elapsed ~reset:true in
latest :=
fresh_freeze_profile (freeze_start_counter ()) t0 initial_section past_adds
let freeze_section ev_name' =
let ev_name = !latest.current_section in
let now = Mtime_clock.now () in
let now_inside = get_elapsed ~reset:false +. !latest.inside_totlen in
let c = !latest.current_counters in
!latest.current_counters <- fresh_counters ();
!latest.current_section <- ev_name';
!latest.rev_timeline <- (ev_name, now, now_inside, c) :: !latest.rev_timeline
let copy_contents () =
!latest.current_counters.contents <- succ !latest.current_counters.contents
let copy_nodes () =
!latest.current_counters.nodes <- succ !latest.current_counters.nodes
let copy_commits () =
!latest.current_counters.commits <- succ !latest.current_counters.commits
let copy_branches () =
!latest.current_counters.branches <- succ !latest.current_counters.branches
let add () =
!latest.current_counters.adds <- succ !latest.current_counters.adds
let skip_test should_skip =
!latest.current_counters.skip_tests <-
succ !latest.current_counters.skip_tests;
if should_skip then
!latest.current_counters.skips <- succ !latest.current_counters.skips
let copy_newies_loop () =
!latest.copy_newies_loops <- succ !latest.copy_newies_loops
let fold_counters v f =
List.fold_left
(fun acc (_, _, _, c) -> acc + f c)
(f v.current_counters) v.rev_timeline
let get_add_count () = fold_counters !latest (fun c -> c.adds)
let get_copied_commits_count () = fold_counters !latest (fun c -> c.commits)
let get_copied_branches_count () = fold_counters !latest (fun c -> c.branches)
let get_copied_contents_count () = fold_counters !latest (fun c -> c.contents)
let get_copied_nodes_count () = fold_counters !latest (fun c -> c.nodes)
let get_freeze_count () = List.length !freeze_profiles
let freeze_yield () =
!latest.current_counters.yields <- succ !latest.current_counters.yields;
let d1 = get_elapsed ~reset:true in
let d0 = !latest.inside_totlen in
!latest.inside_totlen <- d0 +. d1;
let _, d0 = !latest.inside_maxlen in
if d1 > d0 then !latest.inside_maxlen <- (!latest.current_section, d1);
if d1 >= minimum_seconds_to_be_considered_long then
let tbl = !latest.rev_longest_blocks in
let s = !latest.current_section in
let new_entry =
match Hashtbl.find_opt tbl s with
| None -> (1, d1)
| Some (i, d) -> (i + 1, d +. d1)
in
Hashtbl.replace tbl s new_entry
let freeze_yield_end () =
let d1 = get_elapsed ~reset:true in
let d0 = !latest.outside_totlen in
!latest.outside_totlen <- d0 +. d1;
let _, d0 = !latest.outside_maxlen in
if d1 > d0 then !latest.outside_maxlen <- (!latest.current_section, d1);
if d1 >= minimum_seconds_to_be_considered_long then
let tbl = !latest.rev_longest_yields in
let s = !latest.current_section in
let new_entry =
match Hashtbl.find_opt tbl s with
| None -> (1, d1)
| Some (i, d) -> (i + 1, d +. d1)
in
Hashtbl.replace tbl s new_entry
let freeze_stop () =
let v = !latest in
freeze_yield ();
v.current_counters.yields <- pred v.current_counters.yields;
v.t1 <- Mtime_clock.now ();
v.rev_timeline <-
(v.current_section, Mtime_clock.now (), v.inside_totlen, v.current_counters)
:: v.rev_timeline;
v.current_counters <- fresh_counters ();
let shorter ls =
List.fold_left
(fun (acc, i) x ->
if i < limit_length_list then (x :: acc, i + 1) else (acc, i + 1))
([], 0) ls
|> fst
|> List.rev
in
freeze_profiles := shorter (v :: !freeze_profiles)
let pp_latest_when_any ppf v =
let ongoing = Mtime.equal v.t0 v.t1 in
let timeline =
let l, t0, t0_block =
List.fold_right
(fun (s, t1, t1_block, counters) (acc, t0, t0_block) ->
let span = Mtime.span t0 t1 in
let span_block = t1_block -. t0_block in
let data = (s, span, span_block, counters, false) in
(data :: acc, t1, t1_block))
v.rev_timeline ([], v.t0, 0.)
in
let l =
if not ongoing then l
else
let t1 = Mtime_clock.now () in
let t1_block = get_elapsed ~reset:false +. v.inside_totlen in
let span = Mtime.span t0 t1 in
let span_block = t1_block -. t0_block in
(v.current_section, span, span_block, v.current_counters, true) :: l
in
List.rev l
in
let totlen =
if ongoing then Mtime.span v.t0 (Mtime_clock.now ())
else Mtime.span v.t0 v.t1
in
let frac_out, frac_in =
let totlen = Mtime.Span.to_s totlen in
(v.outside_totlen /. totlen, v.inside_totlen /. totlen)
in
let pp_timeline_timings_section ppf (name, span, span_block, _, is_ongoing) =
let totlen = Mtime.Span.to_s totlen in
let span = Mtime.Span.to_s span in
let pp = Mtime.Span.pp_float_s in
let pp' ppf v = Format.fprintf ppf "%.0f%%" (v *. 100.) in
Format.fprintf ppf
"@\n %20s took %a (%a of total) and blocked %a (%a of total)%s." name
pp span pp' (span /. totlen) pp span_block pp'
(span_block /. v.inside_totlen)
(if is_ongoing then " (ongoing)" else "")
in
let pp_timeline_timings ppf =
Format.fprintf ppf "%a"
Fmt.(list ~sep:(any "") pp_timeline_timings_section)
timeline
in
let pp_timeline_counters_section ppf (name, _, _, c, is_ongoing) =
Format.fprintf ppf "@\n %20s %a%s." name
Fmt.(list ~sep:(any ", ") (pair ~sep:(any ":") string int))
[
("copy_contents", c.contents);
("copy_nodes", c.nodes);
("copy_commits", c.commits);
("copy_branches", c.branches);
("adds", c.adds);
("skips", c.skips);
("skip_tests", c.skip_tests);
("yields", c.yields);
]
(if is_ongoing then " (ongoing)" else "")
in
let pp_timeline_counters ppf =
let timeline =
List.filter
(fun (_, _, _, c, _) -> not @@ are_all_counters_zero c)
timeline
in
Format.fprintf ppf "%a"
Fmt.(list ~sep:(any "") pp_timeline_counters_section)
timeline
in
let pp_long_segment ppf (action_name, (max_section, max_len), tbl) =
if max_len = 0. then Format.fprintf ppf "No %ss" action_name
else if Hashtbl.length tbl = 0 then
Format.fprintf ppf "Longest %s: %a (during \"%s\")" action_name
Mtime.Span.pp_float_s max_len max_section
else
let pp_per_section ppf (section, (count, totlen)) =
let pp_if_max ppf =
if section = max_section then
Format.fprintf ppf " (max:%a)" Mtime.Span.pp_float_s max_len
in
if count = 1 then
Format.fprintf ppf "1 long %s in \"%s\" of %a" action_name section
Mtime.Span.pp_float_s totlen
else
Format.fprintf ppf "%d long %ss in \"%s\" of ~%a%t" count action_name
section Mtime.Span.pp_float_s
(totlen /. float_of_int count)
pp_if_max
in
Format.fprintf ppf "Longests %ss: [%a]" action_name
Fmt.(list ~sep:(any "; ") pp_per_section)
(Hashtbl.to_seq tbl |> List.of_seq)
in
Format.fprintf ppf
"freeze %d (%s) blocked %a (%.0f%%), and yielded %a (%.0f%%). Total %a. %d \
adds before freeze. Copy newies loops: %d.@\n\
\ %a.@\n\
\ %a.@\n\
\ Timeline timings: %t@\n\
\ Timeline counters: %t@\n"
v.idx
(if ongoing then "ongoing" else "finished")
Mtime.Span.pp_float_s v.inside_totlen (frac_in *. 100.)
Mtime.Span.pp_float_s v.outside_totlen (frac_out *. 100.) Mtime.Span.pp
totlen v.past_adds v.copy_newies_loops pp_long_segment
("block", v.inside_maxlen, v.rev_longest_blocks)
pp_long_segment
("yield", v.outside_maxlen, v.rev_longest_yields)
pp_timeline_timings pp_timeline_counters
let pp_latest ppf =
let v = !latest in
if v.idx = -1 then Format.fprintf ppf "No freeze started yet."
else pp_latest_when_any ppf v