For loop slower than reduce?












1















I'm getting very strange timings from this code. At times the for loop runs much more slowly.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = 0
for(let i = 0; i < len; i++)
sum += arr[i]
console.log(sum)
console.timeEnd('loop')












share|improve this question

























  • I consistently get the loop performing about 10x faster on Node v10.5.0

    – juanpa.arrivillaga
    Nov 21 '18 at 17:12











  • I edited: I'm in the browser running Chrome 70

    – Truman Purnell
    Nov 21 '18 at 17:16






  • 1





    I figured, just thought it might be of interest. I'm seeing the opposite in the browser as well, Chrome too. Version 70.0.3538.102 (Official Build) (64-bit)

    – juanpa.arrivillaga
    Nov 21 '18 at 17:16








  • 1





    @tommyO except that answer makes the opposite conclusion that this benchmark is demonstrating.

    – Patrick Roberts
    Nov 21 '18 at 17:21






  • 1





    When I was modifying the benchmark to see what might be affecting performance, I noticed that isolating the scope of sum for each test at least makes the results more consistent. This benchmark results in reduce performing 3-10x faster in the first run on Chrome 70, and then up to 60x faster on sequential runs, whereas if you put them into separate block scopes and use let or const, it consistently performs about 10x faster on all runs for me.

    – Patrick Roberts
    Nov 21 '18 at 17:25
















1















I'm getting very strange timings from this code. At times the for loop runs much more slowly.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = 0
for(let i = 0; i < len; i++)
sum += arr[i]
console.log(sum)
console.timeEnd('loop')












share|improve this question

























  • I consistently get the loop performing about 10x faster on Node v10.5.0

    – juanpa.arrivillaga
    Nov 21 '18 at 17:12











  • I edited: I'm in the browser running Chrome 70

    – Truman Purnell
    Nov 21 '18 at 17:16






  • 1





    I figured, just thought it might be of interest. I'm seeing the opposite in the browser as well, Chrome too. Version 70.0.3538.102 (Official Build) (64-bit)

    – juanpa.arrivillaga
    Nov 21 '18 at 17:16








  • 1





    @tommyO except that answer makes the opposite conclusion that this benchmark is demonstrating.

    – Patrick Roberts
    Nov 21 '18 at 17:21






  • 1





    When I was modifying the benchmark to see what might be affecting performance, I noticed that isolating the scope of sum for each test at least makes the results more consistent. This benchmark results in reduce performing 3-10x faster in the first run on Chrome 70, and then up to 60x faster on sequential runs, whereas if you put them into separate block scopes and use let or const, it consistently performs about 10x faster on all runs for me.

    – Patrick Roberts
    Nov 21 '18 at 17:25














1












1








1








I'm getting very strange timings from this code. At times the for loop runs much more slowly.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = 0
for(let i = 0; i < len; i++)
sum += arr[i]
console.log(sum)
console.timeEnd('loop')












share|improve this question
















I'm getting very strange timings from this code. At times the for loop runs much more slowly.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = 0
for(let i = 0; i < len; i++)
sum += arr[i]
console.log(sum)
console.timeEnd('loop')








var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = 0
for(let i = 0; i < len; i++)
sum += arr[i]
console.log(sum)
console.timeEnd('loop')





var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = 0
for(let i = 0; i < len; i++)
sum += arr[i]
console.log(sum)
console.timeEnd('loop')






javascript performance reduce code-timing






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 '18 at 17:15









Keith

8,3801720




8,3801720










asked Nov 21 '18 at 17:08









Truman PurnellTruman Purnell

326




326













  • I consistently get the loop performing about 10x faster on Node v10.5.0

    – juanpa.arrivillaga
    Nov 21 '18 at 17:12











  • I edited: I'm in the browser running Chrome 70

    – Truman Purnell
    Nov 21 '18 at 17:16






  • 1





    I figured, just thought it might be of interest. I'm seeing the opposite in the browser as well, Chrome too. Version 70.0.3538.102 (Official Build) (64-bit)

    – juanpa.arrivillaga
    Nov 21 '18 at 17:16








  • 1





    @tommyO except that answer makes the opposite conclusion that this benchmark is demonstrating.

    – Patrick Roberts
    Nov 21 '18 at 17:21






  • 1





    When I was modifying the benchmark to see what might be affecting performance, I noticed that isolating the scope of sum for each test at least makes the results more consistent. This benchmark results in reduce performing 3-10x faster in the first run on Chrome 70, and then up to 60x faster on sequential runs, whereas if you put them into separate block scopes and use let or const, it consistently performs about 10x faster on all runs for me.

    – Patrick Roberts
    Nov 21 '18 at 17:25



















  • I consistently get the loop performing about 10x faster on Node v10.5.0

    – juanpa.arrivillaga
    Nov 21 '18 at 17:12











  • I edited: I'm in the browser running Chrome 70

    – Truman Purnell
    Nov 21 '18 at 17:16






  • 1





    I figured, just thought it might be of interest. I'm seeing the opposite in the browser as well, Chrome too. Version 70.0.3538.102 (Official Build) (64-bit)

    – juanpa.arrivillaga
    Nov 21 '18 at 17:16








  • 1





    @tommyO except that answer makes the opposite conclusion that this benchmark is demonstrating.

    – Patrick Roberts
    Nov 21 '18 at 17:21






  • 1





    When I was modifying the benchmark to see what might be affecting performance, I noticed that isolating the scope of sum for each test at least makes the results more consistent. This benchmark results in reduce performing 3-10x faster in the first run on Chrome 70, and then up to 60x faster on sequential runs, whereas if you put them into separate block scopes and use let or const, it consistently performs about 10x faster on all runs for me.

    – Patrick Roberts
    Nov 21 '18 at 17:25

















I consistently get the loop performing about 10x faster on Node v10.5.0

– juanpa.arrivillaga
Nov 21 '18 at 17:12





I consistently get the loop performing about 10x faster on Node v10.5.0

– juanpa.arrivillaga
Nov 21 '18 at 17:12













I edited: I'm in the browser running Chrome 70

– Truman Purnell
Nov 21 '18 at 17:16





I edited: I'm in the browser running Chrome 70

– Truman Purnell
Nov 21 '18 at 17:16




1




1





I figured, just thought it might be of interest. I'm seeing the opposite in the browser as well, Chrome too. Version 70.0.3538.102 (Official Build) (64-bit)

– juanpa.arrivillaga
Nov 21 '18 at 17:16







I figured, just thought it might be of interest. I'm seeing the opposite in the browser as well, Chrome too. Version 70.0.3538.102 (Official Build) (64-bit)

– juanpa.arrivillaga
Nov 21 '18 at 17:16






1




1





@tommyO except that answer makes the opposite conclusion that this benchmark is demonstrating.

– Patrick Roberts
Nov 21 '18 at 17:21





@tommyO except that answer makes the opposite conclusion that this benchmark is demonstrating.

– Patrick Roberts
Nov 21 '18 at 17:21




1




1





When I was modifying the benchmark to see what might be affecting performance, I noticed that isolating the scope of sum for each test at least makes the results more consistent. This benchmark results in reduce performing 3-10x faster in the first run on Chrome 70, and then up to 60x faster on sequential runs, whereas if you put them into separate block scopes and use let or const, it consistently performs about 10x faster on all runs for me.

– Patrick Roberts
Nov 21 '18 at 17:25





When I was modifying the benchmark to see what might be affecting performance, I noticed that isolating the scope of sum for each test at least makes the results more consistent. This benchmark results in reduce performing 3-10x faster in the first run on Chrome 70, and then up to 60x faster on sequential runs, whereas if you put them into separate block scopes and use let or const, it consistently performs about 10x faster on all runs for me.

– Patrick Roberts
Nov 21 '18 at 17:25












1 Answer
1






active

oldest

votes


















1














In your first example your giving the V8 compiler lots of hints about what the type is, so there is no boxing / unboxing for it to worry about.



A slightly modified version of the for loop here..
On my machine the for loop is now about 5 times faster than the reduce.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')





As you can see sum[0] += arr[i]; , it's now easy for the V8 compiler to know that this calculation is using a Float64 for adding, because both left & right sides have to be a Float64.



When you had -> sum += arr[i], the right side V8 knows it's a Float64, but the left side could be anything, it could be an integer, string, or even a Float64, so the V8 has to check if it needs boxing into a Float64.



With reduce, again it's implicit that the left & right sides of the add function are going to be Float64, because the v8 engine is traversing an array of Float64, a & b it knows are going to be Float64.






share|improve this answer


























  • To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

    – Jonas Wilms
    Nov 22 '18 at 12:59











  • @JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

    – Keith
    Nov 22 '18 at 14:52











  • @JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

    – Keith
    Nov 22 '18 at 14:56













  • Great answer, that makes a lot more sense now.

    – Patrick Roberts
    Nov 22 '18 at 20:17











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53417267%2ffor-loop-slower-than-reduce%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














In your first example your giving the V8 compiler lots of hints about what the type is, so there is no boxing / unboxing for it to worry about.



A slightly modified version of the for loop here..
On my machine the for loop is now about 5 times faster than the reduce.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')





As you can see sum[0] += arr[i]; , it's now easy for the V8 compiler to know that this calculation is using a Float64 for adding, because both left & right sides have to be a Float64.



When you had -> sum += arr[i], the right side V8 knows it's a Float64, but the left side could be anything, it could be an integer, string, or even a Float64, so the V8 has to check if it needs boxing into a Float64.



With reduce, again it's implicit that the left & right sides of the add function are going to be Float64, because the v8 engine is traversing an array of Float64, a & b it knows are going to be Float64.






share|improve this answer


























  • To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

    – Jonas Wilms
    Nov 22 '18 at 12:59











  • @JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

    – Keith
    Nov 22 '18 at 14:52











  • @JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

    – Keith
    Nov 22 '18 at 14:56













  • Great answer, that makes a lot more sense now.

    – Patrick Roberts
    Nov 22 '18 at 20:17
















1














In your first example your giving the V8 compiler lots of hints about what the type is, so there is no boxing / unboxing for it to worry about.



A slightly modified version of the for loop here..
On my machine the for loop is now about 5 times faster than the reduce.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')





As you can see sum[0] += arr[i]; , it's now easy for the V8 compiler to know that this calculation is using a Float64 for adding, because both left & right sides have to be a Float64.



When you had -> sum += arr[i], the right side V8 knows it's a Float64, but the left side could be anything, it could be an integer, string, or even a Float64, so the V8 has to check if it needs boxing into a Float64.



With reduce, again it's implicit that the left & right sides of the add function are going to be Float64, because the v8 engine is traversing an array of Float64, a & b it knows are going to be Float64.






share|improve this answer


























  • To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

    – Jonas Wilms
    Nov 22 '18 at 12:59











  • @JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

    – Keith
    Nov 22 '18 at 14:52











  • @JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

    – Keith
    Nov 22 '18 at 14:56













  • Great answer, that makes a lot more sense now.

    – Patrick Roberts
    Nov 22 '18 at 20:17














1












1








1







In your first example your giving the V8 compiler lots of hints about what the type is, so there is no boxing / unboxing for it to worry about.



A slightly modified version of the for loop here..
On my machine the for loop is now about 5 times faster than the reduce.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')





As you can see sum[0] += arr[i]; , it's now easy for the V8 compiler to know that this calculation is using a Float64 for adding, because both left & right sides have to be a Float64.



When you had -> sum += arr[i], the right side V8 knows it's a Float64, but the left side could be anything, it could be an integer, string, or even a Float64, so the V8 has to check if it needs boxing into a Float64.



With reduce, again it's implicit that the left & right sides of the add function are going to be Float64, because the v8 engine is traversing an array of Float64, a & b it knows are going to be Float64.






share|improve this answer















In your first example your giving the V8 compiler lots of hints about what the type is, so there is no boxing / unboxing for it to worry about.



A slightly modified version of the for loop here..
On my machine the for loop is now about 5 times faster than the reduce.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')





As you can see sum[0] += arr[i]; , it's now easy for the V8 compiler to know that this calculation is using a Float64 for adding, because both left & right sides have to be a Float64.



When you had -> sum += arr[i], the right side V8 knows it's a Float64, but the left side could be anything, it could be an integer, string, or even a Float64, so the V8 has to check if it needs boxing into a Float64.



With reduce, again it's implicit that the left & right sides of the add function are going to be Float64, because the v8 engine is traversing an array of Float64, a & b it knows are going to be Float64.






var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')





var len = 8e6

function *rands(){
for(let i =0; i < len; i++)
yield Math.random()
}

var add = (a,b) => a + b
var arr = new Float64Array([...rands()])


console.time('reduce')
var sum = arr.reduce(add)
console.log(sum)
console.timeEnd('reduce')

console.time('loop')
var sum = new Float64Array([0]);
for(var i = 0; i < len; i++)
sum[0] += arr[i];
console.log(sum[0])
console.timeEnd('loop')






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 22 '18 at 11:22

























answered Nov 22 '18 at 11:10









KeithKeith

8,3801720




8,3801720













  • To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

    – Jonas Wilms
    Nov 22 '18 at 12:59











  • @JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

    – Keith
    Nov 22 '18 at 14:52











  • @JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

    – Keith
    Nov 22 '18 at 14:56













  • Great answer, that makes a lot more sense now.

    – Patrick Roberts
    Nov 22 '18 at 20:17



















  • To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

    – Jonas Wilms
    Nov 22 '18 at 12:59











  • @JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

    – Keith
    Nov 22 '18 at 14:52











  • @JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

    – Keith
    Nov 22 '18 at 14:56













  • Great answer, that makes a lot more sense now.

    – Patrick Roberts
    Nov 22 '18 at 20:17

















To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

– Jonas Wilms
Nov 22 '18 at 12:59





To verify the final claim it would be interesting how arr.reduce(add, 0) or var sum = arr[0]; perform

– Jonas Wilms
Nov 22 '18 at 12:59













@JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

– Keith
Nov 22 '18 at 14:52





@JonasWilms It has very little effect, yes the accumulator will initially be untyped, but after the first pass it's now known to be Float64, as it would now be boxed, and there would be no reason to unbox back to untyped. IOW: All other ops in the loop are known to be Float64. That's how I see it anyway, I might be off base.. :)

– Keith
Nov 22 '18 at 14:52













@JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

– Keith
Nov 22 '18 at 14:56







@JonasWilms Also doing var sum = arr[0] will cause it to be unboxed, you need to keep access to arr[..] directly to ensure Float64 typing.

– Keith
Nov 22 '18 at 14:56















Great answer, that makes a lot more sense now.

– Patrick Roberts
Nov 22 '18 at 20:17





Great answer, that makes a lot more sense now.

– Patrick Roberts
Nov 22 '18 at 20:17


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53417267%2ffor-loop-slower-than-reduce%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Costa Masnaga

Fotorealismo

Sidney Franklin