140 lines
4.8 KiB
OCaml
140 lines
4.8 KiB
OCaml
let exit_success = 0
|
|
let exit_failure = 1
|
|
|
|
(* First computation wants to count operations needed by
|
|
- one_if_not_zero
|
|
- zero_if_not_zero
|
|
- select_int
|
|
|
|
For each /assembly instructions/, we update a counter. This way is not
|
|
totally true. Even if we check by hands that bitwise operations don't
|
|
emit branches, this is our only assumption! *)
|
|
|
|
let operation = ref 0
|
|
|
|
let logical_shift_right a b = incr operation ; a lsr b
|
|
let logical_or a b = incr operation ; a lor b
|
|
let shift_right a b = incr operation ; a asr b
|
|
let logical_and a b = incr operation ; a land b
|
|
let logical_not a = incr operation ; lnot a
|
|
let minus a = incr operation ; (- a)
|
|
let sub a b = incr operation ; a - b
|
|
|
|
let[@inline always] minus_one_or_less n =
|
|
logical_shift_right n (sub Sys.int_size 1)
|
|
|
|
let[@inline always] one_if_not_zero n = minus_one_or_less (logical_or (minus n) n)
|
|
let[@inline always] zero_if_not_zero n = sub (one_if_not_zero n) 1
|
|
let[@inline always] select_int choose_b a b =
|
|
let mask = shift_right (logical_or (minus choose_b) choose_b) Sys.int_size in
|
|
logical_or (logical_and a (logical_not mask)) (logical_and b mask)
|
|
|
|
let one_if_not_zero_ops =
|
|
let _ = one_if_not_zero 0x7eadbeef in
|
|
Format.printf "[one_if_not_zero]: %d operation(s).\n%!" !operation ;
|
|
!operation
|
|
|
|
let () = operation := 0
|
|
|
|
let zero_if_not_zero_ops =
|
|
let _ = zero_if_not_zero 0x7eadbeef in
|
|
Format.printf "[zero_if_not_zero]: %d operation(s).\n%!" !operation ;
|
|
!operation
|
|
|
|
let () = operation := 0
|
|
|
|
let select_int_ops =
|
|
let _ = select_int 0 1 2 in
|
|
Format.printf "[select_int]: %d operation(s).\n%!" !operation ;
|
|
!operation
|
|
|
|
let eqaf_sleep () = Unix.sleep 1
|
|
|
|
let logical_shift_right a b = eqaf_sleep () ; a lsr b
|
|
let logical_or a b = eqaf_sleep () ; a lor b
|
|
let shift_right a b = eqaf_sleep () ; a asr b
|
|
let logical_and a b = eqaf_sleep () ; a land b
|
|
let logical_not a = eqaf_sleep () ; lnot a
|
|
let minus a = eqaf_sleep () ; (- a)
|
|
let sub a b = eqaf_sleep () ; a - b
|
|
|
|
let[@inline always] minus_one_or_less n =
|
|
logical_shift_right n (sub Sys.int_size 1)
|
|
|
|
let[@inline always] one_if_not_zero n = minus_one_or_less (logical_or (minus n) n)
|
|
let[@inline always] zero_if_not_zero n = sub (one_if_not_zero n) 1
|
|
let[@inline always] select_int choose_b a b =
|
|
let mask = shift_right (logical_or (minus choose_b) choose_b) Sys.int_size in
|
|
logical_or (logical_and a (logical_not mask)) (logical_and b mask)
|
|
|
|
(* Finally, we count how many time we spend when we call our
|
|
functions. [eqaf_sleep] spends 1 second, so our bitwise operators
|
|
should spend 1 second + some nanosecond. At the end, execution of
|
|
them should be closely equal to our operation counter where:
|
|
|
|
1 operation ~= 1 second
|
|
|
|
To be able to count time, we use [caml_time] which is available only
|
|
on Linux and for a native compilation (see [@unboxed]). Because bitwise
|
|
operation spend at least 1 second, we finally [floor] our results to
|
|
delete noise.
|
|
|
|
NOTE: [check/check] does a linear regression to delete noise and really
|
|
get how long is our functions. We think that for our functions:
|
|
- zero_if_not_zero
|
|
- one_if_not_zero
|
|
- select_int
|
|
[check/check] is too huge. *)
|
|
|
|
let time () = Clock.now ()
|
|
|
|
let fdiv a b = a /. b
|
|
|
|
let () =
|
|
let t0 = time () in
|
|
let _ = one_if_not_zero 0x7eadbeef in
|
|
let t1 = time () in
|
|
let v0 = Int64.(floor (fdiv (to_float (sub t1 t0)) 1000000000.)) in
|
|
Format.printf "[one_if_not_zero 0x7eadbeef]: %fs.\n%!" v0 ;
|
|
let t0 = time () in
|
|
let _ = one_if_not_zero 0x0 in
|
|
let t1 = time () in
|
|
let v1 = Int64.(floor (fdiv (to_float (sub t1 t0)) 1000000000.)) in
|
|
Format.printf "[one_if_not_zero 0x0]: %fs.\n%!" v0 ;
|
|
if v0 = v1
|
|
&& int_of_float v0 = one_if_not_zero_ops
|
|
&& int_of_float v1 = one_if_not_zero_ops
|
|
then () else exit exit_failure
|
|
|
|
let () =
|
|
let t0 = time () in
|
|
let _ = zero_if_not_zero 0x7eadbeef in
|
|
let t1 = time () in
|
|
let v0 = Int64.(floor (fdiv (to_float (sub t1 t0)) 1000000000.)) in
|
|
Format.printf "[zero_if_not_zero 0x7eadbeef]: %fs.\n%!" v0 ;
|
|
let t0 = time () in
|
|
let _ = zero_if_not_zero 0x0 in
|
|
let t1 = time () in
|
|
let v1 = Int64.(floor (fdiv (to_float (sub t1 t0)) 1000000000.)) in
|
|
Format.printf "[zero_if_not_zero 0x0]: %fs.\n%!" v0 ;
|
|
if v0 = v1
|
|
&& int_of_float v0 = zero_if_not_zero_ops
|
|
&& int_of_float v1 = zero_if_not_zero_ops
|
|
then () else exit exit_failure
|
|
|
|
let () =
|
|
let t0 = time () in
|
|
let _ = select_int 0 1 2 in
|
|
let t1 = time () in
|
|
let v0 = Int64.(floor (fdiv (to_float (sub t1 t0)) 1000000000.)) in
|
|
Format.printf "[select_int 0 1 2]: %fs.\n%!" v0 ;
|
|
let t0 = time () in
|
|
let _ = select_int 2 1 0 in
|
|
let t1 = time () in
|
|
let v1 = Int64.(floor (fdiv (to_float (sub t1 t0)) 1000000000.)) in
|
|
Format.printf "[select_int 2 1 0]: %fs.\n%!" v1 ;
|
|
if v0 = v1
|
|
&& int_of_float v0 = select_int_ops
|
|
&& int_of_float v1 = select_int_ops
|
|
then () else exit exit_failure
|
|
;;
|