Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Future(computation) constructor slow in javascript (by 1000 times slower than Future.value()) #48104

Closed
tatumizer opened this issue Jan 8, 2022 · 1 comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-async

Comments

@tatumizer
Copy link

tatumizer commented Jan 8, 2022

Running this program in dartpad:

import "dart:async";

main() async {
  var sw = Stopwatch()..start();
  int N = 10;
  int result = await (int n) async {
    for (int i = 0; i < n; i++) {
      await Future.value(1);
    }
    return n;
  }(N);
  print(result);
  print("t=${sw.elapsedMicroseconds}");
 
  sw.reset();
  result = await (int n) async {
    for (int i = 0; i < n; i++) {
      await Future(() => 1);
    }
    return n;
  }(N);
  print(result);
  print("t=${sw.elapsedMicroseconds}");
  return result;
}

Results:
10
t=5201
10
t=5814799

The culprit is Timer.run() method used inside Future constructor.
The same program, when run on VM, produces the following output (with N=100000)
100000
t=64293
100000
t=496046
Exited

Still slower, but only by 7-8 times, not 1000 times. (I expected it to be slower by 2 times max)

(I also compared the Future.value() version in dartpad vs Future.value() in VM. In javascript, it's slower by 2.5 times, which is reasonable).

This affects all methods that use Timer.run() under the hood: Future.delayed, Future.microtask, scheduleMicrotask and probably more.
Flutter also uses Timer.run directly in some places. Maybe this issue should be open against Timer.run instead - not sure.

EDIT: when I run the same test in MS Edge, the timing, in absolute terms, is much better, but the ratio is still around 500.
When you try to reproduce, set N to a higher value (say, 1000 or 10000).

@vsmenon vsmenon added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-async labels Jan 9, 2022
@lrhn
Copy link
Member

lrhn commented Jan 10, 2022

The Future(computation) is equivalent to Future.delayed(Timer.zero, computation), so it delays the entire computation (by a little).
If you wanted to compute now, and still capture errors into the future, you can use Future.sync(computation).

We basically have all three options:

  • Right now: Future.sync(computation)
  • Later microtask: Future.microtask(computation)
  • Later event: Future(computation)

We really had no good input on one should be the default when we designed the Future class, and thought that "a later event" would be what people wanted for their asynchronous computation. In practice, people created woefully few futures with any of those constructors, it was almost always either Completer or just .then on an existing future. The Future.delayed constructor is probably the most used one, even if it's primarily used in tests.

If designed today, I probably would have skipped the Future constructor entirely, and maybe made Future.sync be just Future.
Isn't hindsight great 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-async
Projects
None yet
Development

No branches or pull requests

3 participants