|
| 1 | +module Ctrie = struct |
| 2 | + (* Concurrent, hash array mapped trie based on - |
| 3 | + Prokopec, A. et al. (2011) |
| 4 | + Cache-Aware Lock-Free Concurrent Hash Tries. Technical Report, 2011. *) |
| 5 | + |
| 6 | + (* configuration parameters *) |
| 7 | + let shift = 5 |
| 8 | + |
| 9 | + (* data definition *) |
| 10 | + type node = |
| 11 | + | Empty |
| 12 | + | Cnode of { map : int ; nodes : (node Atomic.t) array } |
| 13 | + | Inode of { key : int ; values : int list ; delete : bool } |
| 14 | + |
| 15 | + type t = node Atomic.t |
| 16 | + |
| 17 | + (* helper functions *) |
| 18 | + |
| 19 | + (* detect flag of key in map *) |
| 20 | + let flag k l _map = |
| 21 | + let i = |
| 22 | + (k lsr (l * shift)) |
| 23 | + land |
| 24 | + ((1 lsl shift) - 1) in |
| 25 | + i |
| 26 | + |
| 27 | + (* check if flag is set *) |
| 28 | + let set i map = |
| 29 | + ((1 lsl i) land map) <> 0 |
| 30 | + |
| 31 | + (* detect position in array *) |
| 32 | + let pos flag map = |
| 33 | + (Base.Int.popcount (((1 lsl flag) - 1) land map)) |
| 34 | + |
| 35 | + (* create empty map *) |
| 36 | + let empty () = Atomic.make Empty |
| 37 | + |
| 38 | + (* insert key and value binding into map *) |
| 39 | + let rec insert_aux k v l t = |
| 40 | + match Atomic.get t with |
| 41 | + | Empty -> |
| 42 | + let i = flag k l 0 in |
| 43 | + let map = 1 lsl i in |
| 44 | + let nodes = [| |
| 45 | + (Atomic.make (Inode {key = k ; values = [v] ; delete = false})) |
| 46 | + |] in |
| 47 | + let new_node = Cnode { map ; nodes } in |
| 48 | + Atomic.compare_and_set t Empty new_node |
| 49 | + | Cnode { map ; nodes } as c -> |
| 50 | + let i = flag k l map in |
| 51 | + if (set i map) then begin |
| 52 | + let p = pos i map in |
| 53 | + insert_aux k v (l+1) nodes.(p) |
| 54 | + end else begin |
| 55 | + let map = (1 lsl i) lor (map) in |
| 56 | + let p = pos i map in |
| 57 | + let old_len = Array.length nodes in |
| 58 | + let new_nodes = Array.init (old_len + 1) (fun i -> |
| 59 | + if i < p then nodes.(i) else |
| 60 | + if i = p then |
| 61 | + Atomic.make (Inode {key = k; values = [v] ; delete = false}) |
| 62 | + else |
| 63 | + nodes.(i-1)) in |
| 64 | + let new_node = Cnode { map ; nodes = new_nodes } in |
| 65 | + Atomic.compare_and_set t c new_node |
| 66 | + end |
| 67 | + | Inode { key ; values ; delete} as inode -> |
| 68 | + if key = k then begin |
| 69 | + let new_values = v :: values in |
| 70 | + let new_node = Inode { key ; values = new_values ; delete } in |
| 71 | + Atomic.compare_and_set t inode new_node |
| 72 | + end else begin |
| 73 | + let i = flag key l 0 in |
| 74 | + let ni = flag k l 0 in |
| 75 | + let map = (1 lsl i) lor 0 in |
| 76 | + let map = (1 lsl ni) lor map in |
| 77 | + let nodes = |
| 78 | + if (ni > i) then |
| 79 | + ([| |
| 80 | + Atomic.make (Inode { key ; values ; delete }) ; |
| 81 | + Atomic.make (Inode { key = k ; values = [v] ; delete = false }) |
| 82 | + |], true) |
| 83 | + else if (ni < i) then |
| 84 | + ([| |
| 85 | + Atomic.make (Inode { key = k ; values = [v] ; delete = false }) ; |
| 86 | + Atomic.make (Inode { key ; values ; delete }) |
| 87 | + |], true) |
| 88 | + else begin |
| 89 | + let i = flag key (l+1) 0 in |
| 90 | + let nmap = (1 lsl i) lor 0 in |
| 91 | + let nnodes = [|Atomic.make (Inode {key ; values; delete})|] in |
| 92 | + ([| |
| 93 | + Atomic.make (Cnode { map = nmap ; nodes = nnodes }) |
| 94 | + |], false) |
| 95 | + end in |
| 96 | + let (nodes, new_level) = nodes in |
| 97 | + let new_node = Cnode { map ; nodes } in |
| 98 | + Atomic.compare_and_set t inode new_node && new_level |
| 99 | + end |
| 100 | + |
| 101 | + let rec insert k v t = |
| 102 | + if insert_aux k v 0 t then () else insert k v t |
| 103 | + |
| 104 | + (* check if key in map *) |
| 105 | + let rec mem k l t = |
| 106 | + match Atomic.get t with |
| 107 | + | Empty -> false |
| 108 | + | Cnode { map ; nodes } -> |
| 109 | + let f = flag k l map in |
| 110 | + if (set f map) then begin |
| 111 | + let p = pos f map in |
| 112 | + mem k (l+1) nodes.(p) |
| 113 | + end else begin |
| 114 | + false |
| 115 | + end |
| 116 | + | Inode { key ; _ } -> if key = k then true else false |
| 117 | + |
| 118 | + let mem k t = mem k 0 t |
| 119 | +end |
| 120 | + |
| 121 | +module T = Domainslib.Task |
| 122 | + |
| 123 | +let num_domains = try int_of_string Sys.argv.(1) with _ -> 4 |
| 124 | +let num_elems = try int_of_string Sys.argv.(2) with _ -> 1000000 |
| 125 | +let ins_ratio = try float_of_string Sys.argv.(3) with _ -> 0.5 |
| 126 | + |
| 127 | +let state_key = Domain.DLS.new_key Random.State.make_self_init in |
| 128 | + |
| 129 | +let rand_int () = |
| 130 | + let state = Domain.DLS.get state_key in |
| 131 | + Random.State.int state 10000 |
| 132 | + |
| 133 | +let rand_float () = |
| 134 | + let state = Domain.DLS.get state_key in |
| 135 | + Random.State.float state 1.0 |
| 136 | + |
| 137 | +let work tree int = |
| 138 | + if rand_float () > ins_ratio then |
| 139 | + ignore (Ctrie.mem (rand_int ()) tree) |
| 140 | + else |
| 141 | + ignore (Ctrie.insert (rand_int ()) 0 tree) |
| 142 | + |
| 143 | +let _ = |
| 144 | + let pool = T.setup_pool num_domains in |
| 145 | + let tree = Ctrie.empty () in |
| 146 | + let work = work tree in |
| 147 | + T.parallel_for ~start:0 ~finish:(num_elems - 1) ~body:work pool; |
| 148 | + T.teardown_pool pool |
0 commit comments