diff --git a/benchmarks/multicore-lockfree/bag-dune.inc b/benchmarks/multicore-lockfree/bag-dune.inc deleted file mode 100644 index 4d5532fafb..0000000000 --- a/benchmarks/multicore-lockfree/bag-dune.inc +++ /dev/null @@ -1,11 +0,0 @@ -(executable - (name test_bag) - (modules test_bag) - (libraries lockfree)) - -(executable - (name test_bag_multicore) - (modules test_bag_multicore) - (libraries lockfree)) - -(alias (name multibench_parallel) (deps test_bag.exe test_bag_multicore.exe)) \ No newline at end of file diff --git a/benchmarks/multicore-lockfree/dune b/benchmarks/multicore-lockfree/dune index 6a22c97dff..3932cf00a4 100644 --- a/benchmarks/multicore-lockfree/dune +++ b/benchmarks/multicore-lockfree/dune @@ -1,5 +1,73 @@ -(include hash-dune.inc) -(include wsqueue-dune.inc) -(include bag-dune.inc) -(include msqueue-dune.inc) -(include list-dune.inc) \ No newline at end of file +(executable + (name test_bag) + (modules test_bag) + (libraries lockfree)) + +(executable + (name test_bag_multicore) + (modules test_bag_multicore) + (libraries lockfree)) + +(executable + (name test_list) + (modules test_list) + (libraries lockfree)) + +(executable + (name test_list_multicore) + (modules test_list_multicore) + (libraries lockfree)) + +(executable + (name test_hash) + (modules test_hash) + (libraries lockfree)) + +(executable + (name test_hash_multicore) + (modules test_hash_multicore) + (libraries lockfree)) + +(executable + (name test_msqueue) + (modules test_msqueue) + (libraries lockfree)) + +(executable + (name test_msqueue_multicore) + (modules test_msqueue_multicore) + (libraries lockfree)) + +(executable + (name test_wsqueue) + (modules test_wsqueue) + (libraries lockfree)) + +(executable + (name test_wsqueue_multicore) + (modules test_wsqueue_multicore) + (libraries lockfree)) + +(executable + (name test_ctrie) + (modules test_ctrie) + (libraries base)) + +(executable + (name test_ctrie_multicore) + (modules test_ctrie_multicore) + (libraries base domainslib)) + +(alias (name multibench_parallel) + (deps test_bag.exe + test_list.exe + test_hash.exe + test_msqueue.exe + test_wsqueue.exe + test_ctrie.exe + test_bag_multicore.exe + test_list_multicore.exe + test_hash_multicore.exe + test_msqueue_multicore.exe + test_wsqueue_multicore.exe + test_ctrie_multicore.exe)) diff --git a/benchmarks/multicore-lockfree/hash-dune.inc b/benchmarks/multicore-lockfree/hash-dune.inc deleted file mode 100644 index ea1d39764f..0000000000 --- a/benchmarks/multicore-lockfree/hash-dune.inc +++ /dev/null @@ -1,11 +0,0 @@ -(executable - (name test_hash) - (modules test_hash) - (libraries lockfree)) - -(executable - (name test_hash_multicore) - (modules test_hash_multicore) - (libraries lockfree)) - -(alias (name multibench_parallel) (deps test_hash.exe test_hash_multicore.exe)) diff --git a/benchmarks/multicore-lockfree/list-dune.inc b/benchmarks/multicore-lockfree/list-dune.inc deleted file mode 100644 index bc2378c8d0..0000000000 --- a/benchmarks/multicore-lockfree/list-dune.inc +++ /dev/null @@ -1,11 +0,0 @@ -(executable - (name test_list) - (modules test_list) - (libraries lockfree)) - -(executable - (name test_list_multicore) - (modules test_list_multicore) - (libraries lockfree)) - -(alias (name multibench_parallel) (deps test_list.exe test_list_multicore.exe)) diff --git a/benchmarks/multicore-lockfree/msqueue-dune.inc b/benchmarks/multicore-lockfree/msqueue-dune.inc deleted file mode 100644 index 738c5694f7..0000000000 --- a/benchmarks/multicore-lockfree/msqueue-dune.inc +++ /dev/null @@ -1,11 +0,0 @@ -(executable - (name test_msqueue) - (modules test_msqueue) - (libraries lockfree)) - -(executable - (name test_msqueue_multicore) - (modules test_msqueue_multicore) - (libraries lockfree)) - -(alias (name multibench_parallel) (deps test_msqueue.exe test_msqueue_multicore.exe)) diff --git a/benchmarks/multicore-lockfree/test_ctrie.ml b/benchmarks/multicore-lockfree/test_ctrie.ml new file mode 100644 index 0000000000..1935c133bb --- /dev/null +++ b/benchmarks/multicore-lockfree/test_ctrie.ml @@ -0,0 +1,141 @@ +module Ctrie = struct + (* Concurrent, hash array mapped trie based on - + Prokopec, A. et al. (2011) + Cache-Aware Lock-Free Concurrent Hash Tries. Technical Report, 2011. *) + + (* configuration parameters *) + let shift = 5 + + (* data definition *) + type node = + | Empty + | Cnode of { map : int ; nodes : (node Atomic.t) array } + | Inode of { key : int ; values : int list ; delete : bool } + + type t = node Atomic.t + + (* helper functions *) + + (* detect flag of key in map *) + let flag k l _map = + let i = + (k lsr (l * shift)) + land + ((1 lsl shift) - 1) in + i + + (* check if flag is set *) + let set i map = + ((1 lsl i) land map) <> 0 + + (* detect position in array *) + let pos flag map = + (Base.Int.popcount (((1 lsl flag) - 1) land map)) + + (* create empty map *) + let empty () = Atomic.make Empty + + (* insert key and value binding into map *) + let rec insert_aux k v l t = + match Atomic.get t with + | Empty -> + let i = flag k l 0 in + let map = 1 lsl i in + let nodes = [| + (Atomic.make (Inode {key = k ; values = [v] ; delete = false})) + |] in + let new_node = Cnode { map ; nodes } in + Atomic.compare_and_set t Empty new_node + | Cnode { map ; nodes } as c -> + let i = flag k l map in + if (set i map) then begin + let p = pos i map in + insert_aux k v (l+1) nodes.(p) + end else begin + let map = (1 lsl i) lor (map) in + let p = pos i map in + let old_len = Array.length nodes in + let new_nodes = Array.init (old_len + 1) (fun i -> + if i < p then nodes.(i) else + if i = p then + Atomic.make (Inode {key = k; values = [v] ; delete = false}) + else + nodes.(i-1)) in + let new_node = Cnode { map ; nodes = new_nodes } in + Atomic.compare_and_set t c new_node + end + | Inode { key ; values ; delete} as inode -> + if key = k then begin + let new_values = v :: values in + let new_node = Inode { key ; values = new_values ; delete } in + Atomic.compare_and_set t inode new_node + end else begin + let i = flag key l 0 in + let ni = flag k l 0 in + let map = (1 lsl i) lor 0 in + let map = (1 lsl ni) lor map in + let nodes = + if (ni > i) then + ([| + Atomic.make (Inode { key ; values ; delete }) ; + Atomic.make (Inode { key = k ; values = [v] ; delete = false }) + |], true) + else if (ni < i) then + ([| + Atomic.make (Inode { key = k ; values = [v] ; delete = false }) ; + Atomic.make (Inode { key ; values ; delete }) + |], true) + else begin + let i = flag key (l+1) 0 in + let nmap = (1 lsl i) lor 0 in + let nnodes = [|Atomic.make (Inode {key ; values; delete})|] in + ([| + Atomic.make (Cnode { map = nmap ; nodes = nnodes }) + |], false) + end in + let (nodes, new_level) = nodes in + let new_node = Cnode { map ; nodes } in + Atomic.compare_and_set t inode new_node && new_level + end + + let rec insert k v t = + if insert_aux k v 0 t then () else insert k v t + + (* check if key in map *) + let rec mem k l t = + match Atomic.get t with + | Empty -> false + | Cnode { map ; nodes } -> + let f = flag k l map in + if (set f map) then begin + let p = pos f map in + mem k (l+1) nodes.(p) + end else begin + false + end + | Inode { key ; _ } -> if key = k then true else false + + let mem k t = mem k 0 t +end + +let num_elems = try int_of_string Sys.argv.(1) with _ -> 10_000_000 +let ins_percent = try int_of_string Sys.argv.(2) with _ -> 50 + +let state_key = Domain.DLS.new_key Random.State.make_self_init + +let rand_int n = + let state = Domain.DLS.get state_key in + Random.State.int state n + +let work tree _ = + if rand_int 100 >= ins_percent then + ignore (Ctrie.mem (rand_int 10000) tree) + else + ignore (Ctrie.insert (rand_int 10000) 0 tree) + +let _ = + let tree = Ctrie.empty () in + let work = work tree in + for i = 0 to (num_elems - 1) do + work i + done diff --git a/benchmarks/multicore-lockfree/test_ctrie_multicore.ml b/benchmarks/multicore-lockfree/test_ctrie_multicore.ml new file mode 100644 index 0000000000..74f822564b --- /dev/null +++ b/benchmarks/multicore-lockfree/test_ctrie_multicore.ml @@ -0,0 +1,144 @@ +module Ctrie = struct + (* Concurrent, hash array mapped trie based on - + Prokopec, A. et al. (2011) + Cache-Aware Lock-Free Concurrent Hash Tries. Technical Report, 2011. *) + + (* configuration parameters *) + let shift = 5 + + (* data definition *) + type node = + | Empty + | Cnode of { map : int ; nodes : (node Atomic.t) array } + | Inode of { key : int ; values : int list ; delete : bool } + + type t = node Atomic.t + + (* helper functions *) + + (* detect flag of key in map *) + let flag k l _map = + let i = + (k lsr (l * shift)) + land + ((1 lsl shift) - 1) in + i + + (* check if flag is set *) + let set i map = + ((1 lsl i) land map) <> 0 + + (* detect position in array *) + let pos flag map = + (Base.Int.popcount (((1 lsl flag) - 1) land map)) + + (* create empty map *) + let empty () = Atomic.make Empty + + (* insert key and value binding into map *) + let rec insert_aux k v l t = + match Atomic.get t with + | Empty -> + let i = flag k l 0 in + let map = 1 lsl i in + let nodes = [| + (Atomic.make (Inode {key = k ; values = [v] ; delete = false})) + |] in + let new_node = Cnode { map ; nodes } in + Atomic.compare_and_set t Empty new_node + | Cnode { map ; nodes } as c -> + let i = flag k l map in + if (set i map) then begin + let p = pos i map in + insert_aux k v (l+1) nodes.(p) + end else begin + let map = (1 lsl i) lor (map) in + let p = pos i map in + let old_len = Array.length nodes in + let new_nodes = Array.init (old_len + 1) (fun i -> + if i < p then nodes.(i) else + if i = p then + Atomic.make (Inode {key = k; values = [v] ; delete = false}) + else + nodes.(i-1)) in + let new_node = Cnode { map ; nodes = new_nodes } in + Atomic.compare_and_set t c new_node + end + | Inode { key ; values ; delete} as inode -> + if key = k then begin + let new_values = v :: values in + let new_node = Inode { key ; values = new_values ; delete } in + Atomic.compare_and_set t inode new_node + end else begin + let i = flag key l 0 in + let ni = flag k l 0 in + let map = (1 lsl i) lor 0 in + let map = (1 lsl ni) lor map in + let nodes = + if (ni > i) then + ([| + Atomic.make (Inode { key ; values ; delete }) ; + Atomic.make (Inode { key = k ; values = [v] ; delete = false }) + |], true) + else if (ni < i) then + ([| + Atomic.make (Inode { key = k ; values = [v] ; delete = false }) ; + Atomic.make (Inode { key ; values ; delete }) + |], true) + else begin + let i = flag key (l+1) 0 in + let nmap = (1 lsl i) lor 0 in + let nnodes = [|Atomic.make (Inode {key ; values; delete})|] in + ([| + Atomic.make (Cnode { map = nmap ; nodes = nnodes }) + |], false) + end in + let (nodes, new_level) = nodes in + let new_node = Cnode { map ; nodes } in + Atomic.compare_and_set t inode new_node && new_level + end + + let rec insert k v t = + if insert_aux k v 0 t then () else insert k v t + + (* check if key in map *) + let rec mem k l t = + match Atomic.get t with + | Empty -> false + | Cnode { map ; nodes } -> + let f = flag k l map in + if (set f map) then begin + let p = pos f map in + mem k (l+1) nodes.(p) + end else begin + false + end + | Inode { key ; _ } -> if key = k then true else false + + let mem k t = mem k 0 t +end + +module T = Domainslib.Task + +let num_domains = try int_of_string Sys.argv.(1) with _ -> 4 +let num_elems = try int_of_string Sys.argv.(2) with _ -> 10_000_000 +let ins_percent = try int_of_string Sys.argv.(3) with _ -> 50 + +let state_key = Domain.DLS.new_key Random.State.make_self_init + +let rand_int n = + let state = Domain.DLS.get state_key in + Random.State.int state n + +let work tree int = + if rand_int 100 >= ins_percent then + ignore (Ctrie.mem (rand_int 10000) tree) + else + ignore (Ctrie.insert (rand_int 10000) 0 tree) + +let _ = + let pool = T.setup_pool num_domains in + let tree = Ctrie.empty () in + let work = work tree in + T.parallel_for ~start:0 ~finish:(num_elems - 1) ~body:work pool; + T.teardown_pool pool diff --git a/benchmarks/multicore-lockfree/wsqueue-dune.inc b/benchmarks/multicore-lockfree/wsqueue-dune.inc deleted file mode 100644 index 4bef6e7d56..0000000000 --- a/benchmarks/multicore-lockfree/wsqueue-dune.inc +++ /dev/null @@ -1,11 +0,0 @@ -(executable - (name test_wsqueue) - (modules test_wsqueue) - (libraries lockfree)) - -(executable - (name test_wsqueue_multicore) - (modules test_wsqueue_multicore) - (libraries lockfree)) - -(alias (name multibench_parallel) (deps test_wsqueue.exe test_wsqueue_multicore.exe)) diff --git a/multicore_parallel_run_config.json b/multicore_parallel_run_config.json index ea514e617c..6ef6768fbd 100644 --- a/multicore_parallel_run_config.json +++ b/multicore_parallel_run_config.json @@ -679,6 +679,47 @@ } ] }, + { + "executable": "benchmarks/multicore-lockfree/test_ctrie.exe", + "name": "test_ctrie", + "tags": ["lockfree_bench"], + "runs": [ + { + "params": "10_000_000 50" + } + ] + }, + { + "executable": "benchmarks/multicore-lockfree/test_ctrie_multicore.exe", + "name": "test_ctrie_multicore", + "tags": ["lockfree_bench"], + "runs": [ + { + "params": "1 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13" + }, + { + "params": "2 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13" + }, + { + "params": "4 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13" + }, + { + "params": "8 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13" + }, + { + "params": "12 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13" + }, + { + "params": "16 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13,16-27" + }, + { + "params": "20 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13,16-27" + }, + { + "params": "24 10_000_000 50", "paramwrapper": "taskset --cpu-list 2-13,16-27" + } + ] + }, { "executable": "benchmarks/simple-tests/alloc_multicore.exe", "name": "alloc_multicore",