JavaScript - Are loops faster than discretely writing line-by-line?
Asked Answered
N

4

5

Ignoring all code cleanliness and readability, which script will finish quicker?

This:

for(var i = 0; i < 10; i++){
  --do that thing--
}

Or this:

--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--

Or are they the same, performance-wise?

Nimmons answered 29/6, 2016 at 22:55 Comment(5)
Or option 3: build up the required html in a variable and only update the DOM once at the end. Why do you ask? Are you having a performance problem with your page, or are you just curious?Mohave
can you remove document.write and replace it with e. g. a placeholder to make this question more generic about 'loop unrolling' or similar? Otherwise most people will probably focus on the document.write aspect / downvote.Poi
just directly count = 9; will be quickest (:Politicking
@Mohave The work the scripts are doing is irrelevant; I updated the question to be more generic.Nimmons
It’s not irrelevant. JS engines will optimize loops like these, they won’t optimize repetitive statements like these. In general: loops are optimized. Use the thing that you would normally use (the loop, built-in methods, etc.), because they have the highest probability of being optimized.Superordinate
P
12

"Unrolling" a loop by repeatedly "copy & pasting" the loop body can improve or reduce performance.

The outcome depends on...

  • ...your JavaScript engine
  • ...the code in the loop body
  • ...how well documented your code is (no kidding!)

Let's analyze the performance using Google's popular V8 JavaScript engine (Chrome, Node):

Ordinary loop:

var count = 0;
for (var i = 0; i < 10; i++) {
  count += 1;
}

Unrolled loop:

var count = 0;
count += 1;
count += 1;
... 
count += 1;

Result: The unrolled loop is about 10x faster.

But what if we loop 1000 times instead of 10 times? Then the unrolled loop suddenly becomes more than 10x slower than the ordinary loop!

And what if we swap the simple arithmetic expression with a function call?

Ordinary loop:

function f() {
  return 1;
}

var count = 0;
for (var i = 0; i < 10; i++) {
  count += f();
}

Unrolled loop:

var count = 0;
count += f();
count += f();
... 
count += f();

Result: The unrolled loop is about 50% faster.

But what if we add a comment to our function f?

function f() {
  // bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
  return 1;
}

Suddenly the unrolled loop is about 20% slower!

Why is that?

  • V8 automatically inlines code when parsing functions whose body contains less than 600 characters, including whitespace and comments.
  • V8 performs on-stack-replacement when a function is 'stuck' in a very long loop.

Regarding the last example: By adding a long > 600 character comment, we prevent V8 from inlining the function f during parsing and rely on the run-time optimization capabilities (such as 'on stack replacement') which target whole functions and loops but not manually repeated code.

As you can see, it is quite hard to predict the outcome of such micro-"optimization". So better don't do it - unless you target a specific version of a specific JS engine.

See https://jsfiddle.net/Lj9v7c2m/ for the performance analysis - you might get different results depending on your computer / browser / version.

Poi answered 30/6, 2016 at 0:54 Comment(0)
I
2

One factor this depends on is which JS engine you're using. Loop unrolling is a common optimization technique that can employed without your intervention, so it really depends on which engine your code is being interpreted by.

Typically you can use http://jsperf.com/ to test specific code for performance differences in different browsers, but it seems to have been down for several days for maintenance. Instead you can manually place each selection of code in their own functions, store the time at the beginning of the test, call the function 10,000 times or something, then log the time elapsed. Here's an example:

    function testEmpty(){ }
    function testLoop(){
        for(var i = 0; i < 10; i++){void i;}
    } function testUnrolled(){
        void 0;void 1;void 2;void 3;void 4;void 5;void 6;void 7;void 8;void 9;
    }
    function testLoop2(){
        for(var i = 0; i < 10; i++){void Date();}
    } function testUnrolled2(){
        void Date();void Date();void Date();void Date();void Date();
        void Date();void Date();void Date();void Date();void Date();
    }
    function testFunction(func, num){
        var a=Date.now();
        for(var i=0;i<num;i++){
            func();
        }
        return Date.now() - a;
    }
    console.log(testFunction(testEmpty,10000000)); // Took 9ms
    console.log(testFunction(testLoop,10000000)); // Took 112ms
    console.log(testFunction(testUnrolled,10000000)); // Took 42ms
    console.log(testFunction(testLoop2,10000)); // Took 705ms
    console.log(testFunction(testUnrolled2,10000)); // Took 714ms

Keeping the browser constant (Firefox 47), this actually shows that performance changes are not tied to one coding style. As a matter of fact, depending on what is being repeated makes a difference.

Overall, incrementing a loop to 10 will have a completely unnoticeable performance impact so usually the difference is negligible (and performance probably shouldn't be a consideration for which style to use).

Inquest answered 30/6, 2016 at 0:26 Comment(4)
There is nothing that prevents testUnrolled to be optimised to nothing. So 42ms can be easily a time to run an empty function that many times.Isothere
@Isothere Thanks for thinking of that possibility, I added a baseline test for a function that does nothing and it shows there is a difference.Inquest
Not convincing: an engine may (and will) optimise it during run time, as a hot function. So "empty from the beginning" still != "emptied during optimisation"Isothere
@Isothere Still addressing your original concern, on my browser I checked and saw a huge performance difference between running a function with a single void 0; call and running a function with 10 void 0; calls. If the two functions where "optimised to nothing," the would take approximately the same amount of time to complete. So your concern doesn't apply for the browser I tested at least.Inquest
P
1

Second version is quicker if it's just simple calculation statement.

But why to do code so ugly and unreadable (it depends on statements per line) when You can just loop it.

In another hand code will be optimized and You will not feel the difference.

Also keep in mind that it's JS. So in some cases statements can be asynchronous that run without sequence (without waiting for ending of previous statement).

Programmer is not code looping machine, programmer must write once and enjoy automatization.

Politicking answered 29/6, 2016 at 23:11 Comment(4)
"Second version is quicker." --- how about extra job that an engine need to perform to parse longer code?Isothere
everything is dependent on what's written. it can be concatenation of strings that will be threaded as function call or used by eval. so I agree with Your answer that question has no valid answer.Politicking
"So in some cases statements can be asynchronous that run without sequence (without waiting for ending of previous statement)" - Unless you count web workers, which in normal circumstances you wouldn't, Javascript is single-threaded. It will not run a statement without waiting for the end of a previous statement. It might queue a function to be run later, asynchronously, but that doesn't happen without the programmer knowing about it. In any case, the OP's example is synchronous.Mohave
@Mohave technically an implementation can run code in multiple threads as soon as all observable side effects comply with the standard.Isothere
I
1

which script will finish quicker

There is no the only valid answer to that.

The other answers speculate with assumptions.

The standard does not guarantee any of those to be faster/slower, so one can only state something for a given ES implementation running on a given machine and only after they measure it. Beware though, that getting any sensible numbers after microbenchmarking is hard (most benchmarks produce some numbers and not necessary those numbers mean anything).

Isothere answered 29/6, 2016 at 23:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.