# OCaml speed comparison - calculating pi with Leibniz - optimize?

**URL:** <https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112>\
**Category:** Learning\
**Created:** [May 6, 2023, 6:55pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112 "2023-05-06T18:55:10Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [May 6, 2023, 6:55pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/1 "2023-05-06T18:55:10Z")

</div>

I have been nerd-sniped by this speed comparison repo: [GitHub - niklas-heer/speed-comparison: A repo which compares the speed of different programming languages.](https://github.com/niklas-heer/speed-comparison)

And implemented the following OCaml version of Leibniz’ algorithm to calculate `pi` (well, not so much implemented as ported over the Python version):

```auto
(* ocamlopt -O2 -o leibniz leibniz.ml && strip leibniz *)

let rec calc sum curr upto =
  if curr >= upto then sum
  else calc (sum +. 1. /. float_of_int curr) (curr + 4) upto

let calc rounds =
  let double = rounds * 2 in
  calc 0. (1 - double) (double + 1)

let () =
  let rounds_txt = open_in_bin "rounds.txt" in
  let rounds = int_of_string (input_line rounds_txt) in
  close_in rounds_txt;

  let pi = 4. *. calc rounds in
  Printf.printf "%.16f\n" pi

```

When I run this with `time ./leibniz` I consistently get:

```auto
$ time ./leibniz
3.1415926435899633
./leibniz 0.38s user 0.00s system 99% cpu 0.388 total

```

Am I missing anything obvious? The [Go version](https://github.com/niklas-heer/speed-comparison/blob/master/src/leibniz.go) runs in a fraction of that time:

```auto
time ./leibniz_go
3.141592663589326
./leibniz_go 0.13s user 0.01s system 95% cpu 0.142 total

```

---

<div class="post-metadata">

**Author:** ![copy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/copy/32/480_2.png) [@copy](https://discuss.ocaml.org/u/copy)\
**Post date:** [May 6, 2023, 7:31pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/2 "2023-05-06T19:31:58Z")

</div>

Judging from the [generated assembly](https://godbolt.org/z/xWrTxa3ov), the OCaml compiler is not able to unbox the float (for the return value) across the recursive call. You can see the `subq $16, %r15 cmpq (%r14), %r15` allocation sequence, as well as memory writes inside of the loop of `camlExample__calc_5`.

An [imperative version](https://godbolt.org/z/sW46nh1GK) does quite a bit better (1.235 vs 2.273 on my machine).

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [May 6, 2023, 7:32pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/3 "2023-05-06T19:32:02Z")

</div>

Probably the old boxing-of-floats issue.  
EDIT: sniped

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [May 6, 2023, 7:33pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/4 "2023-05-06T19:33:42Z")

</div>

To make numeric code go faster, I suggest avoiding recursive calls (which inhibit float unboxing) and sticking to good old while loops, ie something like:

```auto
let calc rounds =
  let sum = ref 0. in
  let double = 2 * rounds in
  let curr = ref (1 - double) in
  let upto = double + 1 in
  while !curr < upto do
    sum := !sum +. 1. /. float_of_int !curr;
    curr := !curr + 4
  done;
  !sum

```

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [May 6, 2023, 7:54pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/5 "2023-05-06T19:54:34Z")

</div>

So now that we’ve brought this issue up again, is there any work being done to fix it? Will Flambda2 deal with it?

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [May 6, 2023, 7:57pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/6 "2023-05-06T19:57:26Z")

</div>

Thanks all! That makes sense.

---

<div class="post-metadata">

**Author:** ![rbjorklin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/rbjorklin/32/1126_2.png) [@rbjorklin](https://discuss.ocaml.org/u/rbjorklin)\
**Post date:** [May 6, 2023, 9:36pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/7 "2023-05-06T21:36:11Z")

</div>

Moving `float_of_int` outside of the loop retires 200M fewer instructions according to `perf stat` but doesn’t actually speed things up on my end.

---

<div class="post-metadata">

**Author:** ![vlaviron](https://avatars.discourse-cdn.com/v4/letter/v/ec9cab/32.png) [@vlaviron](https://discuss.ocaml.org/u/vlaviron)\
**Post date:** [May 6, 2023, 10:00pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/8 "2023-05-06T22:00:49Z")

</div>

> [@bluddy](#):
>
> is there any work being done to fix it? Will Flambda2 deal with it?

Yes, and in some cases yes.  
The best way to fix it is to use Jane Street’s unboxed types (proposed as an RFC a while ago, with significant parts already implemented). Without that, Flambda 2 can still unbox the float values in the above example, but it’s based on heuristics so there’s no guarantee that it will keep working if small changes are made. Typically, if the function wasn’t tail-recursive it wouldn’t work.

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [May 6, 2023, 11:03pm UTC](https://discuss.ocaml.org/t/ocaml-speed-comparison-calculating-pi-with-leibniz-optimize/12112/9 "2023-05-06T23:03:32Z")

</div>

Just to update here that with the recommended imperative approach the time on my laptop now matches the Go time, 0.13s. Just for fun, I’ll try submitting a PR to that repo.
