Comment by jacobolus

11 years ago

You should then go dive into the passAll function:

https://github.com/tjmehta/101/blob/master/pass-all.js

It’s written in such a way that every time you call...

    passAll(f1, f2, ..., fn)(args..)

... there are something like 5 + 2n attribute accesses, 5 + 3n function calls, 3 + n new functions created, as well as some packing and unpacking of arguments, not including the actual application of the functions to the arguments that we care about. That’s in addition to the several functions defined in the dependent submodules, which you only have to pay for constructing once.

[From my quick eyeball count. These numbers could be a bit off.]

I'm more disappointed that it doesn't short-circuit the operation at all. It applies all the functions, THEN it determines whether all of them passed. Even worse, it uses `every` (which does short-circuit) to determine that all the functions are, indeed, functions, but apparently the ability to use that function to determine whether every predicate passes was missed.

  • It could have been a simple for loop with a break whenever one evaluates false. Blazing fast in every javascript engine, and easy to see all the code in one place.

    Instead, it’s a slow tower of bloat, which you need to read 5 files to reason about.

    Javascript implementation:

        function passAll() {
          var fns = [].slice.call(arguments);
          return function() {
            for (var i = 0, len = fns.length; i < len; i++) {
              if (!fns[i].apply(null, arguments)) { return false; }
            }
            return true;
          };
        }
    

    Or Coffeescript (including the function check):

        passAll = (fns...) ->
            for fn in fns then if typeof fn isnt 'function'
                throw new TypeError 'all funcs should be functions'
            (args...) ->
                (return false if not fn args...) for fn in fns
                true

Lol, nice, I wrote "pass-any" first. I then copied the code and replaced "or" w/ "and" to create "pass-all".

I will probably have go back and change this now that I know about it. In general though, not gonna a lie, I am not very concerned about micro performance optimizations.

  • If it were being used only in places in the code where it was rarely called, it would be fine to create a handful of extra functions, make a few dozen function calls, etc. In my opinion, anything that aspires to be a library of basic infrastructure type code should try to have efficient and simple code at the expense of being slightly syntactically longer than the highly abstracted version.

    Because if people start using it for such things as an "is positive integer" check (Which, to be fair, they should not be doing. Nobody should be constructing 2 new javascript functions every time they want to check if an object is a positive integer. But apparently they are...), then it could easily make its way into somebody’s inner loops.

    The end result of this general culture (not picking on anyone in particular) is that trivial Javascript programs end up having their performance tank, because every tiny bit of useful real work gets weighed down by two orders of magnitude of incidental busywork creating and tearing down abstractions built on towers of other abstractions. Because CPUs aren’t very well optimized for this kind of workload, which involves lots of branching and cache misses, it’s worse still, probably more like 4–5 orders of magnitude slower than optimized code written in a low-level language.

    Ultimately every idle facebook page and gmail page and news article in some background browser tab ends up sucking up noticeable amounts of my laptop’s CPU time and battery, even when they’re doing nothing at all for me as the user.