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

Receiving data from an isolate locks the main thread for a very long time #31959

Closed
Hixie opened this issue Jan 22, 2018 · 1 comment
Closed
Assignees
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. customer-flutter

Comments

@Hixie
Copy link
Contributor

Hixie commented Jan 22, 2018

The following little flutter program creates an isolate, and times how long the main thread locks while receiving a 100MB of zero bytes from an isolate. It does this by having a timer run continuously on the main thread, timing how long since it last ran, and printing any time that it took more than 5ms between invocations ("since last checkin"). If the main thread is never locked, this should never print anything. At the same time, in a Future-mediated loop on the main thread, it sends a single byte to the isolate, and then receives a 100MB ByteData buffer of zeros in response, and prints the total round-trip time.

The isolate merely runs a Future-mediated loop that waits for a message, then sends a 100MB buffer of zero bytes back.

For flutter, what matters is primarily that the total overhead of receiving something from a thread is small (small single-digits of milliseconds at most, ideally microseconds). Total round-trip time is only of academic interest so long as it's not measured in minutes.

import 'dart:async';
import 'dart:isolate';
import 'dart:typed_data';

// true = time the in-thread overhead for sending a big message out of the main thread                                                  
// false = time the in-thread overhead for receiving a big message into the main thread                                                 
const bool benchmarkSend = false;

const int toIsolateSize = benchmarkSend ? 100 * 1024 * 1024 : 1;
const int fromIsolateSize = benchmarkSend ? 1 : 100 * 1024 * 1024;

Future<Null> main() async {
  Timer.run(idleTimer);
  ReceivePort port = new ReceivePort();
  StreamIterator<dynamic> inbox = new StreamIterator<dynamic>(port);
  Isolate.spawn(isolateMain, port.sendPort);
  await inbox.moveNext();
  SendPort outbox = inbox.current;
  Stopwatch workWatch = new Stopwatch();
  ByteData data = new ByteData(toIsolateSize);
  while (true) {
    print('sending...');
    workWatch.start();
    outbox.send(data);
    await inbox.moveNext();
    workWatch.stop();
    int time = workWatch.elapsedMilliseconds;
    print('${time}ms for round-trip');
    workWatch.reset();
  }
}
 
Future<Null> isolateMain(SendPort outbox) async {
  ReceivePort port = new ReceivePort();
  StreamIterator<dynamic> inbox = new StreamIterator<dynamic>(port);
  outbox.send(port.sendPort);
  ByteData data = new ByteData(fromIsolateSize);
  while (true) {
    await inbox.moveNext();
    outbox.send(data);
  }
}

Stopwatch idleWatch = new Stopwatch();

void idleTimer() {
  idleWatch.stop();
  int time = idleWatch.elapsedMilliseconds;
  if (time > 5)
    print('${time}ms since last checkin');
  idleWatch.reset();
  idleWatch.start();
  Timer.run(idleTimer);
}

Here is some representative output for this script running on a Pixel XL 2. The total overhead for receiving 100MB from another isolate appears to be in the triple-digit* millisecond range, which means that receiving 100MB from another isolate guarantees that the application will miss many frames, animations will stutter noticeably, etc.

I/flutter ( 3647): sending...
I/flutter ( 3647): 126ms since last checkin
I/flutter ( 3647): 145ms for round-trip
I/flutter ( 3647): sending...
I/flutter ( 3647): 125ms since last checkin
I/flutter ( 3647): 146ms for round-trip
I/flutter ( 3647): sending...
I/flutter ( 3647): 126ms since last checkin
I/flutter ( 3647): 147ms for round-trip
I/flutter ( 3647): sending...
I/flutter ( 3647): 134ms since last checkin
I/flutter ( 3647): 220ms for round-trip
I/flutter ( 3647): sending...
I/flutter ( 3647): 129ms since last checkin
I/flutter ( 3647): 148ms for round-trip
I/flutter ( 3647): sending...
I/flutter ( 3647): 126ms since last checkin
I/flutter ( 3647): 145ms for round-trip
I/flutter ( 3647): sending...
I/flutter ( 3647): 123ms since last checkin
I/flutter ( 3647): 145ms for round-trip
I/flutter ( 3647): sending...

Receiving pure byte data should be something we can implement with near-zero overhead on the receiving thread. It can be placed in memory then the memory handed off without the main thread being particularly involved. When receiving data structures (not benchmarked here), I could see this being more complicated. That is also something we will need to make fast, as it will be common for people to want to create Widgets off the main thread then hand them over efficiently.

See also #31960, which is a less serious problem with sending data taking double-digit milliseconds.

Hixie added a commit to Hixie/flutter that referenced this issue Jan 22, 2018
This reduces the jank of bringing up the license screen further.
The remaining lost frame or two are caused by Dart itself, see:

   dart-lang/sdk#31954
   dart-lang/sdk#31959
   dart-lang/sdk#31960

Fixes flutter#5187
Hixie added a commit to flutter/flutter that referenced this issue Jan 25, 2018
This reduces the jank of bringing up the license screen further.
The remaining lost frame or two are caused by Dart itself, see:

   dart-lang/sdk#31954
   dart-lang/sdk#31959
   dart-lang/sdk#31960

Fixes #5187
@anders-sandholm anders-sandholm added area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Jan 29, 2018
@a-siva
Copy link
Contributor

a-siva commented Jan 29, 2018

cc @rmacnak-google

dart-bot pushed a commit that referenced this issue Jan 30, 2018
…ssages.

Bug: #31959
Change-Id: I7235d24e29b72bc9849d726b63f0d4c38633e803
Reviewed-on: https://dart-review.googlesource.com/37423
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
dart-bot pushed a commit that referenced this issue Feb 14, 2018
…struction of the Message.

Remove unused special case in ApiMessageWriter for lists of int.

This is in preparation for ensuring we always free any external data that ends up in an isolate message.

Bug: #31959
Change-Id: I999656fc11d2aee9aebe70852be5bb075f234b4d
Reviewed-on: https://dart-review.googlesource.com/41020
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
dart-bot pushed a commit that referenced this issue Feb 21, 2018
…ssages.

Be careful to free external data when reading or writing a message is interrupted, or releasing messaging without reading on shutdown.

Bug: #31959
Change-Id: Ia39acb9ca0e27cf9e8b83961741e5949b5930266
Reviewed-on: https://dart-review.googlesource.com/41561
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
DaveShuckerow pushed a commit to DaveShuckerow/flutter that referenced this issue May 14, 2018
This reduces the jank of bringing up the license screen further.
The remaining lost frame or two are caused by Dart itself, see:

   dart-lang/sdk#31954
   dart-lang/sdk#31959
   dart-lang/sdk#31960

Fixes flutter#5187
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. customer-flutter
Projects
None yet
Development

No branches or pull requests

4 participants