0

Sorry this question isn't too accurate. I've come across this by accident somehow: I have 4 heavy load functions HeavyOperation()1-4 that I benchmarked, all being very similar to each other, except 1 of them yields a different result in its execution time, and I cannot explain to myself why that is. They make use of promises and return a promise at some point with a resolve callback simply taking in the result of the heavy for loop. Most notably (I think) being the comparison between the 3rd and 4th one, where saving the result into another variable before returning the promise somehow makes a difference. I don't know what to make of this.

The results in this show the 4th function being twice as slow. Doubling the variable i (using these particular values) heavily changes the results of execution time (7x slower). And a little extra.. Somehow using a value of 1100000000 for i instead of 1000000000 is 9% faster for 1-3rd function, and 21% slower for the 4th one.

When testing please note that Firefox yields extremely inconsistent results, I used chrome which worked better for me. The entire code can be copy pasted

Is this stack/heap related? Something internal in javascript? Are promises even the reason for this to happen?

function TimeTestOperation() {
  let StartTime = Date.now();
  let BigNumber = HeavyOperation1();
  let EndTime = Date.now();
  console.log('HeavyOperation1(): ' + (EndTime - StartTime) + 'ms');

  StartTime = Date.now();
  BigNumber = HeavyOperation2();
  EndTime = Date.now();
  console.log('HeavyOperation2(): ' + (EndTime - StartTime) + 'ms');

  StartTime = Date.now();
  BigNumber = HeavyOperation3();
  EndTime = Date.now();
  console.log('HeavyOperation3(): ' + (EndTime - StartTime) + 'ms');

  StartTime = Date.now();
  BigNumber = HeavyOperation4();
  EndTime = Date.now();
  console.log('HeavyOperation4(): ' + (EndTime - StartTime) + 'ms');
}
TimeTestOperation();

// 711 ms
function HeavyOperation1() {
  return new Promise(resolve => {
    let a = 0;
    for (let i = 0; i < 1000000000; i++) a++;

    let Result = a;
    resolve(Result);
  });
}

// 712 ms
function HeavyOperation2() {
  return new Promise(resolve => {
    let a = 0;
    for (let i = 0; i < 1000000000; i++) a++;

    resolve(a);
  });
}

// 715 ms
function HeavyOperation3() {
  let a = 0;
  for (let i = 0; i < 1000000000; i++) a++;

  let result = a;
  return new Promise(resolve => resolve(result));
}

// 1479 ms
function HeavyOperation4() {
  let a = 0;
  for (let i = 0; i < 1000000000; i++) a++;

  return new Promise(resolve => resolve(a));
}
trincot
  • 317,000
  • 35
  • 244
  • 286
Koyaanis
  • 176
  • 7
  • Unrelated, but why do you return a promise? It is useless, as there is no asynchronous code involved. In each case the function returns a resolved promise. – trincot Feb 13 '21 at 19:24
  • I originally was testing out how async code works when finding this. I just tried turning these into async functions and awaiting the results, same problem – Koyaanis Feb 13 '21 at 19:30
  • Also reading through mozillas async docs, they show that you can make use of normal functions and essentially turn them into async simply by returning a Promise at the beginning of it https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises#rejecting_a_custom_promise – Koyaanis Feb 13 '21 at 19:39
  • The fact that Firefox gives different results is a strong indication that what you're seeing is some optimisation in the Chrome implementation, and the difference might disappear tomorrow if that optimisation is tweaked. As trincot notes, these functions don't wait for anything in the background, so returning a promise or marking them async won't make any difference. You can *mark* them as such, but the way they run won't actually change. – IMSoP Feb 13 '21 at 19:40
  • I understand browsers optimize things, but no matter what browser I use (chrome, chromium, edge, firefox), even for smaller values of i (where one can still reasonably measure performance), I still get bigger results of function 4 over the other 3. I just tried wrapping those functions with async/then() and noticed that they do in fact block (i guess a for loop like that cant be async). But nevermind that.. It's still weird to me why function 4 would run slower. If the for loop is just simply running and completely blocking, why would it take longer to execute in function 4? – Koyaanis Feb 13 '21 at 20:04

2 Answers2

1

Is this stack/heap related? Something internal in javascript?

Yes.

Are promises even the reason for this to happen?

Not exactly the promise, no. It's the fact that in return new Promise(resolve => resolve(a)), the variable a is being closed over by the callback. This has an effect on allocation and optimisation characteristics.

See Does a Javascript closure retain the entire parent lexical environment or only the subset of values the closure references? and the questions linked from there for some details of the V8 implementation. In HeavyOperation3, even while result is being closed over, the variable a that is used heavily in the loop is not.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
-1

Your logic for checking the execution time of Promises is incorrect. Promises are not executed "in sync" that means the execution of the code will move on even though promise is not resolved. If you want to measure the execution time either use aysnc await or use Promise.then syntax

With async await syntax:

async function TimeTestOperation() {
  let StartTime = Date.now();
  let BigNumber = await HeavyOperation1();
  let EndTime = Date.now();
  console.log('HeavyOperation1(): ' + (EndTime - StartTime) + 'ms');

  StartTime = Date.now();
  BigNumber = await HeavyOperation2();
  EndTime = Date.now();
  console.log('HeavyOperation2(): ' + (EndTime - StartTime) + 'ms');

  StartTime = Date.now();
  BigNumber = await HeavyOperation3();
  EndTime = Date.now();
  console.log('HeavyOperation3(): ' + (EndTime - StartTime) + 'ms');

  StartTime = Date.now();
  BigNumber = await HeavyOperation4();
  EndTime = Date.now();
  console.log('HeavyOperation4(): ' + (EndTime - StartTime) + 'ms');
}

Also note that in case of very long loops different browsers may optimize differently and give inconsistent results.

I ran the test on JSFiddle on MS Edge and got

"HeavyOperation1(): 1230ms"
"HeavyOperation2(): 1179ms"
"HeavyOperation3(): 1170ms"
"HeavyOperation4(): 2459ms"

All functions except function 4 ran in nearly same time

Apoorv Kansal
  • 106
  • 2
  • 4
  • *'Promises are not executed "in sync"'*: Promises are objects -- they don't execute. I suppose you talk about the Promise constructor callback. However, it executes synchronously. So that claim is wrong. And your final sentence, is exactly what the OP noticed and is asking about. – trincot Feb 13 '21 at 19:51
  • Yeah it thats what I meant. Promises are async – Apoorv Kansal Feb 13 '21 at 20:21
  • I don't think you get my point. You wrote *"...that means the execution of the code will move on even though promise is not resolved"*: this is not true in the OP's code. All code is executed synchronously, even though it uses promises. Promises give no guarantee by themselves that anything is executed asynchronously. In the OP's code, the functions return *resolved* promises. Your claim at the start of your answer is therefore false. – trincot Feb 13 '21 at 20:23
  • Sorry, I understood the question wrong. But I would like to understand, in this case does javascript wait for the loop to finish before moving on to the next line (the EndTime line?). My understanding on promises says that promises are executed separately. But I may be wrong as you pointed out. – Apoorv Kansal Feb 13 '21 at 20:44
  • Promises don't execute. The callback that you pass to the Promise constructor is what executes. And these callbacks are executed synchronously. In other words, execution does not continue with the next action after `new Promise` until that callback has finished executing. What executes *asynchronously* is the callback that is passed to a `then` method call (or what follows an `await` expression). But since neither is used in the OP's code, there is no asynchrony there. – trincot Feb 13 '21 at 20:54