Skip to content

Sending data to an isolate locks the main thread for some time #31960

Open
@Hixie

Description

@Hixie

The following little flutter program creates an isolate, and times how long the main thread locks while sending a 100MB of zero bytes to 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 100MB ByteData buffer of zeros to the isolate, then awaits a response, and prints the total round-trip time.

The isolate merely runs a Future-mediated loop that waits for a message, then sends a single byte back.

For flutter, what matters is primarily that the total overhead of sending something to 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 = true;

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 sending 100MB to another isolate appears to be in the double-digit millisecond range, which means that sending 100MB to another isolate guarantees that the application will miss a frame.

I/flutter ( 3337): sending...
I/flutter ( 3337): 16ms since last checkin
I/flutter ( 3337): 127ms for round-trip
I/flutter ( 3337): sending...
I/flutter ( 3337): 15ms since last checkin
I/flutter ( 3337): 126ms for round-trip
I/flutter ( 3337): sending...
I/flutter ( 3337): 16ms since last checkin
I/flutter ( 3337): 130ms for round-trip
I/flutter ( 3337): sending...
I/flutter ( 3337): 16ms since last checkin
I/flutter ( 3337): 126ms for round-trip
I/flutter ( 3337): sending...
I/flutter ( 3337): 15ms since last checkin
I/flutter ( 3337): 124ms for round-trip
I/flutter ( 3337): sending...
I/flutter ( 3337): 16ms since last checkin
I/flutter ( 3337): 124ms for round-trip
I/flutter ( 3337): sending...
I/flutter ( 3337): 16ms since last checkin
I/flutter ( 3337): 122ms for round-trip
I/flutter ( 3337): sending...

See also #31959, which is a much more serious problem with receiving data taking double-digit milliseconds.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.customer-flutterlibrary-isolatetype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions