Skip to content

Commit 407ab8f

Browse files
committed
irmin-pack: optimize GC memory usage
1 parent 63e6e74 commit 407ab8f

File tree

1 file changed

+67
-25
lines changed

1 file changed

+67
-25
lines changed

src/irmin-pack/unix/gc_worker.ml

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,69 @@ module Make (Args : Gc_args.S) = struct
3333

3434
val make : unit -> t
3535
val add : off:int63 -> len:int -> t -> unit
36-
val to_list : t -> (int63 * int) list
3736
val count : t -> int
37+
val iter : (off:int63 -> len:int63 -> unit) -> t -> unit
3838
end = struct
39-
type t = { mutable ranges : (int63 * int) list; mutable count : int }
39+
module Stack = struct
40+
type t =
41+
| Empty
42+
| Stack of { mutable len : int; arr : int63 array; prev : t }
43+
44+
let capacity =
45+
131_072 (* = 128*1024, a large but not too large chunk size *)
46+
47+
let make prev =
48+
Stack { len = 0; arr = Array.make capacity Int63.zero; prev }
49+
50+
let is_full = function Empty -> true | Stack s -> s.len >= capacity
51+
52+
let rec push_pair ~off ~len t =
53+
match t with
54+
| Stack s when not (is_full t) ->
55+
let i = s.len in
56+
s.len <- i + 2;
57+
s.arr.(i) <- off;
58+
s.arr.(i + 1) <- Int63.of_int len;
59+
t
60+
| _ -> push_pair ~off ~len (make t)
61+
62+
let rec iter_pair fn = function
63+
| Empty -> ()
64+
| Stack { len; arr; prev } ->
65+
assert (len mod 2 = 0);
66+
for i = (len / 2) - 1 downto 0 do
67+
let off = arr.(2 * i) in
68+
let len = arr.((2 * i) + 1) in
69+
fn ~off ~len
70+
done;
71+
iter_pair fn prev
72+
end
4073

41-
let make () = { ranges = []; count = 0 }
42-
let to_list t = t.ranges
43-
let count t = t.count
74+
type t = {
75+
mutable last : (int63 * int) option;
76+
mutable ranges : Stack.t;
77+
mutable count : int;
78+
}
4479

45-
let add_range ~off ~len lst =
46-
let off_end = Int63.(Syntax.(off + of_int len)) in
47-
match lst with
48-
| (off', len') :: rest when off_end = off' -> (off, len + len') :: rest
49-
| _ -> (off, len) :: lst
80+
let make () = { last = None; ranges = Stack.Empty; count = 0 }
81+
let count t = t.count
5082

5183
let add ~off ~len t =
5284
t.count <- t.count + 1;
53-
t.ranges <- add_range ~off ~len t.ranges
85+
let off_end = Int63.(Syntax.(off + of_int len)) in
86+
match t.last with
87+
| None -> t.last <- Some (off, len)
88+
| Some (off', len') when off_end = off' -> t.last <- Some (off, len + len')
89+
| Some (off', len') ->
90+
t.last <- Some (off, len);
91+
t.ranges <- Stack.push_pair ~off:off' ~len:len' t.ranges
92+
93+
let iter fn t =
94+
match t.last with
95+
| None -> assert (t.count = 0)
96+
| Some (off, len) ->
97+
fn ~off ~len:(Int63.of_int len);
98+
Stack.iter_pair fn t.ranges
5499
end
55100

56101
module Priority_queue = struct
@@ -278,7 +323,7 @@ module Make (Args : Gc_args.S) = struct
278323
Gc_stats.Worker.finish_current_step !stats "mapping: of reachable";
279324
stats :=
280325
Gc_stats.Worker.set_objects_traversed !stats (Live.count live_entries);
281-
Live.to_list live_entries
326+
live_entries
282327
in
283328

284329
let () =
@@ -299,9 +344,8 @@ module Make (Args : Gc_args.S) = struct
299344
|> Errs.log_if_error "GC: Close prefix after data copy")
300345
@@ fun () ->
301346
(* Step 5.1. Transfer all. *)
302-
List.iter
303-
(fun (off, len) ->
304-
let len = Int63.of_int len in
347+
Live.iter
348+
(fun ~off ~len ->
305349
let str = Dispatcher.read_seq_exn dispatcher ~off ~len in
306350
Sparse.Ao.append_seq_exn prefix ~off str)
307351
live_entries
@@ -399,16 +443,14 @@ module Make (Args : Gc_args.S) = struct
399443
stats :=
400444
Gc_stats.Worker.finish_current_step !stats "archive: iter reachable";
401445
let min_offset = Dispatcher.suffix_start_offset dispatcher in
402-
let to_archive =
403-
traverse_range ~min_offset commit_key commit_store node_store
404-
in
405-
let to_archive =
406-
List.map
407-
(fun (off, len) ->
408-
let len = Int63.of_int len in
409-
(off, Dispatcher.read_seq_exn dispatcher ~off ~len))
410-
(Live.to_list to_archive)
411-
in
446+
let to_archive = ref [] in
447+
Live.iter
448+
(fun ~off ~len ->
449+
to_archive :=
450+
(off, Dispatcher.read_seq_exn dispatcher ~off ~len)
451+
:: !to_archive)
452+
(traverse_range ~min_offset commit_key commit_store node_store);
453+
let to_archive = List.rev !to_archive in
412454
stats :=
413455
Gc_stats.Worker.finish_current_step !stats "archive: copy to lower";
414456
Lower.set_readonly lower false;

0 commit comments

Comments
 (0)