Skip to content

Commit e5698cf

Browse files
committed
Optimize tuple bu loop unrolling
1 parent bd638ca commit e5698cf

6 files changed

Lines changed: 217 additions & 72 deletions

File tree

drivers/json/test/bench.ml

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module type Test = sig
1818
val t: unit -> t
1919
val to_json: t -> Json.t
2020
val to_yojson: t -> Yojson.Safe.json
21-
val of_json: Json.t -> (t, Json.t) Protocol_conv.Runtime.result
21+
val of_json: Json.t -> (t, Json.error) Protocol_conv.Runtime.result
2222
val of_yojson: Yojson.Safe.json -> t Ppx_deriving_yojson_runtime.error_or
2323
end
2424

@@ -158,28 +158,116 @@ let bench (module X: Test) =
158158
Bench.Test.create_group ~name:X.name [
159159
Bench.Test.create_group ~name:"Deserialize" [
160160
Bench.Test.create ~name:"to_yojson"
161-
(fun () -> for _ = 0 to 10000 do X.of_yojson yojson |> ignore done);
161+
(fun () -> X.of_yojson yojson);
162162
Bench.Test.create ~name:"to_json"
163-
(fun () -> for _ = 0 to 10000 do X.of_json json |> ignore done);
163+
(fun () -> X.of_json json);
164164
]
165165
]
166166
];
167167
Core.Command.run @@ Bench.make_command @@ [
168168
Bench.Test.create_group ~name:X.name [
169169
Bench.Test.create_group ~name:"Serialize" [
170170
Bench.Test.create ~name:"to_yojson"
171-
(fun () -> for _ = 0 to 10000 do X.to_yojson t |> ignore done);
171+
(fun () -> X.to_yojson t);
172172
Bench.Test.create ~name:"to_json"
173-
(fun () -> for _ = 0 to 10000 do X.to_json t |> ignore done);
173+
(fun () -> X.to_json t);
174174
];
175175
];
176176
];
177177
()
178+
(*
179+
module Bench_lookup = struct
180+
type t =
181+
| A0
182+
| A1
183+
| A2
184+
| A3
185+
| A4
186+
| A5
187+
| A6
188+
| A7
189+
| A8
190+
| A9
191+
| A10
192+
| A11
193+
| A12
194+
| A13
195+
| A14
196+
| A15
197+
| A16
198+
| A17
199+
| A18
200+
| A19
178201
202+
let alist = [
203+
"A0", A0;
204+
"B1", A1;
205+
"C2", A2;
206+
"D3", A3;
207+
"E4", A4;
208+
"F5", A5;
209+
"G6", A6;
210+
"H7", A7;
211+
"I8", A8;
212+
"J9", A9;
213+
"A10", A10;
214+
"B11", A11;
215+
"C12", A12;
216+
"D13", A13;
217+
"E14", A14;
218+
"F15", A15;
219+
"G16", A16;
220+
"H17", A17;
221+
"I18", A18;
222+
"J19", A19;
223+
]
224+
225+
let test_keys = List.map ~f:fst alist
226+
|> fun l -> List.permute l
227+
228+
let ocaml = function
229+
| "A0" -> Some A0
230+
| "B1" -> Some A1
231+
| "C2" -> Some A2
232+
| "D3" -> Some A3
233+
| "E4" -> Some A4
234+
| "F5" -> Some A5
235+
| "G6" -> Some A6
236+
| "H7" -> Some A7
237+
| "I8" -> Some A8
238+
| "J9" -> Some A9
239+
| "A10" -> Some A10
240+
| "B11" -> Some A11
241+
| "C12" -> Some A12
242+
| "D13" -> Some A13
243+
| "E14" -> Some A14
244+
| "F15" -> Some A15
245+
| "G16" -> Some A16
246+
| "H17" -> Some A17
247+
| "I18" -> Some A18
248+
| "J19" -> Some A19
249+
| _ -> None
250+
251+
let tests = [
252+
"ocaml", ocaml;
253+
"hashtbl", Protocol_conv.Runtime.Helper.Hashtbl_lookup.of_alist alist;
254+
"map", Protocol_conv.Runtime.Helper.Map_lookup.of_alist alist;
255+
"radix", Protocol_conv.Runtime.Helper.Radix_lookup.of_alist alist;
256+
"cmph", Protocol_conv.Runtime.Helper.Cmph_lookup.of_alist alist;
257+
"alist", Protocol_conv.Runtime.Helper.List_lookup.of_alist alist;
258+
]
259+
260+
let bench () =
261+
Core.Command.run @@ Bench.make_command @@
262+
List.map ~f:(fun (name, f) ->
263+
Bench.Test.create ~name
264+
(fun () -> List.iter test_keys ~f:(fun x -> f x |> ignore)) ) tests
265+
end
266+
*)
179267

180268
let () =
181-
bench (module Test_enum);
269+
bench (module Test_record);
182270
bench (module Test_tuple);
271+
bench (module Test_enum);
183272
bench (module Test_variant_record);
184-
bench (module Test_record);
185273
bench (module Test_full);

drivers/json/test/dune

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
(executable
2727
(name bench)
2828
(modules bench)
29-
(ocamlopt_flags :standard -O3 -inline 100000)
3029
(libraries ppx_protocol_conv_json core_bench)
3130
(preprocess (pps ppx_protocol_conv ppx_deriving_yojson)))
3231

drivers/json/test/test_expect.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ module Test = struct
178178
| Json.Protocol_error err -> Printf.eprintf "Lazy: Got expected error: %s" (Json.error_to_string_hum err);
179179
end;
180180
[%expect {|
181-
`Fst: 5
182-
`Snd: int expected. Got: "string" |}]
181+
First: 5
182+
Lazy: Got expected error: int expected. Got: "ipsum" |}]
183183
end
184184

185185
module Yojson_test = struct

dune-workspace

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(lang dune 1.1)
2+
(env
3+
(bench (ocamlopt_flags :standard -O3 -inline 20 -inline-max-depth 5 -inline-max-unroll 5 -unbox-closures -unboxed-types))
4+
)

runtime/runtime.ml

Lines changed: 113 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,29 @@ module type Driver = sig
101101
val of_unit: unit -> t
102102
end
103103

104-
(** This module contains helper functions for serializing and deserializing
105-
tuples, records and variants. *)
104+
(** Module contains helper function for serializing and deserializing tuples, records and variants.
105+
Deserialization functions may raise [Helper.Protocol] exception. It is recommended that the calling functions
106+
convert this exception into a [Driver.Protocol_exception]
107+
*)
106108
module Helper = struct
109+
110+
(** Excpetion raised if the type could not be serialized *)
107111
exception Protocol_error of string
108112

109113
(**/**)
114+
module type Lookup = sig
115+
val of_alist: (string * 'a) list -> string -> 'a option
116+
end
117+
module Hashtbl_lookup : Lookup = struct (* 20.22% *)
118+
let of_alist alist =
119+
let tbl = Hashtbl.of_alist_exn (module String) alist in
120+
Hashtbl.find tbl
121+
end
122+
module Lookup = Hashtbl_lookup
123+
110124
let raise_errorf: ('a, unit, string, 'b) format4 -> 'a = fun fmt -> Printf.ksprintf (fun s -> raise (Protocol_error s)) fmt
111125
(**/**)
112126

113-
type 'a variant = Record of (string * 'a) list | Tuple of 'a list | Nil
114-
115127
(** Map fields names of a [Record_in] structure *)
116128
let rec map_record_in: type t a b. (string -> string) -> (t, a, b) Record_in.t -> (t, a, b) Record_in.t = fun field -> function
117129
| Record_in.Cons ((field_name, to_value_func, default), xs) ->
@@ -130,37 +142,32 @@ module Helper = struct
130142
| Record_in.Nil -> []
131143
in
132144
let rec inner: type constr. int -> (t, constr, b) Record_in.t -> constr -> t option array -> b = fun idx ->
145+
let open Record_in in
146+
let value_of to_v field default t = match t, default with
147+
| Some t, _ -> to_v t
148+
| None, Some d -> d
149+
| None, None -> raise_errorf "Missing record field: %s" field
150+
in
133151
function
134-
| Record_in.Cons ((_field, to_value_func, Some default), xs) ->
152+
| (Cons ((n1, f1, d1), xs)) ->
135153
let cont = inner (idx + 1) xs in
136154
fun constr values ->
137-
let v = match values.(idx) with
138-
| None -> default
139-
| Some t -> to_value_func t
140-
in
141-
cont (constr v) values
142-
| Record_in.Cons ((field, to_value_func, None), xs) ->
143-
let cont = inner (idx + 1) xs in
144-
fun constr values ->
145-
let v = match values.(idx) with
146-
| None -> raise_errorf "Missing record field: %s" field
147-
| Some t -> to_value_func t
148-
in
149-
cont (constr v) values
150-
| Record_in.Nil -> fun a _map -> a
155+
let v1 = value_of f1 n1 d1 values.(idx + 0) in
156+
cont (constr v1) values
157+
158+
| Nil -> fun a _values -> a
151159
in
152160
fun ?(strict=false) spec constr ->
153-
let table =
154-
to_alist 0 spec
155-
|> Hashtbl.of_alist_exn (module String)
161+
let lookup, count =
162+
let alist = to_alist 0 spec in
163+
Lookup.of_alist alist, List.length alist
156164
in
157-
let count = Hashtbl.length table in
158165
let f = inner 0 spec constr in
159166

160167
fun values ->
161168
let value_array = Array.create ~len:count None in
162169
List.iter ~f:(fun (field, t) ->
163-
match Hashtbl.find table field with
170+
match lookup field with
164171
| None when strict -> raise_errorf "Unused field when deserialising record: %s" field
165172
| None -> ()
166173
| Some idx -> begin
@@ -186,47 +193,100 @@ module Helper = struct
186193
*)
187194
let of_record: type t a t. omit_default:bool -> t serialize_record -> (t, a, t) Record_out.t -> a =
188195
fun ~omit_default serialize_record ->
189-
let rec inner: type a. (t, a, t) Record_out.t -> (string * t) list -> a = function
190-
| Record_out.Cons ((field, to_t, default), xs) ->
191-
let cont = inner xs in
192-
let f = match omit_default, default with
193-
| true, Some d -> begin
194-
fun acc -> function
195-
| v when Poly.equal v d -> cont acc
196-
| v -> cont ((field, to_t v) :: acc)
196+
let value =
197+
match omit_default with
198+
| false -> fun n f _d -> fun v acc -> (n, f v) :: acc
199+
| true -> fun n f d -> begin match d with
200+
| Some d -> begin
201+
fun v acc -> match Poly.equal v d with
202+
| true -> acc
203+
| false -> (n, f v) :: acc
197204
end
198-
| _, _ -> fun acc v -> cont ((field, to_t v) :: acc)
199-
in
200-
f
205+
| None -> fun v acc -> (n, f v) :: acc
206+
end
207+
in
208+
let rec inner: type a. (t, a, t) Record_out.t -> (string * t) list -> a =
209+
let open Record_out in
210+
function
211+
| Cons ((n1, f1, d1), xs) ->
212+
let cont = inner xs in
213+
let vf1 = value n1 f1 d1 in
214+
fun acc v1 ->
215+
cont (vf1 v1 acc)
216+
201217
| Record_out.Nil ->
202218
fun acc -> serialize_record acc
203219
in
204220
fun spec -> inner spec []
205221

206222
(** {!to_tuple spec tlist} produces a tuple from the serialized values in [tlist] *)
207223
let rec to_tuple: type t a b. (t, a, b) Tuple_in.t -> a -> t list -> b =
224+
let open Tuple_in in
208225
function
209-
| Tuple_in.Cons (to_value_func, xs) ->
210-
let cont = to_tuple xs in
211-
fun constructor -> begin function
212-
| t :: ts ->
213-
let v = to_value_func t in
214-
cont (constructor v) ts
215-
| [] -> raise_errorf "Too few elements when parsing tuple"
216-
end
217-
| Tuple_in.Nil -> fun a -> function
226+
| Cons (f1, Cons (f2, Cons (f3, Cons (f4, Cons (f5, Nil))))) -> begin
227+
fun constructor -> function
228+
| [v1; v2; v3; v4; v5] -> constructor (f1 v1) (f2 v2) (f3 v3) (f4 v4) (f5 v5)
229+
| _ :: _ :: _ :: _ :: _ :: _ :: _ -> raise_errorf "Too many elements when parsing tuple"
230+
| _ -> raise_errorf "Too few elements when parsing tuple"
231+
end
232+
| Cons (f1, Cons (f2, Cons (f3, Cons (f4, Nil)))) -> begin
233+
fun constructor -> function
234+
| [v1; v2; v3; v4] -> constructor (f1 v1) (f2 v2) (f3 v3) (f4 v4)
235+
| _ :: _ :: _ :: _ :: _ :: _ -> raise_errorf "Too many elements when parsing tuple"
236+
| _ -> raise_errorf "Too few elements when parsing tuple"
237+
end
238+
| Cons (f1, Cons (f2, Cons (f3, Nil))) -> begin
239+
fun constructor -> function
240+
| [v1; v2; v3] -> constructor (f1 v1) (f2 v2) (f3 v3)
241+
| _ :: _ :: _ :: _ :: _ -> raise_errorf "Too many elements when parsing tuple"
242+
| _ -> raise_errorf "Too few elements when parsing tuple"
243+
end
244+
| Cons (f1, Cons (f2, Nil)) -> begin
245+
fun constructor -> function
246+
| [v1; v2] -> constructor (f1 v1) (f2 v2)
247+
| _ :: _ :: _ :: _ -> raise_errorf "Too many elements when parsing tuple"
248+
| _ -> raise_errorf "Too few elements when parsing tuple"
249+
end
250+
| Cons (f1, Nil) -> begin
251+
fun constructor -> function
252+
| [v1] -> constructor (f1 v1)
253+
| _ :: _ :: _ -> raise_errorf "Too many elements when parsing tuple"
254+
| _ -> raise_errorf "Too few elements when parsing tuple"
255+
end
256+
| Nil -> fun a -> begin
257+
function
218258
| [] -> a
219259
| _ -> raise_errorf "Too many elements when parsing tuple"
260+
end
261+
262+
| Cons (f1, Cons (f2, Cons (f3, Cons (f4, Cons (f5, xs))))) -> begin
263+
let cont = to_tuple xs in
264+
fun constructor -> function
265+
| v1 :: v2 :: v3 :: v4 :: v5 :: ts -> cont (constructor (f1 v1) (f2 v2) (f3 v3) (f4 v4) (f5 v5)) ts
266+
| _ -> raise_errorf "Too few elements when parsing tuple"
267+
end
220268

221269
type 't serialize_tuple = 't list -> 't
222270
let of_tuple: type t a. t serialize_tuple -> (t, a, t) Tuple_out.t -> a = fun serialize_tuple ->
223-
let rec inner: type a. (t, a, t) Tuple_out.t -> t list -> a = function
224-
| Tuple_out.Cons (to_t, xs) ->
225-
let cont = inner xs in
226-
fun acc v ->
227-
cont (to_t v :: acc)
228-
| Tuple_out.Nil ->
271+
let rec inner: type a. (t, a, t) Tuple_out.t -> t list -> a =
272+
let open Tuple_out in
273+
function
274+
| Cons (f1, Cons (f2, (Cons (f3, (Cons (f4, Cons (f5, Nil))))))) ->
275+
fun acc v1 v2 v3 v4 v5 -> List.rev_append acc [f1 v1; f2 v2; f3 v3; f4 v4; f5 v5] |> serialize_tuple
276+
| Cons (f1, Cons (f2, (Cons (f3, (Cons (f4, Nil)))))) ->
277+
fun acc v1 v2 v3 v4 -> List.rev_append acc [f1 v1; f2 v2; f3 v3; f4 v4] |> serialize_tuple
278+
| Cons (f1, Cons (f2, (Cons (f3, Nil)))) ->
279+
fun acc v1 v2 v3 -> List.rev_append acc [f1 v1; f2 v2; f3 v3] |> serialize_tuple
280+
| Cons (f1, Cons (f2, Nil)) ->
281+
fun acc v1 v2 -> List.rev_append acc [f1 v1; f2 v2] |> serialize_tuple
282+
| Cons (f1, Nil) ->
283+
fun acc v1 -> List.rev_append acc [f1 v1] |> serialize_tuple
284+
| Nil ->
229285
fun acc -> List.rev acc |> serialize_tuple
286+
287+
| Cons (f1, Cons (f2, (Cons (f3, (Cons (f4, Cons (f5, xs))))))) ->
288+
let cont = inner xs in
289+
fun acc v1 v2 v3 v4 v5 -> cont (f5 v5 :: f4 v4 :: f3 v3 :: f2 v2 :: f1 v1 :: acc)
230290
in
231291
fun spec -> inner spec []
232292

@@ -245,12 +305,12 @@ module Helper = struct
245305
List.map variant ~f:(fun (Variant_in.Variant (name, spec, constr)) -> Variant_in.Variant (constructor name, spec, constr))
246306

247307
let to_variant: ('t, 'a) Variant_in.t list -> string -> 't list -> 'a = fun spec ->
248-
let table =
308+
let lookup =
249309
List.map spec ~f:(fun (Variant_in.Variant (name, spec, constr)) -> name, to_tuple spec constr)
250-
|> Hashtbl.of_alist_exn (module String)
310+
|> Lookup.of_alist
251311
in
252312
fun name args ->
253-
match Hashtbl.find table name with
313+
match lookup name with
254314
| None -> raise_errorf "Unknown variant name: %s" name
255315
| Some f -> f args
256316
end

0 commit comments

Comments
 (0)