File: intern.ml

package info (click to toggle)
ocaml 5.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 43,124 kB
  • sloc: ml: 355,439; ansic: 51,636; sh: 25,098; asm: 5,413; makefile: 3,673; python: 919; javascript: 273; awk: 253; perl: 59; fortran: 21; cs: 9
file content (186 lines) | stat: -rw-r--r-- 5,451 bytes parent folder | download | duplicates (2)
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
(* TEST *)

module MP = Gc.Memprof

let alloc_tracker on_alloc =
  { MP.null_tracker with
    alloc_minor = (fun info -> on_alloc info; None);
    alloc_major = (fun info -> on_alloc info; None);
  }

(* avoiding constant folding, make a value tree consisting of n words *)

type t = O | IIi of int | IIt of t | IIIi of int * int | IIIt of t * t
let rec t_tree k n = match n with
   | len when len <= 1 -> O
   | 2 -> IIi k
   | 3 -> IIIi (k,k)
   | 4 -> IIt (IIi k)
   | 5 -> IIIt (IIi k, O)
   | 6 -> IIIt (IIIi (k,k), O)
   | len -> IIIt (t_tree k ((len-3)/2), t_tree k (len - 3 - (len-3)/2));;
let t_of_len n = t_tree 7 n;;

let marshalled_data = Hashtbl.create 17
let[@inline never] get_marshalled_data len : t =
  Marshal.from_string (Hashtbl.find marshalled_data len) 0
let precompute_marshalled_data lo hi =
  for len = lo to hi do
    if not (Hashtbl.mem marshalled_data len) then
      Hashtbl.add marshalled_data len (Marshal.to_string (t_of_len len) [])
  done

let root = ref []
let[@inline never] do_intern lo hi cnt keep =
  for j = 0 to cnt-1 do
    for i = lo to hi do
      root := get_marshalled_data i :: !root
    done;
    if not keep then root := []
  done

(* `get_marshalled_data i` should allocate `i` words with source
 * `Marshal`, in blocks of size 1 or 2. So `do_intern lo hi cnt _`
 * should allocate (hi+lo)(hi-lo+1)/2 words. *)

let check_nosample () =
  Printf.printf "check_nosample\n%!";
  precompute_marshalled_data 2 3000;
  let fail_on_alloc _ =
    Printf.printf "Callback called with sampling_rate = 0\n";
    assert(false)
  in
  let _:MP.t =
    MP.start ~callstack_size:10 ~sampling_rate:0.
                 (alloc_tracker fail_on_alloc)
  in
  do_intern 2 3000 1 false;
  MP.stop ()

let () = check_nosample ()

let check_counts_full_major force_promote =
  Printf.printf "check_counts_full_major\n%!";
  precompute_marshalled_data 2 3000;
  let nalloc_minor = ref 0 in
  let nalloc_major = ref 0 in
  let enable = ref true in
  let npromote = ref 0 in
  let ndealloc_minor = ref 0 in
  let ndealloc_major = ref 0 in
  let _:MP.t = MP.start ~callstack_size:10 ~sampling_rate:0.01
    {
      alloc_minor = (fun _ ->
        if not !enable then None
        else Some (incr nalloc_minor)
      );
      alloc_major = (fun _ ->
        if not !enable then None
        else Some (incr nalloc_major)
      );
      promote = (fun _ ->
        Some (incr npromote)
      );
      dealloc_minor = (fun _ ->
        incr ndealloc_minor
      );
      dealloc_major = (fun _ ->
        incr ndealloc_major
      );
    }
  in
  do_intern 2 3000 1 true;
  enable := false;
  assert (!ndealloc_minor = 0 && !ndealloc_major = 0);
  if force_promote then begin
    Gc.full_major ();
    assert (!ndealloc_minor = 0 && !ndealloc_major = 0 &&
            !npromote = !nalloc_minor);
    root := [];
    Gc.full_major ();
    assert (!ndealloc_minor = 0 &&
            !ndealloc_major = !nalloc_minor + !nalloc_major);
  end else begin
    root := [];
    Gc.minor ();
    Gc.full_major ();
    Gc.full_major ();
    assert (!nalloc_minor = !ndealloc_minor + !npromote &&
            !ndealloc_major = !npromote + !nalloc_major)
  end;
  MP.stop ()

let () =
  check_counts_full_major false;
  check_counts_full_major true

let check_no_nested () =
  Printf.printf "check_no_nested\n%!";
  precompute_marshalled_data 2 300;
  let in_callback = ref false in
  let cb _ =
    assert (not !in_callback);
    in_callback := true;
    do_intern 100 200 1 false;
    in_callback := false;
    ()
  in
  let cb' _ = cb (); Some () in
  let _:MP.t = MP.start ~callstack_size:10 ~sampling_rate:1.
    {
      alloc_minor = cb';
      alloc_major = cb';
      promote = cb';
      dealloc_minor = cb;
      dealloc_major = cb;
    }
  in
  do_intern 100 200 1 false;
  MP.stop ()

let () = check_no_nested ()

let check_distrib lo hi cnt rate =
  Printf.printf "check_distrib %d %d %d %f\n%!" lo hi cnt rate;
  precompute_marshalled_data lo hi;
  let smp = ref 0 in
  let alloc (info:MP.allocation) =
    (* We also allocate the list constructor in the minor heap,
       so we filter that out. *)
    if info.source = Marshal then begin
      assert (info.size = 1 || info.size = 2);
      assert (info.n_samples > 0);
      smp := !smp + info.n_samples
    end
  in
  let _:MP.t =
    MP.start ~callstack_size:10 ~sampling_rate:rate
      (alloc_tracker alloc)
  in
  do_intern lo hi cnt false;
  MP.stop ();

  (* The probability distribution of the number of samples follows a
     binomial distribution of parameters tot_alloc and rate. Given
     that tot_alloc*rate and tot_alloc*(1-rate) are large (i.e., >
     100), this distribution is approximately equal to a normal
     distribution. We compute a 1e-8 confidence interval for !smp
     using quantiles of the normal distribution, and check that we are
     in this confidence interval. *)
  let tot_alloc = cnt*(lo+hi)*(hi-lo+1)/2 in
  assert (float tot_alloc *. rate > 100. &&
          float tot_alloc *. (1. -. rate) > 100.);
  let mean = float tot_alloc *. rate in
  let stddev = sqrt (float tot_alloc *. rate *. (1. -. rate)) in
  (* This should fail approximately one time in 100,000,000 *)
  assert (abs_float (mean -. float !smp) <= stddev *. 5.7)

let () =
  check_distrib 2 3000 3 0.00001;
  check_distrib 2 3000 1 0.0001;
  check_distrib 2 2000 1 0.01;
  check_distrib 2 2000 1 0.9;
  check_distrib 300000 300000 20 0.1

let () =
  Printf.printf "OK !\n"