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

WIP: Optimize CLI startup time #2512

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/patrol_cli/lib/src/analytics/analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class FlutterVersion {
factory FlutterVersion.test() => FlutterVersion('1.2.3', 'stable');

factory FlutterVersion.fromCLI(FlutterCommand flutterCommand) {
final stopwatch = Stopwatch()..start();
final result = io.Process.runSync(
flutterCommand.executable,
[
Expand All @@ -200,6 +201,8 @@ class FlutterVersion {
jsonDecode(cleanJsonResult(result)) as Map<String, dynamic>;
final frameworkVersion = versionData['frameworkVersion'] as String;
final channel = versionData['channel'] as String;
print(
'debug: time checking flutter version ${stopwatch.elapsedMilliseconds} ms');
return FlutterVersion(frameworkVersion, channel);
}

Expand Down
37 changes: 35 additions & 2 deletions packages/patrol_cli/lib/src/runner/patrol_command_runner.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:io' show ProcessSignal, stdin;
import 'dart:async';
import 'dart:io' as p show Platform;
import 'dart:io' show ProcessSignal, stdin;
import 'dart:isolate' as isolate;

import 'package:adb/adb.dart';
import 'package:args/args.dart';
Expand Down Expand Up @@ -35,7 +37,11 @@ import 'package:platform/platform.dart';
import 'package:process/process.dart';
import 'package:pub_updater/pub_updater.dart';

final stopwatch = Stopwatch();

Future<int> patrolCommandRunner(List<String> args) async {
stopwatch.start();
final preSetup = stopwatch.elapsed.inMilliseconds;
final pubUpdater = PubUpdater();
final logger = Logger();
const fs = LocalFileSystem();
Expand All @@ -61,6 +67,8 @@ Future<int> patrolCommandRunner(List<String> args) async {
processManager: processManager,
isCI: isCI,
);
final postSetup = stopwatch.elapsed.inMilliseconds;
print('debug: time setting up: ${postSetup - preSetup} ms');

ProcessSignal.sigint.watch().listen((signal) async {
logger.detail('Caught SIGINT, exiting...');
Expand All @@ -72,12 +80,19 @@ Future<int> patrolCommandRunner(List<String> args) async {
});
});

final preRun = stopwatch.elapsed.inMilliseconds;
final exitCode = await runner.run(args) ?? 0;
final postRun = stopwatch.elapsed.inMilliseconds;
print('debug: time running: ${postRun - preRun} ms');

if (!runner._disposeScope.disposed) {
final preDisposal = stopwatch.elapsed.inMilliseconds;
await runner.dispose();
final postDisposal = stopwatch.elapsed.inMilliseconds;
print('debug: time disposing: ${postDisposal - preDisposal} ms');
}

print('debug: total time: ${stopwatch.elapsed.inMilliseconds} ms');
return exitCode;
}

Expand Down Expand Up @@ -123,6 +138,7 @@ class PatrolCommandRunner extends CompletionCommandRunner<int> {

final rootDirectory = findRootDirectory(_fs) ?? _fs.currentDirectory;

final preConstruct = stopwatch.elapsed.inMilliseconds;
final androidTestBackend = AndroidTestBackend(
adb: adb,
processManager: _processManager,
Expand Down Expand Up @@ -275,6 +291,9 @@ class PatrolCommandRunner extends CompletionCommandRunner<int> {
help: 'Print version of this program.',
negatable: false,
);

final postConstruct = stopwatch.elapsed.inMilliseconds;
print('debug: time constructing: ${postConstruct - preConstruct} ms');
}

final PubUpdater _pubUpdater;
Expand Down Expand Up @@ -315,7 +334,10 @@ Ask questions, get support at https://github.com/leancodepl/patrol/discussions''

var exitCode = 1;
try {
final preFirstRun = stopwatch.elapsed.inMilliseconds;
_handleFirstRun();
final postFirstRun = stopwatch.elapsed.inMilliseconds;
print('debug: time _handleFirstRun: ${postFirstRun - preFirstRun} ms');

final topLevelResults = parse(args);
verbose = topLevelResults['verbose'] == true;
Expand Down Expand Up @@ -373,7 +395,15 @@ Ask questions, get support at https://github.com/leancodepl/patrol/discussions''
final commandName = topLevelResults.command?.name;

if (_wantsUpdateCheck(commandName)) {
await _checkForUpdate(commandName);
final preCheckForUpdate = stopwatch.elapsed.inMilliseconds;
// Keep update check off the critical path (see #1966)
unawaited(
isolate.Isolate.spawn<void>((_) => _checkForUpdate(commandName), null),
);
final postCheckForUpdate = stopwatch.elapsed.inMilliseconds;
print(
'debug: time _checkForUpdate: ${postCheckForUpdate - preCheckForUpdate} ms',
);
}

final int? exitCode;
Expand Down Expand Up @@ -433,6 +463,9 @@ Ask questions, get support at https://github.com/leancodepl/patrol/discussions''

/// Checks if the current version (set by the build runner on the version.dart
/// file) is the most recent one. If not, shows a prompt to the user.
///
/// This method gets data from the network, so it should not block the
/// critical path.
Future<void> _checkForUpdate(String? commandName) async {
if (commandName == 'update' || commandName == 'doctor') {
return;
Expand Down
Loading