caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Jon Harrop <jon@ffconsultancy.com>
To: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] ocaml and int64
Date: Wed, 2 Apr 2008 02:50:24 +0100	[thread overview]
Message-ID: <200804020250.25091.jon@ffconsultancy.com> (raw)
In-Reply-To: <d6c7b34d0804011754k34acaecfp7de0f27769b829b@mail.gmail.com>

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


  reply	other threads:[~2008-04-02  1:59 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-04-02  0:54 Ludovic Coquelle
2008-04-02  1:50 ` Jon Harrop [this message]
2008-04-02  9:43   ` Sylvain Le Gall

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200804020250.25091.jon@ffconsultancy.com \
    --to=jon@ffconsultancy.com \
    --cc=caml-list@yquem.inria.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).