-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathbench_coroutine.ml
285 lines (253 loc) · 7.01 KB
/
bench_coroutine.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
(*
A benchmark of delimcc: comparing ocaml-callcc (undelimited continuations)
with delimcc (delimited continuation library).
The main goal is to compare performance; we can't help but notice the
difference in the comprehensibility of the code.
*)
(* The following is a slightly modified test 'coroutines'
from the file test.ml (version 1.2) of the ocaml-callcc distribution.
Xavier Leroy, Oct 2005.
The modification is to parameterize by nmax and to add a flag to switch
off the printing.
*)
open Callcc;;
let coroutines_cc debug nmax =
callcc (fun init_k ->
let curr_k = ref init_k in
let communicate x =
callcc (fun k -> let old_k = !curr_k in curr_k := k; throw old_k x) in
let rec process1 n =
if n >= nmax then begin
if debug then
begin print_string "1: finished"; print_newline() end;
100
end else begin
if debug then
begin print_string "1: received "; print_int n; print_newline()
end;
process1(communicate(n+1))
end
and process2 n =
if n >= nmax-10 then begin
if debug then
begin print_string "2: finished"; print_newline() end;
nmax / 2
end else begin
if debug then
begin print_string "2: received "; print_int n; print_newline()
end;
process2(communicate(n+1))
end in
process1(callcc(fun start1 ->
process2(callcc(fun start2 ->
curr_k := start2; throw start1 0)))))
;;
(* Exercise to the reader: try to predict the result of the following
expression.
*)
(*
coroutines_cc true 30;;
*)
let 100 = coroutines_cc false 30;;
(* Execute a thunk at a particular stack depth *)
let rec at_depth n thunk =
if n = 0 then thunk ()
else 1 + at_depth (n-1) thunk (* Not tail recursive! *)
;;
(* An implementation using delimited continuations. There are no longer
any mutations. The code is much easier to understand. The code
for process1 and process2 is taken verbatim from
coroutines_cc code above.
*)
open Delimcc;;
type process =
| Done of int
| Ret of int
| Suspend of int * (int -> process)
;;
let coroutines_dc debug nmax =
let p = new_prompt () in
let communicate x = shift0 p (fun k -> Suspend (x,k))
in
(* begin code taken verbatim from coroutines_cc *)
let rec process1 n =
if n >= nmax then begin
if debug then
begin print_string "1: finished"; print_newline() end;
100
end else begin
if debug then
begin print_string "1: received "; print_int n; print_newline()
end;
process1(communicate(n+1))
end
and process2 n =
if n >= nmax-10 then begin
if debug then
begin print_string "2: finished"; print_newline() end;
nmax / 2
end else begin
if debug then
begin print_string "2: received "; print_int n; print_newline()
end;
process2(communicate(n+1))
end in (* end code copied verbatim from coroutines_cc *)
(* The main loop *)
let rec loop curr = function
| Suspend (x,k) -> loop k (curr x)
| Ret x -> loop curr (curr x)
| Done x -> x
in
loop (fun x -> push_prompt p (fun () -> Ret (process2 x)) )
(push_prompt p (fun () -> Done (process1 0)))
;;
let 100 = coroutines_dc false 30;;
(* testing
coroutines_cc true 20;;
coroutines_dc true 20;;
at_depth 5 (fun () -> coroutines_cc true 20);;
at_depth 5 (fun () -> coroutines_dc true 20);;
at_depth 5 (fun () -> coroutines_cc true 30);;
at_depth 5 (fun () -> coroutines_dc true 30);;
*)
(* Holdover
let coroutines_dc_nt debug nmax =
let p = new_prompt () in
let communicate x =
shift0 p (fun k -> Suspend (x,k))
in
let rec process1 n =
if n >= nmax then 100
else begin
if debug then
begin print_string "1: received "; print_int n; print_newline()
end;
let res = process1(communicate(n+1)) in
if debug then
begin print_string "1: finished"; print_newline() end;
res
end
and process2 n =
if n >= nmax-10 then nmax / 2
else begin
if debug then
begin print_string "2: received "; print_int n; print_newline()
end;
let res = process2(communicate(n+1)) in
if debug then
begin print_string "2: finished"; print_newline() end;
res
end in
let rec loop curr = function
| Suspend (x,k) -> loop k (curr x)
| Ret x -> loop curr (curr x)
| Done x -> x
in
loop (fun x -> push_prompt p (fun () ->
Ret (process2 x)) )
(push_prompt p (fun () -> Done (process1 0)))
;;
let 100 = coroutines_dc_nt false 30;;
*)
(* Running the benchmark *)
(* Time the execution *)
let timeit thunk =
let time_start = Sys.time () in
let r = thunk () in
(r, Sys.time () -. time_start)
;;
let bench testf =
let nmax = 5 in
let rec loop res acc = function
| 0 -> Printf.printf "median timing %g sec\n"
(List.nth (List.sort compare acc) (nmax/2));
res
| n ->
let (r,timing) = timeit testf in
begin
match res with
| None -> ()
| Some x -> if not (r = x) then
failwith "Results of different runs differ"
end;
loop (Some r) (timing::acc) (n-1)
in loop None [] nmax
;;
bench (fun () -> coroutines_cc false 100000);;
(* 0.37 sec *)
bench (fun () -> coroutines_dc false 100000);;
(* 1.14 sec *)
(*
bench (fun () -> at_depth 100 (fun () -> coroutines_cc false 100000));;
(* 7.1 sec *)
bench (fun () -> at_depth 100 (fun () -> coroutines_dc false 100000));;
(* 1.15 sec *)
*)
(*
for i = 0 to 10 do
let d = i * 10 in
match bench (fun () -> at_depth d (fun () -> coroutines_cc false 100000)) with
Some x -> Printf.printf "Depth %d result %d\n" d x
done
;;
median timing 0.560035 sec
Depth 0 result 100
median timing 0.676042 sec
Depth 10 result 110
median timing 2.65217 sec
Depth 20 result 120
median timing 4.05225 sec
Depth 30 result 130
median timing 4.7283 sec
Depth 40 result 140
median timing 5.39234 sec
Depth 50 result 150
median timing 6.06438 sec
Depth 60 result 160
median timing 6.73642 sec
Depth 70 result 170
median timing 7.42046 sec
Depth 80 result 180
median timing 8.09251 sec
Depth 90 result 190
median timing 8.74855 sec
Depth 100 result 200
for i = 0 to 15 do
let d = i * 10 in
match bench (fun () -> at_depth d (fun () -> coroutines_dc false 100000)) with
Some x -> Printf.printf "Depth %d result %d\n" d x
done
;;
median timing 1.13607 sec
Depth 0 result 100
median timing 1.14407 sec
Depth 10 result 110
median timing 1.14007 sec
Depth 20 result 120
median timing 1.14407 sec
Depth 30 result 130
median timing 1.14007 sec
Depth 40 result 140
median timing 1.14407 sec
Depth 50 result 150
median timing 1.14407 sec
Depth 60 result 160
median timing 1.14807 sec
Depth 70 result 170
median timing 1.14407 sec
Depth 80 result 180
median timing 1.14807 sec
Depth 90 result 190
median timing 1.14807 sec
Depth 100 result 200
median timing 1.14807 sec
Depth 110 result 210
median timing 1.14407 sec
Depth 120 result 220
median timing 1.15207 sec
Depth 130 result 230
median timing 1.14807 sec
Depth 140 result 240
median timing 1.15207 sec
Depth 150 result 250
*)