Skip to content

Commit 731b018

Browse files
committed
Use adaptive backoff in mpmcq
1 parent 8d5b28e commit 731b018

File tree

8 files changed

+134
-61
lines changed

8 files changed

+134
-61
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
let has_domains = 1 < Domain.recommended_domain_count ()
2+
let n = 128 * 4
3+
let counters = Array.init n (fun _ -> Atomic.make 0)
4+
5+
let[@inline never] once_at counter ~log_scale =
6+
if has_domains then begin
7+
let n_contending_threads = Atomic.fetch_and_add counter 1 + 1 in
8+
let n = ref (Random.int ((n_contending_threads lsl log_scale) + 0)) in
9+
while 0 <= !n do
10+
Domain.cpu_relax ();
11+
decr n
12+
done;
13+
Atomic.decr counter
14+
end
15+
16+
let[@inline never] once ~random_key ~log_scale =
17+
let i = random_key land (n - 1) in
18+
let counter = Array.unsafe_get counters i in
19+
once_at counter ~log_scale
20+
21+
let[@inline] once_unless_alone ~random_key ~log_scale =
22+
let i = random_key land (n - 1) in
23+
let counter = Array.unsafe_get counters i in
24+
if 0 <> Atomic.get counter then once_at counter ~log_scale
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
val once : random_key:int -> log_scale:int -> unit
2+
val once_unless_alone : random_key:int -> log_scale:int -> unit
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let cpu_relax = Fun.id
2+
let recommended_domain_count () = 1
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
(library
2+
(name picos_aux_adaptive_backoff)
3+
(public_name picos_aux.adaptive_backoff))
4+
5+
(rule
6+
(package picos_aux)
7+
(targets domain.ml)
8+
(deps domain.ocaml4.ml)
9+
(enabled_if
10+
(< %{ocaml_version} 5.0.0))
11+
(action
12+
(progn
13+
(copy domain.ocaml4.ml domain.ml))))

lib/picos_aux.mpmcq/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(library
22
(name picos_aux_mpmcq)
33
(public_name picos_aux.mpmcq)
4-
(libraries backoff multicore-magic))
4+
(libraries picos_aux.adaptive_backoff multicore-magic))
55

66
(mdx
77
(package picos_meta)
Lines changed: 86 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
open Picos_aux_adaptive_backoff
12
module Atomic = Multicore_magic.Transparent_atomic
23

3-
type 'a t = { head : 'a head Atomic.t; tail : 'a tail Atomic.t }
4+
type 'a t = {
5+
random_key : int;
6+
head : 'a head Atomic.t;
7+
tail : 'a tail Atomic.t;
8+
}
49

510
and ('a, _) tdt =
611
| Cons : {
@@ -27,14 +32,15 @@ and 'a head = H : ('a, [< `Cons | `Head ]) tdt -> 'a head [@@unboxed]
2732
and 'a tail = T : ('a, [< `Snoc | `Tail ]) tdt -> 'a tail [@@unboxed]
2833

2934
let create ?padded () =
35+
let random_key = Int64.to_int (Random.bits64 ()) in
3036
let head =
3137
Atomic.make (H (Head { counter = 1 })) |> Multicore_magic.copy_as ?padded
3238
in
3339
let tail =
3440
Atomic.make (T (Tail { counter = 0; move = Used }))
3541
|> Multicore_magic.copy_as ?padded
3642
in
37-
Multicore_magic.copy_as ?padded { head; tail }
43+
Multicore_magic.copy_as ?padded { random_key; head; tail }
3844

3945
let rec rev (suffix : (_, [< `Cons ]) tdt) = function
4046
| T (Snoc { counter; prefix; value }) ->
@@ -47,139 +53,164 @@ let rev = function
4753
(Cons { counter; value; suffix = H (Head { counter = counter + 1 }) })
4854
prefix
4955

50-
let rec push t value backoff = function
56+
let[@inline] backoff { random_key; _ } =
57+
Adaptive_backoff.once ~random_key ~log_scale:10
58+
59+
let[@inline] backoff_unless_alone { random_key; _ } =
60+
Adaptive_backoff.once_unless_alone ~random_key ~log_scale:10
61+
62+
let rec push t value = function
5163
| T (Snoc snoc_r) as prefix ->
5264
let after = Snoc { counter = snoc_r.counter + 1; prefix; value } in
53-
if not (Atomic.compare_and_set t.tail prefix (T after)) then
54-
let backoff = Backoff.once backoff in
55-
push t value backoff (Atomic.fenceless_get t.tail)
65+
if not (Atomic.compare_and_set t.tail prefix (T after)) then begin
66+
backoff t;
67+
push t value (Atomic.fenceless_get t.tail)
68+
end
5669
| T (Tail tail_r) as prefix -> begin
5770
match tail_r.move with
5871
| Used ->
5972
let after = Snoc { counter = tail_r.counter + 1; prefix; value } in
60-
if not (Atomic.compare_and_set t.tail prefix (T after)) then
61-
let backoff = Backoff.once backoff in
62-
push t value backoff (Atomic.fenceless_get t.tail)
73+
if not (Atomic.compare_and_set t.tail prefix (T after)) then begin
74+
backoff t;
75+
push t value (Atomic.fenceless_get t.tail)
76+
end
6377
| Snoc move_r as move ->
6478
begin match Atomic.get t.head with
6579
| H (Head head_r as head) when head_r.counter < move_r.counter ->
6680
let after = rev move in
67-
if
68-
Atomic.fenceless_get t.head == H head
69-
&& Atomic.compare_and_set t.head (H head) (H after)
70-
then tail_r.move <- Used
81+
if Atomic.fenceless_get t.head == H head then
82+
if Atomic.compare_and_set t.head (H head) (H after) then
83+
tail_r.move <- Used
84+
else backoff t
7185
| _ -> tail_r.move <- Used
7286
end;
73-
push t value backoff (Atomic.get t.tail)
87+
push t value (Atomic.get t.tail)
7488
end
7589

7690
exception Empty
7791

78-
let rec pop t backoff = function
92+
let rec pop t = function
7993
| H (Cons cons_r as cons) ->
8094
if Atomic.compare_and_set t.head (H cons) cons_r.suffix then cons_r.value
81-
else
82-
let backoff = Backoff.once backoff in
83-
pop t backoff (Atomic.fenceless_get t.head)
95+
else begin
96+
backoff t;
97+
pop t (Atomic.fenceless_get t.head)
98+
end
8499
| H (Head head_r as head) -> begin
85100
match Atomic.get t.tail with
86101
| T (Snoc snoc_r as move) ->
87102
if head_r.counter = snoc_r.counter then
88103
if Atomic.compare_and_set t.tail (T move) snoc_r.prefix then
89104
snoc_r.value
90-
else pop t backoff (Atomic.fenceless_get t.head)
105+
else begin
106+
backoff t;
107+
pop t (Atomic.fenceless_get t.head)
108+
end
91109
else
92110
let (Tail tail_r as tail : (_, [ `Tail ]) tdt) =
93111
Tail { counter = snoc_r.counter; move }
94112
in
95113
let new_head = Atomic.get t.head in
96-
if new_head != H head then pop t backoff new_head
114+
if new_head != H head then pop t new_head
97115
else if Atomic.compare_and_set t.tail (T move) (T tail) then
98116
let (Cons cons_r) = rev move in
99117
let after = cons_r.suffix in
100118
let new_head = Atomic.get t.head in
101-
if new_head != H head then pop t backoff new_head
119+
if new_head != H head then pop t new_head
102120
else if Atomic.compare_and_set t.head (H head) after then begin
103121
tail_r.move <- Used;
104122
cons_r.value
105123
end
106-
else
107-
let backoff = Backoff.once backoff in
108-
pop t backoff (Atomic.fenceless_get t.head)
109-
else pop t backoff (Atomic.fenceless_get t.head)
124+
else begin
125+
backoff t;
126+
pop t (Atomic.fenceless_get t.head)
127+
end
128+
else pop t (Atomic.fenceless_get t.head)
110129
| T (Tail tail_r) -> begin
111130
match tail_r.move with
112131
| Used ->
113132
let new_head = Atomic.get t.head in
114-
if new_head != H head then pop t backoff new_head
115-
else raise_notrace Empty
133+
if new_head != H head then pop t new_head
134+
else begin
135+
backoff_unless_alone t;
136+
raise_notrace Empty
137+
end
116138
| Snoc move_r as move ->
117139
if head_r.counter < move_r.counter then
118140
let (Cons cons_r) = rev move in
119141
let after = cons_r.suffix in
120142
let new_head = Atomic.get t.head in
121-
if new_head != H head then pop t backoff new_head
143+
if new_head != H head then pop t new_head
122144
else if Atomic.compare_and_set t.head (H head) after then begin
123145
tail_r.move <- Used;
124146
cons_r.value
125147
end
126-
else
127-
let backoff = Backoff.once backoff in
128-
pop t backoff (Atomic.fenceless_get t.head)
148+
else begin
149+
backoff t;
150+
pop t (Atomic.fenceless_get t.head)
151+
end
129152
else
130153
let new_head = Atomic.get t.head in
131-
if new_head != H head then pop t backoff new_head
132-
else raise_notrace Empty
154+
if new_head != H head then pop t new_head
155+
else begin
156+
backoff_unless_alone t;
157+
raise_notrace Empty
158+
end
133159
end
134160
end
135161

136-
let rec push_head t value backoff =
162+
let rec push_head t value =
137163
match Atomic.get t.head with
138164
| H (Cons cons_r) as suffix ->
139165
let after = Cons { counter = cons_r.counter - 1; value; suffix } in
140-
if not (Atomic.compare_and_set t.head suffix (H after)) then
141-
push_head t value (Backoff.once backoff)
166+
if not (Atomic.compare_and_set t.head suffix (H after)) then begin
167+
backoff t;
168+
push_head t value
169+
end
142170
| H (Head head_r) as head -> begin
143171
match Atomic.get t.tail with
144172
| T (Snoc snoc_r as move) ->
145-
if Atomic.get t.head != head then push_head t value backoff
173+
if Atomic.get t.head != head then push_head t value
146174
else if head_r.counter = snoc_r.counter then begin
147175
let prefix = T (Snoc { snoc_r with value }) in
148176
let after =
149177
Snoc { snoc_r with counter = snoc_r.counter + 1; prefix }
150178
in
151-
if not (Atomic.compare_and_set t.tail (T move) (T after)) then
152-
push_head t value (Backoff.once backoff)
179+
if not (Atomic.compare_and_set t.tail (T move) (T after)) then begin
180+
backoff t;
181+
push_head t value
182+
end
153183
end
154-
else
184+
else begin
155185
let tail = Tail { counter = snoc_r.counter; move } in
156-
let backoff =
157-
if Atomic.compare_and_set t.tail (T move) (T tail) then backoff
158-
else Backoff.once backoff
159-
in
160-
push_head t value backoff
186+
if not (Atomic.compare_and_set t.tail (T move) (T tail)) then
187+
backoff t;
188+
push_head t value
189+
end
161190
| T (Tail tail_r) as prefix -> begin
162191
match tail_r.move with
163192
| Used ->
164193
if Atomic.get t.head == head then begin
165194
let tail =
166195
Snoc { counter = tail_r.counter + 1; value; prefix }
167196
in
168-
if not (Atomic.compare_and_set t.tail prefix (T tail)) then
169-
push_head t value (Backoff.once backoff)
197+
if not (Atomic.compare_and_set t.tail prefix (T tail)) then begin
198+
backoff t;
199+
push_head t value
200+
end
170201
end
171-
else push_head t value backoff
202+
else push_head t value
172203
| Snoc move_r as move ->
173204
begin match Atomic.get t.head with
174205
| H (Head head_r as head) when head_r.counter < move_r.counter ->
175206
let after = rev move in
176-
if
177-
Atomic.fenceless_get t.head == H head
178-
&& Atomic.compare_and_set t.head (H head) (H after)
179-
then tail_r.move <- Used
207+
if Atomic.fenceless_get t.head == H head then
208+
if Atomic.compare_and_set t.head (H head) (H after) then
209+
tail_r.move <- Used
210+
else backoff t
180211
| _ -> tail_r.move <- Used
181212
end;
182-
push_head t value backoff
213+
push_head t value
183214
end
184215
end
185216

@@ -204,9 +235,5 @@ let[@inline] length t =
204235
tail_at - head_at + 1
205236

206237
let[@inline] is_empty t = length t == 0
207-
let[@inline] pop_exn t = pop t Backoff.default (Atomic.fenceless_get t.head)
208-
209-
let[@inline] push t value =
210-
push t value Backoff.default (Atomic.fenceless_get t.tail)
211-
212-
let[@inline] push_head t value = push_head t value Backoff.default
238+
let[@inline] pop_exn t = pop t (Atomic.fenceless_get t.head)
239+
let[@inline] push t value = push t value (Atomic.fenceless_get t.tail)

lib/picos_aux/index.mld

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ This package contains auxiliary libraries used in the implementation of other
44
Picos libraries.
55

66
{!modules:
7+
Picos_aux_adaptive_backoff
78
Picos_aux_htbl
89
Picos_aux_mpmcq
910
Picos_aux_mpscq

test/dune

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,11 @@
198198
(modules test_mpmcq_dscheck picos_aux_mpmcq)
199199
(build_if
200200
(>= %{ocaml_version} 5))
201-
(libraries backoff multicore-magic-dscheck dscheck alcotest)
201+
(libraries
202+
picos_aux.adaptive_backoff
203+
multicore-magic-dscheck
204+
dscheck
205+
alcotest)
202206
(flags
203207
(:standard -open Multicore_magic_dscheck)))
204208

0 commit comments

Comments
 (0)