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.

Reply via email to