* ocaml and int64
@ 2008-04-02 0:54 Ludovic Coquelle
2008-04-02 1:50 ` [Caml-list] " Jon Harrop
0 siblings, 1 reply; 3+ messages in thread
From: Ludovic Coquelle @ 2008-04-02 0:54 UTC (permalink / raw)
To: Caml
[-- Attachment #1: Type: text/plain, Size: 528 bytes --]
Hi,
I wrote a direct translation of a simple algo from F# to ocaml.
(details can be found here:
http://khigia.wordpress.com/2008/03/30/ocaml-vs-f-for-big-integer-surprising-performance-test/
)
The compile F# program (12s) is much faster than Ocaml (30s), probably
because the algo do integer arithmetic with Int64 module (thanks to David
for this info).
Have someone here face this kind of problem (optimizing a code doing
arithmetic on big integer)?
Any advice to improve the Ocaml code (without changing the algo)?
Thanks
[-- Attachment #2: Type: text/html, Size: 672 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [Caml-list] ocaml and int64
2008-04-02 0:54 ocaml and int64 Ludovic Coquelle
@ 2008-04-02 1:50 ` Jon Harrop
2008-04-02 9:43 ` Sylvain Le Gall
0 siblings, 1 reply; 3+ messages in thread
From: Jon Harrop @ 2008-04-02 1:50 UTC (permalink / raw)
To: caml-list
On Wednesday 02 April 2008 01:54:54 Ludovic Coquelle wrote:
> Hi,
>
> I wrote a direct translation of a simple algo from F# to ocaml.
> (details can be found here:
> http://khigia.wordpress.com/2008/03/30/ocaml-vs-f-for-big-integer-surprisin
>g-performance-test/ )
>
> The compile F# program (12s) is much faster than Ocaml (30s), probably
> because the algo do integer arithmetic with Int64 module (thanks to David
> for this info).
>
> Have someone here face this kind of problem (optimizing a code doing
> arithmetic on big integer)?
> Any advice to improve the Ocaml code (without changing the algo)?
Get a 64-bit machine. ;-)
There are some performance pitfalls in your code (which takes 21s on my
machine):
. OCaml makes no attempt to optimize integer div and mod so avoid these at all
costs. In this case, use bitwise ANDs and shifts.
. Int64 is boxed by default and your use of recursion is likely to worsen this
effect.
Optimizing for these, the following code takes only 4.6s, which is 4.6x faster
than your original:
let int = Int64.to_int
let int64 = Int64.of_int
let ( +: ) = Int64.add
let ( -: ) = Int64.sub
let ( *: ) = Int64.mul
let ( >>> ) = Int64.shift_right
let rec seq_length x n =
match x with
| 0L -> n +: 1L
| 1L -> seq_length 0L (n +: 1L)
| x when int x land 1 = 0 -> seq_length (x >>> 1) (n +: 1L)
| _ -> seq_length (3L *: x +: 1L) (n +: 1L)
let rec loop i imax n =
let n' = seq_length i 0L in
let imax, n = if n' > n then (i, n') else (imax, n) in
if i < 1000000L then loop (i +: 1L) imax n else imax
let _ = print_string (Int64.to_string (loop 1L 0L 0L))
Using 63-bit ints on a 64-bit machine, the time drops to only 2s:
let rec seq_length x n =
match x with
| 0 -> n + 1
| 1 -> seq_length 0 (n + 1)
| x when x land 1 = 0 -> seq_length (x lsr 1) (n + 1)
| _ -> seq_length (3*x + 1) (n + 1)
let rec loop i imax n =
let n' = seq_length i 0 in
let imax, n = if n' > n then (i, n') else (imax, n) in
if i < 1000000 then loop (i+1) imax n else imax
let _ = print_string (string_of_int (loop 1 0 0))
As you have allured to, algorithmic optimizations buy you even more. The
following implementation is several times faster again, taking only 0.6s to
complete:
let int = Int64.to_int
let int64 = Int64.of_int
let ( +: ) = Int64.add
let ( -: ) = Int64.sub
let ( *: ) = Int64.mul
let rec inside a n =
if n<=1L then 0 else
if a.(int n)>0 then a.(int n) else
let p =
if int n land 1 = 0 then inside a (Int64.shift_right n 1) else
let n = 3L *: n +: 1L in
if n < int64(Array.length a) then inside a n else outside a n in
a.(int n) <- 1 + p;
1 + p
and outside a n =
let n = if int n land 1 = 0 then Int64.shift_right n 1 else 3L *: n +: 1L in
1 + if n < int64(Array.length a) then inside a n else outside a n
let euler14 n =
let a = Array.create (n+1) 0 in
let longest_n = ref 0 and longest_len = ref 0 in
for n=1 to n do
let len = inside a (int64 n) in
if len > !longest_len then begin
longest_n := n;
longest_len := len
end
done;
!longest_n, !longest_len
let () =
let n, len = euler14 1000000 in
Printf.printf "%d: %d\n%!" n len
Converting this to use native ints on my 64-bit machine the time drops to only
0.2s, which is over 100x faster than your original!
However, this benchmark really plays to F#'s strengths and you will probably
never beat F# here. My best F# is 3x faster than anything I can write in
OCaml, not least because it is trivial to parallelize efficiently but also
because F# and .NET automate many relevant optimizations.
HTH.
--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: ocaml and int64
2008-04-02 1:50 ` [Caml-list] " Jon Harrop
@ 2008-04-02 9:43 ` Sylvain Le Gall
0 siblings, 0 replies; 3+ messages in thread
From: Sylvain Le Gall @ 2008-04-02 9:43 UTC (permalink / raw)
To: caml-list
On 02-04-2008, Jon Harrop <jon@ffconsultancy.com> wrote:
> On Wednesday 02 April 2008 01:54:54 Ludovic Coquelle wrote:
>> Hi,
>>
>> I wrote a direct translation of a simple algo from F# to ocaml.
>> (details can be found here:
>> http://khigia.wordpress.com/2008/03/30/ocaml-vs-f-for-big-integer-surprisin
>>g-performance-test/ )
>>
>> The compile F# program (12s) is much faster than Ocaml (30s), probably
>> because the algo do integer arithmetic with Int64 module (thanks to David
>> for this info).
>>
>> Have someone here face this kind of problem (optimizing a code doing
>> arithmetic on big integer)?
>> Any advice to improve the Ocaml code (without changing the algo)?
>
> Get a 64-bit machine. ;-)
>
> There are some performance pitfalls in your code (which takes 21s on my
> machine):
>
> . OCaml makes no attempt to optimize integer div and mod so avoid these at all
> costs. In this case, use bitwise ANDs and shifts.
>
> . Int64 is boxed by default and your use of recursion is likely to worsen this
> effect.
>
> Optimizing for these, the following code takes only 4.6s, which is 4.6x faster
> than your original:
Nice speed up... But without changing the algo, replace seq_length by:
let limit_int =
(max_int / 3) - 1
;;
let limit_int64 =
Int64.of_int max_int
;;
let rec seq_length_int x n =
match x with
| 0 ->
(n + 1)
| 1 ->
seq_length_int 0 (n + 1)
| x when x mod 2 = 0 ->
seq_length_int (x / 2) (n + 1)
| _ ->
if x < limit_int then
seq_length_int ((3 * x) + 1) (n + 1)
else
seq_length_int64 (Int64.of_int x) n
and seq_length_int64 x n =
match x with
| 0L ->
(n + 1)
| 1L ->
seq_length_int64 0L (n + 1)
| x when x %% 2L = 0L ->
let nx =
x // 2L
in
if Int64.compare nx limit_int64 < 0 then
seq_length_int (Int64.to_int nx) (n + 1)
else
seq_length_int64 nx (n + 1)
| _ ->
seq_length_int64 (Int64.succ (3L ** x)) (n + 1)
;;
let seq_length =
seq_length_int64
;;
Give you a 19x speed up on 32 bit machine. On 64 bit machine this should
lead to nearly native performance (i.e. beat F# more than you think).
The trick is very simple: switch to "int" as soon as you can, only use
boxed integer when you cannot do anything else (in my case the limit is
when we go from 64bit to 31bit integer, but on 64bit machine this will
be when you go from 64bit to 63bit integer i.e. you will always compute
using "int" which will be native perf).
You can explain the perf gain by doing some statistic on 3n + 1, n / 2,
to see how much time is spend under max_int... and explain the speed-up
;-)
Applying the ">>>" trick of Dr. Harrop doesn't really improve perf here
(even if it is a good idea).
Regards,
Sylvain Le Gall
ps: for the sake of writing better code, there is a way to write the
code in a more generic way, but i am too lazy
pps:
gildor@peta:~/tmp/ocaml-test/eul14$ time ./eul14
837799
real 1m11.870s
user 1m11.324s
sys 0m0.428s
(with seq_length_int/seq_length_int64)
gildor@peta:~/tmp/ocaml-test/eul14$ time ./eul14
837799
real 0m3.736s
user 0m3.676s
sys 0m0.052s
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2008-04-02 9:44 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-02 0:54 ocaml and int64 Ludovic Coquelle
2008-04-02 1:50 ` [Caml-list] " Jon Harrop
2008-04-02 9:43 ` Sylvain Le Gall
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox