user3103070
user3103070

Reputation: 115

performance of dart code in browser vs. VM

I was surprised by the performance of my Dart code in the browser vs. the Dart VM. Here is a simple example that reproduces the issue.

test('speed test', () {
    var n = 10000;
    var rand = Random(0);
    var x = List.generate(n, (i) => rand.nextDouble());

    var res = <num>[];
    var sw = Stopwatch()..start();
    for (int i=0; i<1000; i++) {
      for (int j=0; j<n; j++) {
        x[j] += i;
      }
      res.add(x.reduce((a, b) => a + b));
    }
    sw.stop();
    print('Milliseconds: ${sw.elapsedMilliseconds}');
  });

If I run this code with dart, I get somewhere around 140 milliseconds. If I run the same code as a browser test with pub run test -p "chrome" ... I get times around 8000 milliseconds.

I am willing to wait for a 0.1 s calculation, but to wait 8 s for something in the browser, no -- it is basically unusable. When I go in release mode, the performance in browser improve but it's still 10x slower.

Am I missing something? Do I have to avoid any calculations in the browser?

Thanks, Tony

Upvotes: 0

Views: 184

Answers (2)

Stephen
Stephen

Reputation: 471

The Dart tracking issue for this is https://github.com/dart-lang/sdk/issues/38705.

The performance of this kind of code has recently improved considerably and is much closer to the Dart VM.

Upvotes: 0

lrn
lrn

Reputation: 71813

It's interesting how slow this is.

The corresponding JavaScript code:

(function() {
  "use strict";
  var n = 10000;
  var x = [];
  var res = [];
  for (var i = 0; i < n; i++) x.push(Math.random());
  var t0 = Date.now();
  for (var i = 0; i < 1000; i++) {
    for (var j = 0; j < n; j++) {
      x[j] += i;
    }
    res.push(x.reduce((a, b) => a + b));
  }
  var t1 = Date.now();
  console.log("Milliseconds: " + (t1 - t0));
}());

runs in as little as ~20 milliseconds.

So, it looks like Dart is somehow triggering "slow mode" for its generated Javascript.

If you look at the generated code, it contains:

  for (i = 0; i < 1000; ++i) {
    for (j = 0; j < 10000; ++j) {
      if (j >= x.length)
        return H.ioore(x, j);
      t1 = x[j];
      if (typeof t1 !== "number")
        return t1.$add();
      C.JSArray_methods.$indexSet(x, j, t1 + i);
    }
    C.JSArray_methods.add$1(res, C.JSArray_methods.reduce$1(x, new A.main_closure0()));
  }

You can try to tweak this code, but the big cost comes from C.JSArray_methods.$indexSet(x, j, t1 + i);. If you change that to x[j] = t1 + i;, the time drops to a few hundred milliseconds. So, this is the problem with the current code.

(You can improve performance a little, ~20%, by making x a List<num> instead of a List<double>. I have no idea why that makes a difference, the generated code is almost the same, the add closure uses checkDouble to check the type instead of checkNum, but they have exactly the same body).

You don't have to avoid any computation in the browser. You may have to optimize a little for slow cases like this (or report them to the compiler developers, because this probably can be recognized and optimized, it just fails to be so for now). For example, you can change your list x of doubles to a Float64List from dart:typed_data:

var x = Float64List.fromList([for (var i = 0; i < n; i++) rand.nextDouble()]);

Then speed increases quite a lot.

Upvotes: 1

Related Questions