Hi Bob/Hongbo,
I've run into my first speed problem with BuckleScript: When it decides it
needs to form a closure of captured free binding(s), it creates a "function
returning a function" with the outer function's arguments being the current
state of the "pure" captured bindings, and the inner returned function the
actual closure code. When this happens anywhere near an inner loop, the
code gets very slow, although sometimes the compiler is smart enough to
infer that the closure is not necessary is when the binding value is an
argument of an enclosing function; however, this does not happen for local
`let` bindings even when they are in the same scope as the callee/caller
sites.
For example, the following bit-packed Sieve of Eratosthenes sieves over a
range and returns the bit packed array of found primes over that range
(odds-only. It runs about five times slower than using imperative code
for/while loops (top of a quarter million). Code is as follows:
let soe_loop top =
if top < 3 then Array.make 0 0 else
let ndxlmt = (top - 3) lsr 1 in
let cmpsts = Array.make ((ndxlmt lsr 5) + 1) 0 in
for loop = 1 to 1000 do (* do it many times for timing *)
let pir = ref 0 in
while !pir <= ndxlmt do
let pi = !pir in
let p = pi + pi + 3 in
let rec nxtc ci =
if ci > ndxlmt then () else
let w = ci lsr 5 in
cmpsts.(w) <- cmpsts.(w) lor (1 lsl (ci land 31));
nxtc (ci + p) in
let si = (p * p - 3) lsr 1 in
if si > ndxlmt then pir := ndxlmt + 1 else (
if cmpsts.(pi lsr 5) land (1 lsl (pi land 31)) == 0 then
nxtc si
cmpsts
where the outer `pi` loops are imperative and the inner `nxtc` loop is
functional; this latter becomes the closure that captures the `p` value as
you can see in the following generated JavaScript:
function soe_loop(top) {
if (top < 3) {
return Caml_array.caml_make_vect(0, 0);
}
else {
var ndxlmt = ((top - 3 | 0) >>> 1);
var cmpsts = Caml_array.caml_make_vect((ndxlmt >>> 5) + 1 | 0, 0);
for(var loop = 1; loop <= 1000; ++loop){
var pir = 0;
while(pir <= ndxlmt) {
var pi = pir;
var p = (pi + pi | 0) + 3 | 0;
var nxtc = (function(p){
return function nxtc(_ci) {
while(true) {
var ci = _ci;
if (ci > ndxlmt) {
return /* () */0;
}
else {
var w = (ci >>> 5);
cmpsts[w] = cmpsts[w] | (1 << (ci & 31));
_ci = ci + p | 0;
continue ;
}
};
}
}(p));
var si = ((Caml_int32.imul(p, p) - 3 | 0) >>> 1);
if (si > ndxlmt) {
pir = ndxlmt + 1 | 0;
}
else {
if (!(cmpsts[(pi >>> 5)] & (1 << (pi & 31)))) {
nxtc(si);
}
pir = pi + 1 | 0;
}
};
}
return cmpsts;
}
}
As the compiler knows that the `p` value is pure, it should know that it
can reference it without danger of it being in an unknown state, so there
is no need for a cloaure, and if `nxtc` is not a closure then it can be
inlined as usual.
The same thing happens currently when all loops are functional.
The back up work around is to use imperative code, but that's ugly. I
wonder if you have any suggestions to avoid the closure?
Regards, Gordon
>
>
--
You received this message because you are subscribed to the Google Groups "Elm
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.