diff --git a/benchs/dune b/benchs/dune index f0d36ef7..666f57f6 100644 --- a/benchs/dune +++ b/benchs/dune @@ -10,7 +10,6 @@ qcheck oseq batteries - base sek) (flags :standard -warn-error -3-5 -w -60 -safe-string -color always) (optional) diff --git a/benchs/run_benchs.ml b/benchs/run_benchs.ml index db23a3c4..380183b5 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -1824,4 +1824,35 @@ module Str = struct ]) end +module Hash = struct + let hash_ocaml (n : int) : int = + let offset_basis = 0xcbf29ce484222325L in + let prime = 0x100000001b3L in + let h = ref offset_basis in + for k = 0 to 7 do + h := Int64.(mul !h prime); + h := Int64.(logxor !h (of_int ((n lsr (k * 8)) land 0xff))) + done; + Int64.to_int !h land max_int + + let bench_hash n = + let run_ocaml () = + for i = 0 to n - 1 do + opaque_ignore (hash_ocaml i) + done + and run_c_stub () = + for i = 0 to n - 1 do + opaque_ignore (CCInt.hash i) + done + in + B.throughputN 3 ~repeat + [ + "ocaml_fnv", run_ocaml, (); + "c_stub", run_c_stub, (); + ] + + let () = + B.Tree.register ("hash" @>>> [ "int" @>> app_ints bench_hash [ 1_000 ] ]) +end + let () = try B.Tree.run_global () with Arg.Help msg -> print_endline msg diff --git a/src/core/CCInt.ml b/src/core/CCInt.ml index 801b0857..09d1ba85 100644 --- a/src/core/CCInt.ml +++ b/src/core/CCInt.ml @@ -6,18 +6,8 @@ type 'a iter = ('a -> unit) -> unit (* use FNV: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function *) -let hash (n : int) : int = - let offset_basis = 0xcbf29ce484222325L in - let prime = 0x100000001b3L in - - let h = ref offset_basis in - for k = 0 to 7 do - (h := Int64.(mul !h prime)); - (* h := h xor (k-th byte of n) *) - h := Int64.(logxor !h (of_int ((n lsr (k * 8)) land 0xff))) - done; - (* truncate back to int and remove sign *) - Int64.to_int !h land max_int +external hash : (int[@untagged]) -> (int[@untagged]) + = "caml_cc_hash_int_byte" "caml_cc_hash_int" [@@noalloc] let range i j yield = let rec up i j yield = diff --git a/src/core/cc_stubs.c b/src/core/cc_stubs.c new file mode 100644 index 00000000..1c6befd9 --- /dev/null +++ b/src/core/cc_stubs.c @@ -0,0 +1,48 @@ +#include +#include + +/* FNV-1a hash for a 64-bit integer. + https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + + Core routine: operates on all 8 bytes of an int64_t. */ + +static inline int64_t cc_fnv_hash_int64(int64_t n) { + uint64_t un = (uint64_t)n; + uint64_t h = UINT64_C(0xcbf29ce484222325); + const uint64_t prime = UINT64_C(0x100000001b3); + for (int k = 0; k < 8; k++) { + h *= prime; + h ^= (un >> (k * 8)) & 0xff; + } + return (int64_t)h; +} + +/* --- CCInt.hash entry points (int -> int) --- */ + +/* Mask to the OCaml int range (63 bits on 64-bit, 31 on 32-bit) + before hashing, so negative OCaml ints hash the same as + the unsigned representation seen by OCaml's [lsr]. */ +#define OCAML_INT_MASK ((UINT64_C(1) << (8 * sizeof(value) - 1)) - 1) + +/* native: untagged int in, untagged int out */ +CAMLprim intnat caml_cc_hash_int(intnat n) { + int64_t projected = (int64_t)((uint64_t)n & OCAML_INT_MASK); + return (intnat)((uint64_t)cc_fnv_hash_int64(projected) & Max_long); +} + +/* bytecode: boxed value in, boxed value out */ +CAMLprim value caml_cc_hash_int_byte(value v_n) { + return Val_long(caml_cc_hash_int(Long_val(v_n))); +} + +/* --- int64 hash entry points (int64 -> int) --- */ + +/* native: unboxed int64 in, untagged int out */ +CAMLprim intnat caml_cc_hash_int64(int64_t n) { + return (intnat)((uint64_t)cc_fnv_hash_int64(n) & Max_long); +} + +/* bytecode: boxed int64 value in, boxed value out */ +CAMLprim value caml_cc_hash_int64_byte(value v_n) { + return Val_long(caml_cc_hash_int64(Int64_val(v_n))); +} diff --git a/src/core/dune b/src/core/dune index bd362200..86872496 100644 --- a/src/core/dune +++ b/src/core/dune @@ -6,6 +6,10 @@ (action (run %{project_root}/src/core/cpp/cpp.exe %{input-file}))) (flags :standard -nolabels -open CCMonomorphic) + (foreign_stubs + (language c) + (flags :standard -std=c99 -O2) + (names cc_stubs)) (libraries either containers.monomorphic containers.domain)) (ocamllex