Skip to content

Commit

Permalink
Add a transaction benchmark using a naïve unscalable leftist heap
Browse files Browse the repository at this point in the history
  • Loading branch information
polytypic committed Feb 25, 2024
1 parent fc24f99 commit 60a0454
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 0 deletions.
139 changes: 139 additions & 0 deletions bench/bench_leftist_heap.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
open Kcas

(** This is a naïve transactional leftist heap implementation.
⚠️ This simply cannot scale, because essentially every delete min operation
updates the root. This can still be a useful benchmark of the transaction
mechanism, but please do not believe for a second that this is a scalable
concurrent priority queue. *)
module Leftist_heap : sig
type 'a t

val create : lt:('a -> 'a -> bool) -> 'a t

module Xt : sig
val insert : xt:'x Xt.t -> 'a t -> 'a -> unit
val delete_min_opt : xt:'x Xt.t -> 'a t -> 'a option
end

val insert : 'a t -> 'a -> unit
val delete_min_opt : 'a t -> 'a option
end = struct
type 'a t = { lt : 'a -> 'a -> bool; root : 'a link Loc.t }

and ('a, _) tdt =
| Null : ('a, [> `Null ]) tdt
| Node :
'a link Loc.t * int Loc.t * 'a * 'a link Loc.t
-> ('a, [> `Node ]) tdt

and 'a link = Link : ('a, [< `Null | `Node ]) tdt -> 'a link [@@unboxed]

let create ~lt =
let root = Loc.make ~padded:true (Link Null) in
Multicore_magic.copy_as_padded { lt; root }

module Xt = struct
let npl_of ~xt = function
| Link Null -> 0
| Link (Node (_, npl, _, _)) -> Xt.get ~xt npl

let rec merge ~xt ~lt h1 h2 =
match (h1, h2) with
| Link Null, h2 -> h2
| h1, Link Null -> h1
| Link (Node (_, _, v1, _) as h1), Link (Node (_, _, v2, _) as h2) ->
let (Node (h1l, npl, _, h1r) as h1 : (_, [ `Node ]) tdt), h2 =
if lt v1 v2 then (h1, h2) else (h2, h1)
in
let l = Xt.get ~xt h1l in
if l == Link Null then Xt.set ~xt h1l (Link h2)
else begin
let r = merge ~xt ~lt (Xt.get ~xt h1r) (Link h2) in
match (npl_of ~xt l, npl_of ~xt r) with
| l_npl, r_npl when l_npl < r_npl ->
Xt.set ~xt h1l r;
Xt.set ~xt h1r l;
Xt.set ~xt npl (l_npl + 1)
| _, r_npl ->
Xt.set ~xt h1r r;
Xt.set ~xt npl (r_npl + 1)
end;
Link h1

let insert ~xt h x =
let h1 =
Node (Loc.make (Link Null), Loc.make 1, x, Loc.make (Link Null))
in
Xt.set ~xt h.root (merge ~xt ~lt:h.lt (Link h1) (Xt.get ~xt h.root))

let delete_min_opt ~xt h =
match Xt.get ~xt h.root with
| Link Null -> None
| Link (Node (h1, _, x, h2)) ->
Xt.set ~xt h.root (merge ~xt ~lt:h.lt (Xt.get ~xt h1) (Xt.get ~xt h2));
Some x
end

let insert h x = Kcas.Xt.commit { tx = Xt.insert h x }
let delete_min_opt h = Kcas.Xt.commit { tx = Xt.delete_min_opt h }
end

open Multicore_bench

let run_one ~budgetf ~n_domains ~preload () =
let n_ops =
Float.to_int
(Float.of_int (50 * Util.iter_factor) /. Float.log2 (Float.of_int preload))
in

let t = Leftist_heap.create ~lt:(( < ) : int -> int -> bool) in

let n_ops_todo = Atomic.make 0 |> Multicore_magic.copy_as_padded in

let before () =
Atomic.set n_ops_todo n_ops;
while Option.is_some @@ Leftist_heap.delete_min_opt t do
()
done;
let state = Random.State.make_self_init () in
for _ = 1 to preload do
Leftist_heap.insert t (Random.State.bits state)
done
in
let init _ = Random.State.make_self_init () in
let work _ state =
let rec work () =
let n = Util.alloc ~batch:100 n_ops_todo in
if n <> 0 then
let rec loop n =
if 0 < n then
let value = Random.State.bits state in
if value land 1 = 0 then begin
Leftist_heap.insert t value;
loop (n - 1)
end
else begin
Leftist_heap.delete_min_opt t |> ignore;
loop (n - 1)
end
else work ()
in
loop n
in
work ()
in

let config =
Printf.sprintf "%d worker%s, %d preload" n_domains
(if n_domains = 1 then "" else "s")
preload
in

Times.record ~budgetf ~n_domains ~before ~init ~work ()
|> Times.to_thruput_metrics ~n:n_ops ~singular:"operation" ~config

let run_suite ~budgetf =
Util.cross [ 10; 100; 1000 ] [ 1; 2; 4 ]
|> List.concat_map @@ fun (preload, n_domains) ->
run_one ~budgetf ~n_domains ~preload ()
1 change: 1 addition & 0 deletions bench/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ let benchmarks =
("Kcas Loc", Bench_loc.run_suite);
("Kcas Xt", Bench_xt.run_suite);
("Kcas Xt read-only", Bench_xt_ro.run_suite);
("Kcas Xt Leftist_heap (unscalable)", Bench_leftist_heap.run_suite);
("Kcas parallel CMP", Bench_parallel_cmp.run_suite);
("Kcas_data Accumulator", Bench_accumulator.run_suite);
("Kcas_data Dllist", Bench_dllist.run_suite);
Expand Down

0 comments on commit 60a0454

Please sign in to comment.