From 903243643f987413f9546460ec7c5c3b0c664439 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 4 Apr 2021 17:45:36 -0400 Subject: [PATCH] perf(hash): use FNV hashing --- src/util/Hash.ml | 64 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/src/util/Hash.ml b/src/util/Hash.ml index 3d47a2b1..ee43b20c 100644 --- a/src/util/Hash.ml +++ b/src/util/Hash.ml @@ -3,24 +3,63 @@ type 'a t = 'a -> int -let bool b = if b then 1 else 2 +(* FNV hashing + https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function +*) +let fnv_offset_basis = 0xcbf29ce484222325L +let fnv_prime = 0x100000001b3L -let int i = i land max_int +(* hash an integer *) +let hash_int_ n = + let h = ref fnv_offset_basis in + for k = 0 to 7 do + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((n lsr (k * 8)) land 0xff))); + done; + (Int64.to_int !h) land max_int (* truncate back to int and remove sign *) -let string (s:string) = Hashtbl.hash s +let combine2 a b = + let h = ref fnv_offset_basis in + (* we only do one loop, where we mix bytes of [a] and [b], so as + to simplify control flow *) + for k = 0 to 7 do + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((a lsr (k * 8)) land 0xff))); + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((b lsr (k * 8)) land 0xff))); + done; + Int64.to_int !h land max_int -let combine f a b = Hashtbl.seeded_hash a (f b) - -let combine2 a b = Hashtbl.seeded_hash a b +let[@inline] combine f s x = + combine2 s (f x) let combine3 a b c = - combine2 a b - |> combine2 c + let h = ref fnv_offset_basis in + (* we only do one loop, where we mix bytes of [a] [b] and [c], so as + to simplify control flow *) + for k = 0 to 7 do + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((a lsr (k * 8)) land 0xff))); + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((b lsr (k * 8)) land 0xff))); + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((c lsr (k * 8)) land 0xff))); + done; + Int64.to_int !h land max_int let combine4 a b c d = - combine2 a b - |> combine2 c - |> combine2 d + let h = ref fnv_offset_basis in + for k = 0 to 7 do + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((a lsr (k * 8)) land 0xff))); + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((b lsr (k * 8)) land 0xff))); + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((c lsr (k * 8)) land 0xff))); + h := Int64.(mul !h fnv_prime); + h := Int64.(logxor !h (of_int ((d lsr (k * 8)) land 0xff))); + done; + Int64.to_int !h land max_int let pair f g (x,y) = combine2 (f x) (g y) @@ -28,9 +67,12 @@ let opt f = function | None -> 42 | Some x -> combine2 43 (f x) +let int = hash_int_ +let bool b = hash_int_ (if b then 1 else 2) let list f l = List.fold_left (combine f) 0x42 l let array f = Array.fold_left (combine f) 0x43 let iarray f = IArray.fold (combine f) 0x44 +let string : string t = Hashtbl.hash let seq f seq = let h = ref 0x43 in seq (fun x -> h := combine f !h x);