fix(int): use shims to provide separate 32/64 bits versions of popcount

close #327
This commit is contained in:
Simon Cruanes 2020-08-05 14:05:48 -04:00
parent 9fe414f793
commit e0f2c78edd
3 changed files with 59 additions and 35 deletions

View file

@ -319,40 +319,20 @@ let range_by ~step i j yield =
(CCInt.range i j |> Iter.to_list) ) (CCInt.range i j |> Iter.to_list) )
*) *)
(* (* popcount comes from [Shims] as it's 32/64 bits dependent, see #327 *)
from https://en.wikipedia.org/wiki/Hamming_weight
//This uses fewer arithmetic operations than any other known
//implementation on machines with slow multiplication.
//It uses 17 arithmetic operations.
int popcount_2(uint64_t x) {
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
x += x >> 8; //put count of each 16 bits into their lowest 8 bits
x += x >> 16; //put count of each 32 bits into their lowest 8 bits
x += x >> 32; //put count of each 64 bits into their lowest 8 bits
return x & 0x7f;
}
m1 = 0x5555555555555555
m2 = 0x3333333333333333
m4 = 0x0f0f0f0f0f0f0f0f
*)
let popcount (b:int) : int = let popcount (b:int) : int =
let b = b - ((b lsr 1) land 0x5555555555555555) in let rec loop count x =
let b = (b land 0x3333333333333333) + ((b lsr 2) land 0x3333333333333333) in if x=0 then count
let b = (b + (b lsr 4)) land 0x0f0f0f0f0f0f0f0f in else loop (count+1) (x land (x-1))
let b = b + (b lsr 8) in in
let b = b + (b lsr 16) in loop 0 b
let b = b + (b lsr 32) in
b land 0x7f
(*$= (*$=
0 (popcount 0) 0 (popcount 0)
1 (popcount 1) 1 (popcount 1)
(Sys.word_size-2) (popcount max_int) (Sys.word_size-2) (popcount max_int)
1 (popcount min_int) 1 (popcount min_int)
10 (popcount 0b1110010110110001010)
5 (popcount 0b1101110000000000) 5 (popcount 0b1101110000000000)
*) *)

View file

@ -170,6 +170,48 @@ let shims_int_post_408 = "
(** {{: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Int.html} Documentation for the standard Int module}*) (** {{: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Int.html} Documentation for the standard Int module}*)
" "
let shims_int_64bit = "
(*
from https://en.wikipedia.org/wiki/Hamming_weight
//This uses fewer arithmetic operations than any other known
//implementation on machines with slow multiplication.
//It uses 17 arithmetic operations.
int popcount_2(uint64_t x) {
x -= (x >> 1) & m1; //put count of each 2 bits into those 2 bits
x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
x = (x + (x >> 4)) & m4; //put count of each 8 bits into those 8 bits
x += x >> 8; //put count of each 16 bits into their lowest 8 bits
x += x >> 16; //put count of each 32 bits into their lowest 8 bits
x += x >> 32; //put count of each 64 bits into their lowest 8 bits
return x & 0x7f;
}
m1 = 0x5555555555555555
m2 = 0x3333333333333333
m4 = 0x0f0f0f0f0f0f0f0f
*)
let popcount (b:int) : int =
let b = b - ((b lsr 1) land 0x5555555555555555) in
let b = (b land 0x3333333333333333) + ((b lsr 2) land 0x3333333333333333) in
let b = (b + (b lsr 4)) land 0x0f0f0f0f0f0f0f0f in
let b = b + (b lsr 8) in
let b = b + (b lsr 16) in
let b = b + (b lsr 32) in
b land 0x7f
"
let shims_int_32bit = "
(* we use the simple version for 32 bits. *)
let popcount (b:int) : int =
let rec loop count x =
if x=0 then count
else loop (count+1) (x land (x-1))
in
loop 0 b
"
let () = let () =
C.main ~name:"mkshims" (fun c -> C.main ~name:"mkshims" (fun c ->
let version = C.ocaml_config_var_exn c "version" in let version = C.ocaml_config_var_exn c "version" in
@ -188,5 +230,7 @@ let () =
write_file "CCShimsFun_.ml" (if (major, minor) >= (4,8) then shims_fun_post_408 else shims_fun_pre_408); write_file "CCShimsFun_.ml" (if (major, minor) >= (4,8) then shims_fun_post_408 else shims_fun_pre_408);
write_file "CCShimsFun_.mli" (if (major, minor) >= (4,8) then shims_fun_mli_post_408 else shims_fun_mli_pre_408); write_file "CCShimsFun_.mli" (if (major, minor) >= (4,8) then shims_fun_mli_post_408 else shims_fun_mli_pre_408);
write_file "CCShimsMkLet_.ml" (if (major, minor) >= (4,8) then shims_let_op_post_408 else shims_let_op_pre_408); write_file "CCShimsMkLet_.ml" (if (major, minor) >= (4,8) then shims_let_op_post_408 else shims_let_op_pre_408);
write_file "CCShimsInt_.ml" (if (major, minor) >= (4,8) then shims_int_post_408 else shims_int_pre_408); write_file "CCShimsInt_.ml"
((if (major, minor) >= (4,8) then shims_int_post_408 else shims_int_pre_408)
^ if Sys.word_size=32 then shims_int_32bit else shims_int_64bit);
) )

View file

@ -56,17 +56,17 @@ let with_cache_rec ?(cb=default_callback_) c f =
f' f'
(*$R (*$R
let c = unbounded ~eq:CCInt.equal 256 in let c = unbounded ~eq:Int64.equal 256 in
let fib = with_cache_rec c let fib = with_cache_rec c
(fun self n -> match n with (fun self n -> match n with
| 1 | 2 -> 1 | 1L | 2L -> 1L
| _ -> self (n-1) + self (n-2) | _ -> CCInt64.(self (n-1L) + self (n-2L))
) )
in in
assert_equal 55 (fib 10); assert_equal 55L (fib 10L);
assert_equal 832040 (fib 30); assert_equal 832040L (fib 30L);
assert_equal 12586269025 (fib 50); assert_equal 12586269025L (fib 50L);
assert_equal 190392490709135 (fib 70) assert_equal 190392490709135L (fib 70L)
*) *)
let size c = c.size () let size c = c.size ()