Skip to content

Commit b053eac

Browse files
committed
Reduce allocations in hashconsing hash function
Seems to improve performance a bit. Not sure if it is just by not using tuples or because of the hash functions of the other modules.
1 parent 59013a7 commit b053eac

File tree

9 files changed

+260
-16
lines changed

9 files changed

+260
-16
lines changed

src/smtml/bitvector.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ let of_int32 v = make (Z.of_int32 v) 32
2525

2626
let of_int64 v = make (Z.of_int64 v) 64
2727

28+
(* Optimized mixer (DJB2 variant). Inlines to simple arithmetic. *)
29+
let[@inline] combine h v = (h * 33) + v
30+
31+
let hash a = combine (Z.hash a.value) a.width
32+
2833
let equal a b = Z.equal a.value b.value && a.width = b.width
2934

3035
let eqz v = Z.equal Z.zero v.value

src/smtml/bitvector.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ val to_int64 : t -> Int64.t
1818

1919
val numbits : t -> int
2020

21+
val hash : t -> int
22+
2123
val equal : t -> t -> bool
2224

2325
val compare : t -> t -> int

src/smtml/expr.ml

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,56 @@ module Expr = struct
6666
, _ ) ->
6767
false
6868

69+
(* Optimized mixer (DJB2 variant). Inlines to simple arithmetic. *)
70+
let[@inline] combine h v = (h * 33) + v
71+
6972
let hash (e : expr) : int =
70-
let h x = Hashtbl.hash x in
7173
match e with
72-
| Val v -> h v
73-
| Ptr { base; offset } -> h (base, offset.tag)
74-
| Loc l -> h l
75-
| Symbol s -> h s
76-
| List v -> h v
77-
| App (x, es) -> h (x, es)
78-
| Unop (ty, op, e) -> h (ty, op, e.tag)
79-
| Cvtop (ty, op, e) -> h (ty, op, e.tag)
80-
| Binop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
81-
| Relop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
82-
| Triop (ty, op, e1, e2, e3) -> h (ty, op, e1.tag, e2.tag, e3.tag)
83-
| Naryop (ty, op, es) -> h (ty, op, es)
84-
| Extract (e, hi, lo) -> h (e.tag, hi, lo)
85-
| Concat (e1, e2) -> h (e1.tag, e2.tag)
86-
| Binder (b, vars, e) -> h (b, vars, e.tag)
74+
| Val v -> Value.hash v
75+
| Ptr { base; offset } -> combine (Bitvector.hash base) offset.tag
76+
| Loc l -> Loc.hash l
77+
| Symbol s -> Symbol.hash s
78+
| List l -> List.fold_left (fun acc x -> combine acc x.Hc.tag) 0 l
79+
| App (s, es) ->
80+
let h_s = Symbol.hash s in
81+
List.fold_left (fun acc x -> combine acc x.Hc.tag) h_s es
82+
| Unop (ty, op, e) ->
83+
let h1 = Ty.hash ty in
84+
let h2 = combine h1 (Ty.Unop.hash op) in
85+
combine h2 e.tag
86+
| Binop (ty, op, e1, e2) ->
87+
let h = Ty.hash ty in
88+
let h = combine h (Ty.Binop.hash op) in
89+
let h = combine h e1.tag in
90+
combine h e2.tag
91+
| Triop (ty, op, e1, e2, e3) ->
92+
let h = Ty.hash ty in
93+
let h = combine h (Ty.Triop.hash op) in
94+
let h = combine h e1.tag in
95+
let h = combine h e2.tag in
96+
combine h e3.tag
97+
| Relop (ty, op, e1, e2) ->
98+
let h = Ty.hash ty in
99+
let h = combine h (Ty.Relop.hash op) in
100+
let h = combine h e1.tag in
101+
combine h e2.tag
102+
| Cvtop (ty, op, e) ->
103+
let h = Ty.hash ty in
104+
let h = combine h (Ty.Cvtop.hash op) in
105+
combine h e.tag
106+
| Naryop (ty, op, es) ->
107+
let h = Ty.hash ty in
108+
let h = combine h (Ty.Naryop.hash op) in
109+
List.fold_left (fun acc x -> combine acc x.Hc.tag) h es
110+
| Extract (e, hi, lo) ->
111+
let h = e.tag in
112+
let h = combine h hi in
113+
combine h lo
114+
| Concat (e1, e2) -> combine e1.tag e2.tag
115+
| Binder (b, vars, e) ->
116+
let h = Hashtbl.hash b in
117+
let h_vars = List.fold_left (fun acc x -> combine acc x.Hc.tag) h vars in
118+
combine h_vars e.tag
87119
end
88120

89121
module Hc = Hc.Make [@inlined hint] (Expr)

src/smtml/symbol.ml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ let namespace { namespace; _ } = namespace
4444

4545
let discr_namespace = function Attr -> 0 | Sort -> 1 | Term -> 2 | Var -> 3
4646

47+
(* Optimized mixer (DJB2 variant). Inlines to simple arithmetic. *)
48+
let[@inline] combine h v = (h * 33) + v
49+
50+
let hash_name n =
51+
match n with
52+
| Simple s ->
53+
(* Hashtbl.hash is fine for strings (it's a C primitive) *)
54+
combine 0 (Hashtbl.hash s)
55+
| Indexed { basename; indices } ->
56+
let h = combine 1 (Hashtbl.hash basename) in
57+
(* Fold over indices to avoid list allocation *)
58+
List.fold_left (fun acc s -> combine acc (Hashtbl.hash s)) h indices
59+
60+
let hash { ty; name; namespace } =
61+
let h = Ty.hash ty in
62+
let h = combine h (hash_name name) in
63+
combine h (discr_namespace namespace)
64+
4765
let compare_namespace a b = compare (discr_namespace a) (discr_namespace b)
4866

4967
let compare_name a b =

src/smtml/symbol.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ val type_of : t -> Ty.t
8686

8787
(** {1 Comparison} *)
8888

89+
val hash : t -> int
90+
8991
(** [compare sym1 sym2] performs a total order comparison of [sym1] and [sym2].
9092
*)
9193
val compare : t -> t -> int

src/smtml/ty.ml

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ let discr = function
3535
| Ty_bitv n -> 10 + n
3636
| Ty_fp n -> 11 + n
3737

38+
(* Optimized mixer (DJB2 variant). Inlines to simple arithmetic. *)
39+
let[@inline] combine h v = (h * 33) + v
40+
41+
let hash = function
42+
| Ty_app -> 1
43+
| Ty_bitv width -> combine 2 width
44+
| Ty_bool -> 3
45+
| Ty_fp width -> combine 4 width
46+
| Ty_int -> 5
47+
| Ty_list -> 6
48+
| Ty_none -> 7
49+
| Ty_real -> 8
50+
| Ty_str -> 9
51+
| Ty_unit -> 10
52+
| Ty_regexp -> 11
53+
| Ty_roundingMode -> 12
54+
3855
let compare t1 t2 = compare (discr t1) (discr t2)
3956

4057
let equal t1 t2 = compare t1 t2 = 0
@@ -128,6 +145,42 @@ module Unop = struct
128145
| Regexp_comp
129146
[@@deriving ord]
130147

148+
let hash = function
149+
| Neg -> 0
150+
| Not -> 1
151+
| Clz -> 2
152+
| Ctz -> 3
153+
| Popcnt -> 4
154+
(* Float *)
155+
| Abs -> 5
156+
| Sqrt -> 6
157+
| Is_normal -> 7
158+
| Is_subnormal -> 8
159+
| Is_negative -> 9
160+
| Is_positive -> 10
161+
| Is_infinite -> 11
162+
| Is_nan -> 12
163+
| Is_zero -> 13
164+
| Ceil -> 14
165+
| Floor -> 15
166+
| Trunc -> 16
167+
| Nearest -> 17
168+
| Head -> 18
169+
| Tail -> 19
170+
| Reverse -> 20
171+
| Length -> 21
172+
(* String *)
173+
| Trim -> 22
174+
(* RegExp *)
175+
| Regexp_star -> 23
176+
| Regexp_loop (min, max) ->
177+
let h = 24 in
178+
let h = combine h min in
179+
combine h max
180+
| Regexp_plus -> 25
181+
| Regexp_opt -> 26
182+
| Regexp_comp -> 27
183+
131184
let equal o1 o2 =
132185
match (o1, o2) with
133186
| Neg, Neg
@@ -235,6 +288,41 @@ module Binop = struct
235288
| Regexp_diff
236289
[@@deriving ord]
237290

291+
let hash = function
292+
| Add -> 0
293+
| Sub -> 1
294+
| Mul -> 2
295+
| Div -> 3
296+
| DivU -> 4
297+
| Rem -> 5
298+
| RemU -> 6
299+
| Shl -> 7
300+
| ShrA -> 8
301+
| ShrL -> 9
302+
| And -> 10
303+
| Or -> 11
304+
| Xor -> 12
305+
| Implies -> 13
306+
| Pow -> 14
307+
| Min -> 15
308+
| Max -> 16
309+
| Copysign -> 17
310+
| Rotl -> 18
311+
| Rotr -> 19
312+
| At -> 20
313+
| List_cons -> 21
314+
| List_append -> 22
315+
(* String *)
316+
| String_prefix -> 23
317+
| String_suffix -> 24
318+
| String_contains -> 25
319+
| String_last_index -> 26
320+
| String_in_re -> 27
321+
(* Regexp *)
322+
| Regexp_range -> 28
323+
| Regexp_inter -> 29
324+
| Regexp_diff -> 30
325+
238326
let equal o1 o2 =
239327
match (o1, o2) with
240328
| Add, Add
@@ -325,6 +413,18 @@ module Relop = struct
325413
| GeU
326414
[@@deriving ord]
327415

416+
let hash = function
417+
| Eq -> 0
418+
| Ne -> 1
419+
| Lt -> 2
420+
| LtU -> 3
421+
| Gt -> 4
422+
| GtU -> 5
423+
| Le -> 6
424+
| LeU -> 7
425+
| Ge -> 8
426+
| GeU -> 9
427+
328428
let equal op1 op2 =
329429
match (op1, op2) with
330430
| Eq, Eq
@@ -366,6 +466,17 @@ module Triop = struct
366466
| String_replace_re_all
367467
[@@deriving ord]
368468

469+
let hash = function
470+
| Ite -> 0
471+
| List_set -> 1
472+
(* String *)
473+
| String_extract -> 2
474+
| String_replace -> 3
475+
| String_index -> 4
476+
| String_replace_all -> 5
477+
| String_replace_re -> 6
478+
| String_replace_re_all -> 7
479+
369480
let equal op1 op2 =
370481
match (op1, op2) with
371482
| Ite, Ite
@@ -427,6 +538,38 @@ module Cvtop = struct
427538
| String_to_re
428539
[@@deriving ord]
429540

541+
let hash = function
542+
| ToString -> 0
543+
| OfString -> 1
544+
| ToBool -> 2
545+
| OfBool -> 3
546+
| Reinterpret_int -> 4
547+
| Reinterpret_float -> 5
548+
| DemoteF64 -> 6
549+
| PromoteF32 -> 7
550+
| ConvertSI32 -> 8
551+
| ConvertUI32 -> 9
552+
| ConvertSI64 -> 10
553+
| ConvertUI64 -> 11
554+
| TruncSF32 -> 12
555+
| TruncUF32 -> 13
556+
| TruncSF64 -> 14
557+
| TruncUF64 -> 15
558+
| Trunc_sat_f32_s -> 16
559+
| Trunc_sat_f32_u -> 17
560+
| Trunc_sat_f64_s -> 18
561+
| Trunc_sat_f64_u -> 19
562+
| WrapI64 -> 20
563+
| Sign_extend i -> combine 21 i
564+
| Zero_extend i -> combine 22 i
565+
(* String *)
566+
| String_to_code -> 23
567+
| String_from_code -> 24
568+
| String_to_int -> 25
569+
| String_from_int -> 26
570+
| String_to_float -> 27
571+
| String_to_re -> 28
572+
430573
let equal op1 op2 =
431574
match (op1, op2) with
432575
| ToString, ToString
@@ -508,6 +651,12 @@ module Naryop = struct
508651
| Regexp_union
509652
[@@deriving ord]
510653

654+
let hash = function
655+
| Logand -> 0
656+
| Logor -> 1
657+
| Concat -> 2
658+
| Regexp_union -> 3
659+
511660
let equal op1 op2 =
512661
match (op1, op2) with
513662
| Logand, Logand

src/smtml/ty.mli

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type t =
3434

3535
(** {1 Type Comparison} *)
3636

37+
val hash : t -> int
38+
3739
(** [compare t1 t2] performs a total order comparison of types [t1] and [t2]. *)
3840
val compare : t -> t -> int
3941

@@ -97,6 +99,8 @@ module Unop : sig
9799
| Regexp_comp (** Complement. *)
98100
[@@deriving ord]
99101

102+
val hash : t -> int
103+
100104
(** [equal op1 op2] checks if unary operations [op1] and [op2] are equal. *)
101105
val equal : t -> t -> bool
102106

@@ -149,6 +153,8 @@ module Binop : sig
149153
| Regexp_diff (** Difference of regular expressions. *)
150154
[@@deriving ord]
151155

156+
val hash : t -> int
157+
152158
(** [equal op1 op2] checks if binary operations [op1] and [op2] are equal. *)
153159
val equal : t -> t -> bool
154160

@@ -174,6 +180,8 @@ module Relop : sig
174180
| GeU (** Unsigned greater than or equal. *)
175181
[@@deriving ord]
176182

183+
val hash : t -> int
184+
177185
(** [equal op1 op2] checks if relational operations [op1] and [op2] are equal.
178186
*)
179187
val equal : t -> t -> bool
@@ -207,6 +215,8 @@ module Triop : sig
207215
(str.replace_re_all String RegLan String) *)
208216
[@@deriving ord]
209217

218+
val hash : t -> int
219+
210220
(** [equal op1 op2] checks if ternary operations [op1] and [op2] are equal. *)
211221
val equal : t -> t -> bool
212222

@@ -254,6 +264,8 @@ module Cvtop : sig
254264
| String_to_re (** Convert string to regular expression. *)
255265
[@@deriving ord]
256266

267+
val hash : t -> int
268+
257269
(** [equal op1 op2] checks if conversion operations [op1] and [op2] are equal.
258270
*)
259271
val equal : t -> t -> bool
@@ -274,6 +286,8 @@ module Naryop : sig
274286
| Regexp_union (** Union of regular expressions. *)
275287
[@@deriving ord]
276288

289+
val hash : t -> int
290+
277291
(** [equal op1 op2] checks if n-ary operations [op1] and [op2] are equal. *)
278292
val equal : t -> t -> bool
279293

0 commit comments

Comments
 (0)