# JSOO/ReScript 'primes' benchmark

**URL:** <https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270>\
**Category:** Ecosystem\
**Created:** [February 13, 2021, 4:59pm UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270 "2021-02-13T16:59:57Z")\
**Posts on this page:** 15\
**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:** [February 13, 2021, 4:59pm UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/1 "2021-02-13T16:59:57Z")

</div>

Continuing the discussion from [A short history of ReScript (BuckleScript)](https://discuss.ocaml.org/t/a-short-history-of-rescript-bucklescript/7222/38): thought I’d start a new thread instead of continuing there since it was getting fairly OT.

> You have a nice fast machine. By way of comparison, how long does the native ocamlopt code take to solve the prime?

Thanks 🙂 It’s a little indulgence for trying out different tech stacks. The native and bytecode benchmarks:

```auto
$ ./primes.opt
Elapsed time: 0.142687
1299709
$ ./primes.byte
Elapsed time: 0.475783
1299709

```

> Loading it in the head seems to improve the chance of the JITer getting on the fast path, particularly if reloading the page.

Interesting. I made the change and get the following:

- Firefox: 0.105s (before: 0.117s, seems like a speedup)
- Chromium: 0.095s (before: 0.094s, doesn’t seem like a significant difference)

> if I tweak your rescript-generated primes.js file by omitting the imports of curry.js and caml\_int32.js and substituting standard javascript, the rescript version runs slightly faster than the JSOO version in chrome and slightly slower in firefox.

Yeah, the ‘curry’ stuff is because the compiler has to check whether the return value is a curried function or if it’s fully applied. There’s actually support for totally uncurried functions, with `[@bs]` annotation in OCaml syntax and dedicated ergonomic syntax in ReScript. I purposely didn’t use that though because I typically wouldn’t use it unless performance was actually a concern.

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [February 13, 2021, 6:47pm UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/2 "2021-02-13T18:47:29Z")

</div>

The consistent thing that emerges is that the emitted javascript of both rescript and JSOO is, with the tight loop optimizations of modern browser JIT implementations, faster than native ocaml for this workload. I don’t think that would often happen in more normal workloads but it is still quite impressive.

By contrast with my 0.29 secs for chrome and 0.26 secs for firefox, the same code rewritten in common lisp using sbcl (I think about the fastest lisp) scores at 1.05 seconds, in scheme chez-scheme and gambit (I think about the fastest schemes) come in at 1.13 seconds and 1.04 seconds respectively, and python3 is miserable at 5.7 seconds.

I guess this speaks to the quality of javascript’s current JITing engines.

**Edit** : I have become sufficiently intrigued that I have also just rewritten this for C++. This algorithm comes in on C++ at 0.21 seconds. So for this algorithm JSOO/rescript are nearly as fast as C++, and native ocaml is just under 2 and a half time slower, which isn’t bad at all.

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [February 13, 2021, 8:36pm UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/3 "2021-02-13T20:36:28Z")

</div>

Some more context would help people follow this conversation:

- Could one person list all relevant timings on their machine for the benchmark ine one place? Currently the timings are speared across the two threads.
- Could someone post a URL to a browsable version of the benchmark, not an archive uploaded somewhere? This would be useful for people who want to know what sort of code we are talking about, but not necessarily play with it locally.

---

<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:** [February 14, 2021, 12:01am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/4 "2021-02-14T00:01:00Z")

</div>

True, sorry 🙂 Let me provide some more context. First of all, this is a small ‘benchmark’ that @cvine and I are playing around with to mainly check how JavaScript engines handle the OCaml-to-JS compilers’ output JS.

I’ve tried preparing a JSBench benchmark, but the JSOO output JS size is actually too large to save there–I get an error when I try to save it.

However, I have tried to do a slightly more formal comparison and prepared a new repo: [GitHub - yawaramin/primes-ocaml-benchmarks: Benchmark of prime number calculation in OCaml compilers](https://github.com/yawaramin/primes-ocaml-benchmarks)

I believe this is a bit better in terms of methodology–it also includes variants of his original native, bytecode, and JSOO benchmarks–but I’m open to suggestions.

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [February 14, 2021, 12:49am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/5 "2021-02-14T00:49:17Z")

</div>

You are doing something wrong. JSOO and rescript are not over 600x slower than native ocaml for this algorithm. For this algorithm they are (I agree surprisingly) somewhat faster than native.

I really don’t think that this particular benchmark is worth the effort. It started as a matter of interest. Let’s not take it out of proportion.

---

<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:** [February 14, 2021, 1:00am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/6 "2021-02-14T01:00:29Z")

</div>

Yeah, I may well have messed up the measurement somehow. I agree, it’s not very meaningful by itself–micro-benchmarks like this are nearly always worse than actually measuring performance of a realistic application. The only meaningful result from here might be the bundle size benefit especially for slower mobile networks.

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [February 14, 2021, 1:07am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/7 "2021-02-14T01:07:13Z")

</div>

Your native and bytecode ocaml measurement is out by a factor of 1000. Sys.time evaluates to seconds.

---

<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:** [February 14, 2021, 1:17am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/8 "2021-02-14T01:17:09Z")

</div>

Ah, right you are. That was silly of me 🙂 Fixed now:

- Native: 1.57x
- Bytecode: 5.34x
- JSOO: 1.11x
- ReScript: 1x

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [February 14, 2021, 1:45am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/9 "2021-02-14T01:45:15Z")

</div>

That looks about right, and it’s interesting. As a general rule of thumb, in my opinion ocaml native is normally going to beat out JSOO and rescript reasonably comfortably, but I cannot provide data to back that up. But if your program is bound by tight loops, as in some numerical calculations such as this test case, the very high quality of today’s javascript implementations, and in particular their optimized JIT compilation of loops, means that rescript/JSOO may beat you out.

---

<div class="post-metadata">

**Author:** ![shonfeder](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/shonfeder/32/424_2.png) [@shonfeder](https://discuss.ocaml.org/u/shonfeder)\
**Post date:** [February 14, 2021, 2:43am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/10 "2021-02-14T02:43:27Z")

</div>

I’m no Jsoo expert, but I notice you are compiling with lwt packages and ppx packages, but doesn’t look like you’re using any of those in your Ocaml code. Are these necessary dependencies to compile the example?

---

<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:** [February 14, 2021, 3:05am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/11 "2021-02-14T03:05:04Z")

</div>

Ah, good point, that was from Chris’ original version of the code which was using the DOM onload event. I’ve removed it now, but it doesn’t really make a significant difference.

---

<div class="post-metadata">

**Author:** ![shonfeder](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/shonfeder/32/424_2.png) [@shonfeder](https://discuss.ocaml.org/u/shonfeder)\
**Post date:** [February 14, 2021, 3:06am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/12 "2021-02-14T03:06:25Z")

</div>

That makes sense! Iiic, the dead code elimination should have cleaned it up any how, but nice to simplify where possible any how 🙂

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [February 14, 2021, 7:53am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/13 "2021-02-14T07:53:26Z")

</div>

I would guess that the native-code performance (relative to Js) is harmed by this operation:

```ocaml
    let lim = truncate ((sqrt (float_of_int num)) +. 0.5) in

```

`sqrt` is not a primitive, it goes through a C call, whereas I would guess that Javascript engines optimize this better (especially as `truncate` and `float_of_int` are no-ops in Javascript land).

But you don’t need this to compute primes: instead of

```ocaml
    let lim = truncate ((sqrt (float_of_int num)) +. 0.5) in
    let rec lp curr =
      if curr > lim then

```

just use:

```ocaml
    let rec lp curr =
      if curr * curr > num then

```

My prediction would be that naive would look much better, relative to other benchmarks, with this new version. (I still expect Js engines to do pretty well as those are just arithmetic-computation loops.)

---

<div class="post-metadata">

**Author:** ![Kakadu](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/kakadu/32/1204_2.png) [@Kakadu](https://discuss.ocaml.org/u/Kakadu)\
**Post date:** [February 14, 2021, 10:46am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/14 "2021-02-14T10:46:24Z")

</div>

> [@gasche](#):
>
> I would guess that the native-code performance (relative to Js) is harmed by this operation

I got that your variant is 8% slower than the previous one: [fastware/primes/primes.ml at master · Kakadu/fastware · GitHub](https://github.com/Kakadu/fastware/blob/master/primes/primes.ml)

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [February 14, 2021, 10:48am UTC](https://discuss.ocaml.org/t/jsoo-rescript-primes-benchmark/7270/15 "2021-02-14T10:48:00Z")

</div>

It does look better, relatively, on my original code (I haven’t run yawaramin’s version). Of course, calculating the limit (the integer root) out of the loop amortizes the cost over the whole of the time in the loop. The alternative of squaring each tested number has to be carried out on each iteration of the loop. On my own code, squaring on each iteration instead of taking the root slows all of the results (by 19% for the JSOO version, 7% for ocaml bytecode and 1% for ocaml native) so there is a relative closing of the gap but the JSOO version is still ahead.
