mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Added performance benchmarks for platform channels (#81414)
This commit is contained in:
parent
dbc8826816
commit
a5f57b9e64
10
dev/benchmarks/platform_channels_benchmarks/.metadata
Normal file
10
dev/benchmarks/platform_channels_benchmarks/.metadata
Normal file
@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: ce18d702e90d3dff9fee53d61a770c94f14f2811
|
||||
channel: master
|
||||
|
||||
project_type: app
|
6
dev/benchmarks/platform_channels_benchmarks/README.md
Normal file
6
dev/benchmarks/platform_channels_benchmarks/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# platform_channels_benchmarks
|
||||
|
||||
The harness for running performance benchmark tests for Platform Channels.
|
||||
|
||||
If you want to run these benchmarks outside of devicelab you need to first run:
|
||||
`flutter create --platforms="ios,android" --no-overwrite .`
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.example.platform_channels_benchmarks
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.BasicMessageChannel
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
val basicStandard = BasicMessageChannel(flutterEngine.dartExecutor, "dev.flutter.echo.basic.standard", StandardMessageCodec.INSTANCE)
|
||||
basicStandard.setMessageHandler { message, reply -> reply.reply(message) }
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
let registrar = self.registrar(forPlugin: "Echo")!
|
||||
let basicStandard = FlutterBasicMessageChannel(
|
||||
name: "dev.flutter.echo.basic.standard", binaryMessenger: registrar.messenger(),
|
||||
codec: FlutterStandardMessageCodec.sharedInstance())
|
||||
basicStandard.setMessageHandler { (input, reply) in
|
||||
reply(input)
|
||||
}
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
130
dev/benchmarks/platform_channels_benchmarks/lib/main.dart
Normal file
130
dev/benchmarks/platform_channels_benchmarks/lib/main.dart
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:microbenchmarks/common.dart';
|
||||
|
||||
List<Object> _makeTestBuffer(int size) {
|
||||
final List<Object> answer = <Object>[];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
switch (i % 9) {
|
||||
case 0:
|
||||
answer.add(1);
|
||||
break;
|
||||
case 1:
|
||||
answer.add(math.pow(2, 65));
|
||||
break;
|
||||
case 2:
|
||||
answer.add(1234.0);
|
||||
break;
|
||||
case 3:
|
||||
answer.add(null);
|
||||
break;
|
||||
case 4:
|
||||
answer.add(<int>[1234]);
|
||||
break;
|
||||
case 5:
|
||||
answer.add(<String, int>{'hello': 1234});
|
||||
break;
|
||||
case 6:
|
||||
answer.add('this is a test');
|
||||
break;
|
||||
case 7:
|
||||
answer.add(true);
|
||||
break;
|
||||
case 8:
|
||||
answer.add(Uint8List(64));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
Future<void> _runTests() async {
|
||||
if (kDebugMode) {
|
||||
throw Exception(
|
||||
"Must be run in profile mode! Use 'flutter run --profile'.");
|
||||
}
|
||||
const int numMessages = 2500;
|
||||
|
||||
const BasicMessageChannel<Object> channel = BasicMessageChannel<Object>(
|
||||
'dev.flutter.echo.basic.standard',
|
||||
StandardMessageCodec(),
|
||||
);
|
||||
final Stopwatch watch = Stopwatch();
|
||||
|
||||
watch.start();
|
||||
for (int i = 0; i < numMessages; ++i) {
|
||||
await channel.send(1234);
|
||||
}
|
||||
watch.stop();
|
||||
final double smallPayloadTime = watch.elapsedMicroseconds / numMessages;
|
||||
|
||||
watch.reset();
|
||||
/// WARNING: Don't change the following line of code, it will invalidate
|
||||
/// `Large` tests. Instead make a different test. The size of largeBuffer
|
||||
/// serialized is 14214 bytes.
|
||||
final List<Object> largeBuffer = _makeTestBuffer(1000);
|
||||
|
||||
int size = 0;
|
||||
watch.start();
|
||||
for (int i = 0; i < numMessages; ++i) {
|
||||
final List<Object> result = await channel.send(largeBuffer) as List<Object>;
|
||||
// This check should be tiny compared to the actual channel send/receive.
|
||||
size += (result == null) ? 0 : result.length;
|
||||
}
|
||||
watch.stop();
|
||||
final double largePayloadTime = watch.elapsedMicroseconds / numMessages;
|
||||
|
||||
if (size != largeBuffer.length * numMessages) {
|
||||
throw Exception(
|
||||
'There is an error with the echo channel, the results don\'t add up: $size');
|
||||
}
|
||||
|
||||
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
|
||||
printer.addResult(
|
||||
description: 'BasicMessageChannel/StandardMessageCodec/Flutter->Host/Small',
|
||||
value: smallPayloadTime,
|
||||
unit: 'µs',
|
||||
name: 'platform_channel_basic_standard_2host_small',
|
||||
);
|
||||
printer.addResult(
|
||||
description: 'BasicMessageChannel/StandardMessageCodec/Flutter->Host/Large',
|
||||
value: largePayloadTime,
|
||||
unit: 'µs',
|
||||
name: 'platform_channel_basic_standard_2host_large',
|
||||
);
|
||||
printer.printToStdout();
|
||||
}
|
||||
|
||||
class _BenchmarkWidget extends StatefulWidget {
|
||||
const _BenchmarkWidget(this.tests, {Key key}) : super(key: key);
|
||||
|
||||
final Future<void> Function() tests;
|
||||
|
||||
@override
|
||||
_BenchmarkWidgetState createState() => _BenchmarkWidgetState();
|
||||
}
|
||||
|
||||
class _BenchmarkWidgetState extends State<_BenchmarkWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
widget.tests();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Container();
|
||||
}
|
||||
|
||||
void main() {
|
||||
runApp(const _BenchmarkWidget(_runTests));
|
||||
}
|
77
dev/benchmarks/platform_channels_benchmarks/pubspec.yaml
Normal file
77
dev/benchmarks/platform_channels_benchmarks/pubspec.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
name: platform_channels_benchmarks
|
||||
description: Test harness for Platform Channel performance tests.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.9.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
microbenchmarks:
|
||||
path: ../microbenchmarks
|
||||
cupertino_icons: 1.0.2
|
||||
|
||||
_fe_analyzer_shared: 21.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 1.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
args: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
async: 2.6.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
characters: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
cli_util: 0.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.15.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 3.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 6.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_multi_server: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_parser: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
io: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
js: 0.6.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
logging: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
matcher: 0.12.10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
meta: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
mime: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
node_preamble: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
package_config: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
path: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
pedantic: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
pool: 1.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
pub_semver: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_packages_handler: 3.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_static: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_web_socket: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
source_map_stack_trace: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
source_maps: 0.10.10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
source_span: 1.8.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
stack_trace: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
stream_channel: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
string_scanner: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
term_glyph: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
test: 1.16.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
test_api: 0.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
test_core: 0.3.19 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
typed_data: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
vector_math: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
vm_service: 6.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
watcher: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
web_socket_channel: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
webkit_inspection_protocol: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
yaml: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
clock: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
fake_async: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 77ad
|
14
dev/devicelab/bin/tasks/platform_channels_benchmarks.dart
Normal file
14
dev/devicelab/bin/tasks/platform_channels_benchmarks.dart
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_devicelab/framework/adb.dart'
|
||||
show DeviceOperatingSystem;
|
||||
import 'package:flutter_devicelab/framework/framework.dart' show task;
|
||||
import 'package:flutter_devicelab/tasks/platform_channels_benchmarks.dart'
|
||||
as platform_channels_benchmarks;
|
||||
|
||||
Future<void> main() async {
|
||||
await task(
|
||||
platform_channels_benchmarks.runTask(DeviceOperatingSystem.android));
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_devicelab/framework/adb.dart'
|
||||
show DeviceOperatingSystem;
|
||||
import 'package:flutter_devicelab/framework/framework.dart' show task;
|
||||
import 'package:flutter_devicelab/tasks/platform_channels_benchmarks.dart'
|
||||
as platform_channels_benchmarks;
|
||||
|
||||
Future<void> main() async {
|
||||
await task(platform_channels_benchmarks.runTask(DeviceOperatingSystem.ios));
|
||||
}
|
105
dev/devicelab/lib/microbenchmarks.dart
Normal file
105
dev/devicelab/lib/microbenchmarks.dart
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
/// Launches a new Flutter process.
|
||||
Future<Process> startFlutter({
|
||||
List<String> options = const <String>[],
|
||||
bool canFail = false,
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
final List<String> args = flutterCommandArgs('run', options);
|
||||
return startProcess(path.join(flutterDirectory.path, 'bin', 'flutter'), args, environment: environment);
|
||||
}
|
||||
|
||||
/// Reades through the print commands from [process] waiting for the magic phase
|
||||
/// that contains microbenchmarks results as defined in
|
||||
/// `dev/benchmarks/microbenchmarks/lib/common.dart`.
|
||||
Future<Map<String, double>> readJsonResults(Process process) {
|
||||
// IMPORTANT: keep these values in sync with dev/benchmarks/microbenchmarks/lib/common.dart
|
||||
const String jsonStart = '================ RESULTS ================';
|
||||
const String jsonEnd = '================ FORMATTED ==============';
|
||||
const String jsonPrefix = ':::JSON:::';
|
||||
bool jsonStarted = false;
|
||||
final StringBuffer jsonBuf = StringBuffer();
|
||||
final Completer<Map<String, double>> completer = Completer<Map<String, double>>();
|
||||
|
||||
final StreamSubscription<String> stderrSub = process.stderr
|
||||
.transform<String>(const Utf8Decoder())
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
stderr.writeln('[STDERR] $line');
|
||||
});
|
||||
|
||||
bool processWasKilledIntentionally = false;
|
||||
bool resultsHaveBeenParsed = false;
|
||||
final StreamSubscription<String> stdoutSub = process.stdout
|
||||
.transform<String>(const Utf8Decoder())
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) async {
|
||||
print(line);
|
||||
|
||||
if (line.contains(jsonStart)) {
|
||||
jsonStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.contains(jsonEnd)) {
|
||||
final String jsonOutput = jsonBuf.toString();
|
||||
|
||||
// If we end up here and have already parsed the results, it suggests that
|
||||
// we have received output from another test because our `flutter run`
|
||||
// process did not terminate correctly.
|
||||
// https://github.com/flutter/flutter/issues/19096#issuecomment-402756549
|
||||
if (resultsHaveBeenParsed) {
|
||||
throw 'Additional JSON was received after results has already been '
|
||||
'processed. This suggests the `flutter run` process may have lived '
|
||||
'past the end of our test and collected additional output from the '
|
||||
'next test.\n\n'
|
||||
'The JSON below contains all collected output, including both from '
|
||||
'the original test and what followed.\n\n'
|
||||
'$jsonOutput';
|
||||
}
|
||||
|
||||
jsonStarted = false;
|
||||
processWasKilledIntentionally = true;
|
||||
resultsHaveBeenParsed = true;
|
||||
// Sending a SIGINT/SIGTERM to the process here isn't reliable because [process] is
|
||||
// the shell (flutter is a shell script) and doesn't pass the signal on.
|
||||
// Sending a `q` is an instruction to quit using the console runner.
|
||||
// See https://github.com/flutter/flutter/issues/19208
|
||||
process.stdin.write('q');
|
||||
await process.stdin.flush();
|
||||
// Also send a kill signal in case the `q` above didn't work.
|
||||
process.kill(ProcessSignal.sigint);
|
||||
try {
|
||||
completer.complete(Map<String, double>.from(json.decode(jsonOutput) as Map<String, dynamic>));
|
||||
} catch (ex) {
|
||||
completer.completeError('Decoding JSON failed ($ex). JSON string was: $jsonOutput');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonStarted && line.contains(jsonPrefix))
|
||||
jsonBuf.writeln(line.substring(line.indexOf(jsonPrefix) + jsonPrefix.length));
|
||||
});
|
||||
|
||||
process.exitCode.then<void>((int code) async {
|
||||
await Future.wait<void>(<Future<void>>[
|
||||
stdoutSub.cancel(),
|
||||
stderrSub.cancel(),
|
||||
]);
|
||||
if (!processWasKilledIntentionally && code != 0) {
|
||||
completer.completeError('flutter run failed: exit code=$code');
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
@ -3,13 +3,13 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_devicelab/framework/adb.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
import 'package:flutter_devicelab/microbenchmarks.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
/// Creates a device lab task that runs benchmarks in
|
||||
@ -34,14 +34,15 @@ TaskFunction createMicrobenchmarkTask() {
|
||||
device.deviceId,
|
||||
];
|
||||
options.add(benchmarkPath);
|
||||
return _startFlutter(
|
||||
return startFlutter(
|
||||
options: options,
|
||||
canFail: false,
|
||||
);
|
||||
});
|
||||
|
||||
return _readJsonResults(flutterProcess);
|
||||
return readJsonResults(flutterProcess);
|
||||
}
|
||||
|
||||
return _run();
|
||||
}
|
||||
|
||||
@ -57,99 +58,9 @@ TaskFunction createMicrobenchmarkTask() {
|
||||
...await _runMicrobench('lib/language/sync_star_semantics_bench.dart'),
|
||||
...await _runMicrobench('lib/foundation/all_elements_bench.dart'),
|
||||
...await _runMicrobench('lib/foundation/change_notifier_bench.dart'),
|
||||
};
|
||||
};
|
||||
|
||||
return TaskResult.success(allResults, benchmarkScoreKeys: allResults.keys.toList());
|
||||
return TaskResult.success(allResults,
|
||||
benchmarkScoreKeys: allResults.keys.toList());
|
||||
};
|
||||
}
|
||||
|
||||
Future<Process> _startFlutter({
|
||||
List<String> options = const <String>[],
|
||||
bool canFail = false,
|
||||
Map<String, String> environment,
|
||||
}) {
|
||||
final List<String> args = flutterCommandArgs('run', options);
|
||||
return startProcess(path.join(flutterDirectory.path, 'bin', 'flutter'), args, environment: environment);
|
||||
}
|
||||
|
||||
Future<Map<String, double>> _readJsonResults(Process process) {
|
||||
// IMPORTANT: keep these values in sync with dev/benchmarks/microbenchmarks/lib/common.dart
|
||||
const String jsonStart = '================ RESULTS ================';
|
||||
const String jsonEnd = '================ FORMATTED ==============';
|
||||
const String jsonPrefix = ':::JSON:::';
|
||||
bool jsonStarted = false;
|
||||
final StringBuffer jsonBuf = StringBuffer();
|
||||
final Completer<Map<String, double>> completer = Completer<Map<String, double>>();
|
||||
|
||||
final StreamSubscription<String> stderrSub = process.stderr
|
||||
.transform<String>(const Utf8Decoder())
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
stderr.writeln('[STDERR] $line');
|
||||
});
|
||||
|
||||
bool processWasKilledIntentionally = false;
|
||||
bool resultsHaveBeenParsed = false;
|
||||
final StreamSubscription<String> stdoutSub = process.stdout
|
||||
.transform<String>(const Utf8Decoder())
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) async {
|
||||
print(line);
|
||||
|
||||
if (line.contains(jsonStart)) {
|
||||
jsonStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.contains(jsonEnd)) {
|
||||
final String jsonOutput = jsonBuf.toString();
|
||||
|
||||
// If we end up here and have already parsed the results, it suggests that
|
||||
// we have received output from another test because our `flutter run`
|
||||
// process did not terminate correctly.
|
||||
// https://github.com/flutter/flutter/issues/19096#issuecomment-402756549
|
||||
if (resultsHaveBeenParsed) {
|
||||
throw 'Additional JSON was received after results has already been '
|
||||
'processed. This suggests the `flutter run` process may have lived '
|
||||
'past the end of our test and collected additional output from the '
|
||||
'next test.\n\n'
|
||||
'The JSON below contains all collected output, including both from '
|
||||
'the original test and what followed.\n\n'
|
||||
'$jsonOutput';
|
||||
}
|
||||
|
||||
jsonStarted = false;
|
||||
processWasKilledIntentionally = true;
|
||||
resultsHaveBeenParsed = true;
|
||||
// Sending a SIGINT/SIGTERM to the process here isn't reliable because [process] is
|
||||
// the shell (flutter is a shell script) and doesn't pass the signal on.
|
||||
// Sending a `q` is an instruction to quit using the console runner.
|
||||
// See https://github.com/flutter/flutter/issues/19208
|
||||
process.stdin.write('q');
|
||||
await process.stdin.flush();
|
||||
// Also send a kill signal in case the `q` above didn't work.
|
||||
process.kill(ProcessSignal.sigint);
|
||||
try {
|
||||
completer.complete(Map<String, double>.from(json.decode(jsonOutput) as Map<String, dynamic>));
|
||||
} catch (ex) {
|
||||
completer.completeError('Decoding JSON failed ($ex). JSON string was: $jsonOutput');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonStarted && line.contains(jsonPrefix))
|
||||
jsonBuf.writeln(line.substring(line.indexOf(jsonPrefix) + jsonPrefix.length));
|
||||
});
|
||||
|
||||
process.exitCode.then<void>((int code) async {
|
||||
await Future.wait<void>(<Future<void>>[
|
||||
stdoutSub.cancel(),
|
||||
stderrSub.cancel(),
|
||||
]);
|
||||
if (!processWasKilledIntentionally && code != 0) {
|
||||
completer.completeError('flutter run failed: exit code=$code');
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
55
dev/devicelab/lib/tasks/platform_channels_benchmarks.dart
Normal file
55
dev/devicelab/lib/tasks/platform_channels_benchmarks.dart
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io' show Process, Directory;
|
||||
|
||||
import 'package:flutter_devicelab/framework/adb.dart' as adb;
|
||||
import 'package:flutter_devicelab/framework/framework.dart' show TaskFunction;
|
||||
import 'package:flutter_devicelab/framework/task_result.dart' show TaskResult;
|
||||
import 'package:flutter_devicelab/framework/utils.dart' as utils;
|
||||
import 'package:flutter_devicelab/microbenchmarks.dart' as microbenchmarks;
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
TaskFunction runTask(adb.DeviceOperatingSystem operatingSystem) {
|
||||
return () async {
|
||||
adb.deviceOperatingSystem = operatingSystem;
|
||||
final adb.Device device = await adb.devices.workingDevice;
|
||||
await device.unlock();
|
||||
|
||||
final Directory appDir = utils.dir(path.join(utils.flutterDirectory.path,
|
||||
'dev/benchmarks/platform_channels_benchmarks'));
|
||||
final Process flutterProcess = await utils.inDirectory(appDir, () async {
|
||||
final String flutterExe =
|
||||
path.join(utils.flutterDirectory.path, 'bin', 'flutter');
|
||||
final List<String> createArgs = <String>[
|
||||
'create',
|
||||
'--platforms',
|
||||
'ios,android',
|
||||
'--no-overwrite',
|
||||
'-v',
|
||||
'.'
|
||||
];
|
||||
print('\nExecuting: $flutterExe $createArgs $appDir');
|
||||
utils.eval(flutterExe, createArgs);
|
||||
|
||||
final List<String> options = <String>[
|
||||
'-v',
|
||||
// --release doesn't work on iOS due to code signing issues
|
||||
'--profile',
|
||||
'--no-publish-port',
|
||||
'-d',
|
||||
device.deviceId,
|
||||
];
|
||||
return microbenchmarks.startFlutter(
|
||||
options: options,
|
||||
canFail: false,
|
||||
);
|
||||
});
|
||||
|
||||
final Map<String, double> results =
|
||||
await microbenchmarks.readJsonResults(flutterProcess);
|
||||
return TaskResult.success(results,
|
||||
benchmarkScoreKeys: results.keys.toList());
|
||||
};
|
||||
}
|
@ -372,6 +372,12 @@
|
||||
"task_name": "linux_picture_cache_perf__e2e_summary",
|
||||
"flaky": false
|
||||
},
|
||||
{
|
||||
"name": "Linux platform channels benchmarks",
|
||||
"repo": "flutter",
|
||||
"task_name": "linux_platform_channels_benchmarks",
|
||||
"flaky": true
|
||||
},
|
||||
{
|
||||
"name": "Linux platform_views_scroll_perf__timeline_summary",
|
||||
"repo": "flutter",
|
||||
@ -1152,6 +1158,12 @@
|
||||
"task_name": "mac_ios_platform_channel_sample_test_swift",
|
||||
"flaky": false
|
||||
},
|
||||
{
|
||||
"name": "Mac_ios platform channels benchmarks",
|
||||
"repo": "flutter",
|
||||
"task_name": "mac_ios_platform_channels_benchmarks",
|
||||
"flaky": true
|
||||
},
|
||||
{
|
||||
"name": "Mac_ios platform_interaction_test_ios",
|
||||
"repo": "flutter",
|
||||
|
Loading…
Reference in New Issue
Block a user