In cases where all of the arguments to a function are provided (like here, e.g. let foo = ref @@ expressions... ), shouldn’t composition operators simply vanish?
They do simply vanish – but they vanish after primitives have been translated. Which means the ref primitive is translated as if it were partially applied leaving you with the equivalent of:
(fun x -> ref x) initial_value
which is too complex for the simple ref optimisation to understand. This unnecessary application will be cleared up by inlining – but the ref optimisation is done before inlining so it’s already too late, and we don’t actually do inlining for bytecode anyway.
I suspect you are hitting a combination of the bytecode issue and another issue: flambda’s representation of “toplevel” computations (i.e. computations not inside of a function) doesn’t actually allow for the ref optimisation to happen – essentially another optimisation gets in the way. This issue is discussed here.
For this reason we re-enabled the old ref optimisation in flambda mode as well. That means that for “toplevel” code using ref you have the same restrictions as in bytecode. If you move the code inside a function body in the benchmark then it should fix things.
In practise this issue doesn’t really matter since the performance of “toplevel” code is rarely important, with one notable exception: micro-benchmarks. So we have here the opposite of the usual problem of compilers being over-tuned for micro-benchmarks
It seems like I can’t get the code to avoid float allocation without changing it to something like:
module Rectangle = struct
type t = {
lx: float;
by: float;
rx: float;
ty: float
}
let r lx by rx ty = {lx; by; rx; ty}
let min (a: float) (b: float) : float = if a < b then a else b
let max (a: float) (b: float) : float = if a > b then a else b
let union alx aby arx aty blx bby brx bty =
alx := min !alx blx;
aby := min !aby bby;
arx := max !arx brx;
aty := max !aty bty
[@@inline]
end;;
open Rectangle
let bench lim =
let lx = ref 25. in
let by = ref 25. in
let rx = ref 200. in
let ty = ref 200. in
for i = 0 to lim do
union lx by rx ty 20. 0. 100. (float_of_int i)
done;
print_endline (string_of_float !ty)
let () = bench 1000000000
Which is a good demonstration of how the unboxing optimisations need to be improved for code like this. Looking at the assembly for that code I suspect you need some loop vectorization to go noticeably faster than what that gives you – which the gcc and llvm based languages probably have access to.
After putting the main body of the benchmark in a function, the results for ocamlopt dropped by almost half. Salut!
I have so many questions, but I’ll suppress them until after I’ve perused the ocaml source some.
It’s fair enough to point out the micro aspect of this particular benchmark, but I greatly appreciate everyone’s help in identifying where it went astray. I’ve been hunting for a better language for a particular project, and so it’s sort of unavoidable that the common program walked around to the various options be a trivial shadow of the real work to be done.
I gathered that BuckleScript became ReScript and no longer supports native OCaml syntax. So it seems a little more difficult to compare against Js_of_OCaml these days. You can’t really run the same code in both–is this not correct?
BuckleScript can still be written with OCaml syntax. Reason is a compiler frontend and BuckleScript is a backend. They can be used entirely independently. Reason can compile to native code with ocamlopt and BuckleScript compiles (mostly) normal OCaml to JS.
There’s no, uh “reason” to use Reason if you already know OCaml, except that the syntax for JS interop stuff is slightly nicer.
This is good to hear (since I already know OCaml syntax and have some amount of existing OCaml code that I’ve been compiling to jS). But when I look for BuckleScript online I find only ReScript, and there is no mention whatsoever of OCaml (that I saw anyway). If you can give a URL of a page that shows how to use OCaml syntax with the newest BuckleScript (or ReScript?), that would be very useful. But I will also try to work it out for myself.
I have to admit that all the emphasis on branding and syntax tweaking make me want to avoid this project entirely. That’s not to say that syntax isn’t important, but it’s not the most important thing, and OCaml syntax is better anyway.
It seems like the things this community cares about are not the things I care about.
This is a gift horse situation–BuckleScript/ReScript is really nice for coding webapps in OCaml (in my experience). So it’s hard to complain. But I have to agree that I’m completely happy with OCaml syntax (and prefer it to any JS-like syntax), and I don’t really care about compile speeds particularly.
Reason is a compiler frontend and BuckleScript is a backend.
Minor correction: BuckleScript is the whole compiler.
There’s no, uh “reason” to use Reason if you already know OCaml, except that the syntax for JS interop stuff is slightly nicer.
We want to support the OCaml syntax and another tuned syntax for JS developers, reason used to be that ideal candidate, however, it does not end up this way, actually the reason team care more for native based on the communication, that’s why we want to replace the reason syntax with our own dedicated syntax which caused a whole mess of communication.
Thanks, this is a very helpful clarification of your goals. And thanks for BuckleScript/ReScript. It really is excellent for building web apps in OCaml, in my (humble) experience.
It is. But unlike jsoo which leaves the compiler untouched, we vendored the compiler and added lots of work in all phases to make sure we can generate better JS.
This is benchmarking thread, sorry that my discussion is a bit off-topic.