Source file cache.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
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
open Prelude
module StdHashtbl = Hashtbl
open ExtLib
open Printf
module type Lock = sig
  type t
  val create : unit -> t
  val locked : t -> (unit -> 'a) -> 'a
end
module NoLock = struct
  type t = unit
  let create () = ()
  let locked () f = f ()
end
module TimeLimited2(E: Set.OrderedType)
  (Lock: sig type t val create : unit -> t val locked : t -> (unit -> 'a) -> 'a end) = struct
  type time = Int64.t
  let fixed f = 10000. *. f |> Int64.of_float
  let current () = Unix.gettimeofday () |> fixed
  module Value = struct
    type t = E.t * time
    let compare (v1,_) (v2,_) = E.compare v1 v2
  end
  module M = Set.Make(Value)
  type t = { limit : time; mutable next : time; lock : Lock.t; mutable m : M.t; }
  let private_purge t =
    let cur = current () in
    if cur >= t.next then
    begin
      t.next <- Int64.add t.limit cur;
      t.m <- M.filter (fun (_,t) -> t > cur) t.m
    end
  let create limit = { limit = fixed limit; next = 0L; lock = Lock.create (); m = M.empty }
  let add t x =
    let expire = Int64.add t.limit (current ()) in
    
    Lock.locked t.lock (fun () -> private_purge t; t.m <- M.add (x, expire) t.m)
  let get t v =
    
    Lock.locked t.lock (fun () -> M.find_opt (v, t.limit) t.m)
  let count t = Lock.locked t.lock (fun () -> M.cardinal t.m)
  let iter t f = Lock.locked t.lock (fun () -> M.iter (fun (x,_) -> f x) t.m)
end
module Count = struct
  open Hashtbl
  type 'a t = ('a, int ref) Hashtbl.t
  let create () : 'a t = create 16
  let clear = Hashtbl.clear
  let entry t x = match find t x with r -> r | exception Not_found -> let r = ref 0 in Hashtbl.add t x r; r
  let plus t x n = entry t x += n
  let minus t x n = entry t x -= n
  let of_enum e = let h = create () in Enum.iter (fun (k,n) -> plus h k n) e; h
  let of_list l = of_enum @@ List.enum l
  let add t x = plus t x 1
  let del t x = minus t x 1
  let enum t = enum t |> Enum.map (fun (k,n) -> k, !n)
  let iter t f = iter (fun k n -> f k !n) t
  let fold t f acc = Hashtbl.fold (fun k n acc -> f k !n acc) t acc
  let count t k = match Hashtbl.find t k with n -> !n | exception Not_found -> 0
  let count_all t = Hashtbl.fold (fun _ n acc -> acc + !n) t 0
  let size = Hashtbl.length
  let show t ?(sep=" ") f = enum t |>
    List.of_enum |> List.sort ~cmp:(Action.compare_by fst) |>
    List.map (fun (x,n) -> sprintf "%S: %u" (f x) n) |>
    String.concat sep
  let show_sorted t ?limit ?(sep="\n") f = enum t |>
    List.of_enum |> List.sort ~cmp:(flip @@ Action.compare_by snd) |>
    (match limit with None -> id | Some n -> List.take n) |>
    List.map (fun (x,n) -> sprintf "%6d : %S" n (f x)) |>
    String.concat sep
  let stats t ?(cmp=compare) f =
    if Hashtbl.length t = 0 then
      "<empty>"
    else
      let a = Array.of_enum (enum t) in
      let total = Array.fold_left (fun t (_,n) -> t + n) 0 a in
      let half = total / 2 in
      let cmp (x,_) (y,_) = cmp x y in
      Array.sort cmp a;
      let med = ref None in
      let (mi,ma,_) = Array.fold_left begin fun (mi,ma,sum) x ->
        let sum = sum + snd x in
        if !med = None && half <= sum then med := Some x;
        let mi = if snd x < snd mi then x else mi in
        let ma = if snd x > snd ma then x else ma in
        mi, ma, sum
      end ((fst a.(0), max_int), (fst a.(0),min_int), 0) a
      in
      let show (x,n) = sprintf "%S (%d)" (f x) n in
      sprintf "total %d median %s min %s max %s"
        total (match !med with None -> "?" | Some x -> show x) (show mi) (show ma)
  let distrib t =
    if Hashtbl.length t = 0 then
      [||]
    else
      let a = Array.of_enum (enum t) in
      let total = Array.fold_left (fun t (_,n) -> t + n) 0 a in
      let limits = Array.init 10 (fun i -> total * (i + 1) / 10) in
      let cmp (x,_) (y,_) = compare (x:float) y in
      Array.sort cmp a;
      let distrib = limits |> Array.map begin fun limit ->
        let (v,_) = Array.fold_left begin fun (found,sum) (v,n) ->
          let sum = sum + n in
          if found = None && limit <= sum then Some v, sum else (found,sum)
        end (None,0) a in
        match v with
        | None -> nan
        | Some v -> v
      end
      in
      distrib
  let show_distrib ?(sep="\n") t =
    distrib t |> Array.mapi (fun i v -> sprintf "%d%% <= %f" ((i + 1) * 10) v) |> Array.to_list |> String.concat sep
  let report t ?limit ?cmp ?(sep="\n") f =
    let data = show_sorted t ?limit ~sep f in
    let stats = stats t ?cmp f in
    stats^sep^data
  let names (t : 'a t) = List.of_enum @@ Hashtbl.keys t
end
module LRU (Keys : StdHashtbl.HashedType) = struct
  module Hashtbl = StdHashtbl.Make(Keys)
  module Queue = struct
    exception Empty
    type 'a elem = 'a Dllist.node_t
    type 'a t = 'a elem option ref
    let create () = ref None
    let unwrap = Dllist.get
    let is_singleton list = Dllist.next list == list
    let drop elem =
      match is_singleton elem with
      | true -> None
      | false -> Some (Dllist.rev_drop elem)
    let append t value =
      match !t with
      | None -> t := Some (value)
      | Some queue ->
        Dllist.splice value (Dllist.next queue);
        Dllist.splice queue value
    let push t value =
      let node = Dllist.create value in
      append t node;
      node
    let pop t =
      match !t with
      | None -> raise Empty
      | Some queue ->
        t := drop queue;
        queue
    let remove t elem =
      match !t with
      | None -> ()
      | Some queue when elem == queue ->
        t := drop queue
      | Some _ ->
        Dllist.remove elem
  end
  type 'v entry = {
    key : Hashtbl.key;
    mutable value : 'v;
    mutable queue : [`Lru | `Lfu ];
  }
  type 'v t = {
    table : 'v entry Queue.elem Hashtbl.t;
    mutable lru_avaibl : int;
    mutable lfu_avaibl : int;
    lru : 'v entry Queue.t;
    lfu : 'v entry Queue.t;
    mutable hit : int;
    mutable miss : int;
  }
  let create size =
    assert (size > 0);
    {
      table = Hashtbl.create size;
      lru = Queue.create ();
      lfu = Queue.create ();
      hit = 0;
      miss = 0;
      lru_avaibl = size;
      lfu_avaibl = size;
    }
  let size cache = Hashtbl.length cache.table
  let iter f cache = Hashtbl.iter (fun key value -> f key (Queue.unwrap value).value) cache.table
  let miss cache = cache.miss
  let hit cache = cache.hit
  let replace cache key value =
    try
      let entry = Hashtbl.find cache.table key |> Queue.unwrap in
      entry.value <- value
    with Not_found -> ()
  let get_evicted cache key =
    try
      let node = Hashtbl.find cache.table key in
      let entry = Queue.unwrap node in
      cache.hit <- cache.hit + 1;
      
      begin match entry.queue with
      | `Lru ->
        
        cache.lru_avaibl <- cache.lru_avaibl + 1;
        entry.queue <- `Lfu;
        Queue.remove cache.lru node;
      | `Lfu ->
        cache.lfu_avaibl <- cache.lfu_avaibl + 1;
        Queue.remove cache.lfu node
      end;
      
      let evicted = 
        if cache.lfu_avaibl <= 0 then begin
          let evicted = Queue.unwrap (Queue.pop cache.lfu) in
          Hashtbl.remove cache.table evicted.key;
          Some (evicted.key, evicted.value)
        end else begin
          cache.lfu_avaibl <- cache.lfu_avaibl - 1;
          None
        end
      in
      Queue.append cache.lfu node;
      entry.value, evicted
    with Not_found -> cache.miss <- cache.miss + 1; raise Not_found
  let find cache key =
    let entry = Queue.unwrap @@ Hashtbl.find cache.table key in
    entry.value
  let get cache key =
    fst @@ get_evicted cache key
  let mem cache key = Hashtbl.mem cache.table key
  let lru_free cache = cache.lru_avaibl
  let lfu_free cache = cache.lfu_avaibl
  let put_evicted cache key value =
    try
      let node = Hashtbl.find cache.table key |> Queue.unwrap in
      node.value <- value;
      None
    with Not_found ->
      let evicted =
        if cache.lru_avaibl = 0 then begin
          let evicted = Queue.unwrap (Queue.pop cache.lru) in
          Hashtbl.remove cache.table evicted.key;
          Some (evicted.key, evicted.value)
        end else begin
          cache.lru_avaibl <- cache.lru_avaibl - 1;
          None
        end
      in
      let node = Queue.push cache.lru { key;  value; queue = `Lru } in
      Hashtbl.add cache.table key node;
      evicted
  let put cache key value = put_evicted cache key value |> ignore
  let remove cache key =
    try
      let node = Hashtbl.find cache.table key in
      Hashtbl.remove cache.table key;
      match (Queue.unwrap node).queue with
      | `Lru ->
        cache.lru_avaibl <- cache.lru_avaibl + 1;
        Queue.remove cache.lru node
      | `Lfu ->
        cache.lfu_avaibl <- cache.lfu_avaibl + 1;
        Queue.remove cache.lfu node
    with Not_found -> ()
end
module Group = struct
  type ('a,'b) t = ('b,'a list) Hashtbl.t * ('a -> 'b)
  let by f = Hashtbl.create 32, f
  let add (h,f) x = let k = f x in try Hashtbl.replace h k (x :: Hashtbl.find h k) with Not_found -> Hashtbl.add h k [x]
  let get (h,_) k = try Hashtbl.find h k with Not_found -> []
  let iter (h,_) k = Hashtbl.iter k h
  let keys (h,_) = Hashtbl.keys h
end
let group_fst e =
  let h = Hashtbl.create 10 in
  Enum.iter (fun (k,v) -> Hashtbl.replace h k (try v :: Hashtbl.find h k with Not_found -> [v])) e;
  Hashtbl.enum h
module Assoc = struct
  type ('a,'b) t = ('a,'b) Hashtbl.t
  let create () = Hashtbl.create 32
  let add h k v =
    assert (false = Hashtbl.mem h k);
    Hashtbl.add h k v
  let get = Hashtbl.find
  let try_get = Hashtbl.find_option
  let del h k =
    try
      let v = Hashtbl.find h k in
      Hashtbl.remove h k; v
    with
      Not_found -> assert false
  let remove h k =
    assert (true = Hashtbl.mem h k);
    Hashtbl.remove h k
  let size = Hashtbl.length
  let fold = Hashtbl.fold
end
module Lists = struct
type ('a,'b) t = ('a,'b list) Hashtbl.t
let create () = Hashtbl.create 16
let get h k = try Hashtbl.find h k with Not_found -> []
let set = Hashtbl.replace
let add h k v = Hashtbl.replace h k (v::get h k)
let enum = Hashtbl.enum
let clear = Hashtbl.clear
let count_keys = Hashtbl.length
let count_all h = Hashtbl.fold (fun _ l acc -> acc + List.length l) h 0
end
class ['a] cache (cb : ('a list -> unit)) ~limit =
object(self)
  val mutable l = []
  method name = "cache"
  method add x =
    l <- x :: l;
    if List.length l >= limit then
    begin
      cb l;
      self#clear
    end
  method get = l
  method dump = cb l; l <- []
  method clear = l <- []
  method to_list = l
  method size = List.length l
end
type 'a reused = { cache : 'a Stack.t; create : (unit -> 'a); reset : ('a -> unit); }
let reuse create reset = { cache = Stack.create (); create; reset; }
let use t = if Stack.is_empty t.cache then t.create () else Stack.pop t.cache
let recycle t x = t.reset x; Stack.push x t.cache
module ReuseLocked(L : Lock)(T : sig type t val create : unit -> t val reset : t -> unit end) : sig
type t = T.t
val get : unit -> t
val release : t -> unit
end = struct
type t = T.t
type cache = { cache : t Stack.t; lock : L.t }
let cache = { cache = Stack.create (); lock = L.create () }
let get' () = if Stack.is_empty cache.cache then T.create () else Stack.pop cache.cache
let get () = L.locked cache.lock get'
let release x = L.locked cache.lock (fun () -> T.reset x; Stack.push x cache.cache)
end
module Reuse = ReuseLocked(NoLock)