*Synopsis:* Some, including Evan, maintain that Elm can be "faster than JavaScipt". While that may be true for some use cases including use of perhaps more efficient UI updates due to compartmentalized use of VirtualDOM, the actaul Javascript code generated by the compiler is not very efficient for many/most tight code cases. The reason is often unnecessary nested function calls that could easily be eliminated by making full use of the type information that the Elm compiler has.
Part I *An example;* The following tight loop doesn't really do anything, so should therefore compile into the very tightest of code (and I'm not expecting the Elm compiler to recognize that the result is actually known at compile time): range : Int range = 1000000000 testProg : Int -> Int testProg n = -- do some work let lmt = min (n + 100000000) range in let loop i = if i >= lmt then i else loop (i + 1) in loop n which compiles to the following JavaScript: var _user$project$Temp1482759649866537$range = 1000000000; var _user$project$Temp1482759649866537$testProg = function (n) { var lmt = A2(_elm_lang$core$Basics$min, n + 10000000000, _user$project$Temp1482759649866537$range); var loop = function (i) { loop: while (true) { if (_elm_lang$core$Native_Utils.cmp(i, lmt) > -1) { return i; } else { var _v0 = i + 1; i = _v0; continue loop; } } }; return loop(n); }; All right, the code looks fairly good, and we can see that for the inner `loop` function that the compiler used its new capability to do tail call optimization and turn it into a while loop. Also, one might expect that any decent JIT compiler such as Chrome V8 will use constant folding and get rigd of the `_v0 variable. However, the real limitation of this loop is the call to the `Native_Utils.cmp` function. Function calls are expensive at 10's of CPU clock cycles each! The pertinent JavaScript for `Native_Utils.cmp` is as follows: var LT = -1, EQ = 0, GT = 1; function cmp(x, y) { if (typeof x !== 'object') { return x === y ? EQ : x < y ? LT : GT; } ... Note that there are three native branches here (in addition to the enclosing one): one for the check to see it the arguments are objects (which of course they are not in the case of Int's as here or Float's as the compiler well knows), one to check if they are equal. and (given that generally they won't be equal most of the time) one to see which is greater. Now these conditions are not so bad by themselves as they are very predictable for modern CPU's branch prediction (i is almost always < lmt), so that will cost at most a few CPU cycles; However, the call to the function in the first place will cost 10's of CPU clock cycles! Given that the compiler already knows and strictly enforces that the arguments are both Int's or Float's (which are both just Numbers in JavaScript), there is no reason that it cannot directly output (i >= lmt) instead of the function call and make the whole inner loop take only a few CPU clock cycles (on a JavaScript engine such as Chrome V8). If the compiler were consistent in applying this specialization rule, there would be no need for the `Native_Utils.cmp` function to do the check if the arguments are objects, but for safety's sake and considering that one extra check in the case of objects is likely negligible compare to the object processing, it may as well be left in for its true best use case of comparing objects of the various kinds. *The Elm Compiler only deals with two primitive types: Int and Float (which are both actual Number/Float to JavaScript), which makes direct use of primitive operands very easy* *Part II* In a similar way, the definition of the Bitwise library to emulate Haskell's definition of the Data.Bits library was silly for only five Int functions, made even worse by a name collision with the Bool `xor` operator. Because these are library functions, there is at least one level of function call for every use. *Just as for the above function call for known primitive types (in this case only for Int), these functions should be added to the Basics library as operators with appropriate infix levels and with the names `&&&`, `|||`, `^^^` , `<<<`, and `>>>` just as for F# (which Elm emulates more and more), with the Elm compiler directly substituting the equivalent primitive JavaScript operators. The Bitwise library, which should never have existed, should be canned.* Note that although this will make bitwise operators much faster than currently, programmers should be aware that there are many operations taking place under the covers that may not make these operations as efficient as possible alternate means: under the covers the arguments are and'ed to produce 32-bit values, the operation is carried out, and then the result is sign extended to convert back to a Number/Float, although for a sequence of bitwise operations, the JavaScript engines may only do the conversions at the beginning and the end of the sequence. Also, using bits in place of Bools does save space but doesn't save as much as perhaps thought: 32 bits are stored in a 64-bit Float, which may not save much space as compared to Bool's, especially as some JavaScript engines by automatically do the specialization. In other words, potential time and space savings must be tested. But in any case,these operations may as well be as efficient as possible and this above changes should be made. -- 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 elm-discuss+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.