Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: pkg/analyzer_cli/lib/src/build_mode.dart

Issue 3006543002: Cache PackageBundle instances in AnalyzerWorkerLoop and reuse for multiple requests. (Closed)
Patch Set: Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer_cli/pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library analyzer_cli.src.build_mode; 5 library analyzer_cli.src.build_mode;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:io' as io; 8 import 'dart:io' as io;
9 9
10 import 'package:analyzer/error/error.dart'; 10 import 'package:analyzer/error/error.dart';
(...skipping 11 matching lines...) Expand all
22 import 'package:analyzer/src/summary/link.dart'; 22 import 'package:analyzer/src/summary/link.dart';
23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; 23 import 'package:analyzer/src/summary/package_bundle_reader.dart';
24 import 'package:analyzer/src/summary/summarize_ast.dart'; 24 import 'package:analyzer/src/summary/summarize_ast.dart';
25 import 'package:analyzer/src/summary/summarize_elements.dart'; 25 import 'package:analyzer/src/summary/summarize_elements.dart';
26 import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk; 26 import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk;
27 import 'package:analyzer_cli/src/driver.dart'; 27 import 'package:analyzer_cli/src/driver.dart';
28 import 'package:analyzer_cli/src/error_formatter.dart'; 28 import 'package:analyzer_cli/src/error_formatter.dart';
29 import 'package:analyzer_cli/src/error_severity.dart'; 29 import 'package:analyzer_cli/src/error_severity.dart';
30 import 'package:analyzer_cli/src/options.dart'; 30 import 'package:analyzer_cli/src/options.dart';
31 import 'package:bazel_worker/bazel_worker.dart'; 31 import 'package:bazel_worker/bazel_worker.dart';
32 import 'package:collection/collection.dart';
33 import 'package:convert/convert.dart';
32 import 'package:front_end/src/base/performace_logger.dart'; 34 import 'package:front_end/src/base/performace_logger.dart';
33 import 'package:front_end/src/byte_store/byte_store.dart'; 35 import 'package:front_end/src/byte_store/byte_store.dart';
36 import 'package:front_end/src/byte_store/cache.dart';
34 37
35 /** 38 /**
36 * Persistent Bazel worker. 39 * Persistent Bazel worker.
37 */ 40 */
38 class AnalyzerWorkerLoop extends AsyncWorkerLoop { 41 class AnalyzerWorkerLoop extends AsyncWorkerLoop {
39 final ResourceProvider resourceProvider; 42 final ResourceProvider resourceProvider;
40 final PerformanceLog logger = new PerformanceLog(null); 43 final PerformanceLog logger = new PerformanceLog(null);
41 final String dartSdkPath; 44 final String dartSdkPath;
45 WorkerPackageBundleCache packageBundleCache;
42 46
43 final StringBuffer errorBuffer = new StringBuffer(); 47 final StringBuffer errorBuffer = new StringBuffer();
44 final StringBuffer outBuffer = new StringBuffer(); 48 final StringBuffer outBuffer = new StringBuffer();
45 49
46 AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection, 50 AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection,
47 {this.dartSdkPath}) 51 {this.dartSdkPath})
48 : super(connection: connection); 52 : super(connection: connection) {
53 packageBundleCache = new WorkerPackageBundleCache(
54 resourceProvider, logger, 256 * 1024 * 1024);
55 }
49 56
50 factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider, 57 factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider,
51 {io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) { 58 {io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) {
52 AsyncWorkerConnection connection = new StdAsyncWorkerConnection( 59 AsyncWorkerConnection connection = new StdAsyncWorkerConnection(
53 inputStream: stdinStream, outputStream: stdoutStream); 60 inputStream: stdinStream, outputStream: stdoutStream);
54 return new AnalyzerWorkerLoop(resourceProvider, connection, 61 return new AnalyzerWorkerLoop(resourceProvider, connection,
55 dartSdkPath: dartSdkPath); 62 dartSdkPath: dartSdkPath);
56 } 63 }
57 64
58 /** 65 /**
59 * Performs analysis with given [options]. 66 * Performs analysis with given [options].
60 */ 67 */
61 Future<Null> analyze(CommandLineOptions options) async { 68 Future<Null> analyze(
69 CommandLineOptions options, Map<String, WorkerInput> inputs) async {
70 var packageBundleProvider =
71 new WorkerPackageBundleProvider(packageBundleCache, inputs);
62 var buildMode = new BuildMode( 72 var buildMode = new BuildMode(
63 resourceProvider, options, new AnalysisStats(), 73 resourceProvider, options, new AnalysisStats(),
64 logger: logger); 74 logger: logger, packageBundleProvider: packageBundleProvider);
65 await buildMode.analyze(); 75 await buildMode.analyze();
66 AnalysisEngine.instance.clearCaches(); 76 AnalysisEngine.instance.clearCaches();
67 } 77 }
68 78
69 /** 79 /**
70 * Perform a single loop step. 80 * Perform a single loop step.
71 */ 81 */
72 @override 82 @override
73 Future<WorkResponse> performRequest(WorkRequest request) async { 83 Future<WorkResponse> performRequest(WorkRequest request) async {
74 return logger.run('Perform request', () async { 84 return logger.run('Perform request', () async {
75 errorBuffer.clear(); 85 errorBuffer.clear();
76 outBuffer.clear(); 86 outBuffer.clear();
77 try { 87 try {
88 // Prepare inputs with their digests.
89 Map<String, WorkerInput> inputs = {};
90 for (var input in request.inputs) {
91 inputs[input.path] = new WorkerInput(input.path, input.digest);
92 }
93
78 // Add in the dart-sdk argument if `dartSdkPath` is not null, 94 // Add in the dart-sdk argument if `dartSdkPath` is not null,
79 // otherwise it will try to find the currently installed sdk. 95 // otherwise it will try to find the currently installed sdk.
80 var arguments = request.arguments.toList(); 96 var arguments = request.arguments.toList();
81 if (dartSdkPath != null && 97 if (dartSdkPath != null &&
82 !arguments.any((arg) => arg.startsWith('--dart-sdk'))) { 98 !arguments.any((arg) => arg.startsWith('--dart-sdk'))) {
83 arguments.add('--dart-sdk=$dartSdkPath'); 99 arguments.add('--dart-sdk=$dartSdkPath');
84 } 100 }
101
85 // Prepare options. 102 // Prepare options.
86 CommandLineOptions options = 103 CommandLineOptions options =
87 CommandLineOptions.parse(arguments, printAndFail: (String msg) { 104 CommandLineOptions.parse(arguments, printAndFail: (String msg) {
88 throw new ArgumentError(msg); 105 throw new ArgumentError(msg);
89 }); 106 });
107
90 // Analyze and respond. 108 // Analyze and respond.
91 await analyze(options); 109 await analyze(options, inputs);
92 String msg = _getErrorOutputBuffersText(); 110 String msg = _getErrorOutputBuffersText();
93 return new WorkResponse() 111 return new WorkResponse()
94 ..exitCode = EXIT_CODE_OK 112 ..exitCode = EXIT_CODE_OK
95 ..output = msg; 113 ..output = msg;
96 } catch (e, st) { 114 } catch (e, st) {
97 String msg = _getErrorOutputBuffersText(); 115 String msg = _getErrorOutputBuffersText();
98 msg += '$e\n$st'; 116 msg += '$e\n$st';
99 return new WorkResponse() 117 return new WorkResponse()
100 ..exitCode = EXIT_CODE_ERROR 118 ..exitCode = EXIT_CODE_ERROR
101 ..output = msg; 119 ..output = msg;
(...skipping 27 matching lines...) Expand all
129 } 147 }
130 148
131 /** 149 /**
132 * Analyzer used when the "--build-mode" option is supplied. 150 * Analyzer used when the "--build-mode" option is supplied.
133 */ 151 */
134 class BuildMode { 152 class BuildMode {
135 final ResourceProvider resourceProvider; 153 final ResourceProvider resourceProvider;
136 final CommandLineOptions options; 154 final CommandLineOptions options;
137 final AnalysisStats stats; 155 final AnalysisStats stats;
138 final PerformanceLog logger; 156 final PerformanceLog logger;
157 final PackageBundleProvider packageBundleProvider;
139 158
140 SummaryDataStore summaryDataStore; 159 SummaryDataStore summaryDataStore;
141 AnalysisOptions analysisOptions; 160 AnalysisOptions analysisOptions;
142 Map<Uri, File> uriToFileMap; 161 Map<Uri, File> uriToFileMap;
143 final List<Source> explicitSources = <Source>[]; 162 final List<Source> explicitSources = <Source>[];
144 final List<PackageBundle> unlinkedBundles = <PackageBundle>[]; 163 final List<PackageBundle> unlinkedBundles = <PackageBundle>[];
145 164
146 AnalysisDriver analysisDriver; 165 AnalysisDriver analysisDriver;
147 166
148 PackageBundleAssembler assembler; 167 PackageBundleAssembler assembler;
149 final Set<Source> processedSources = new Set<Source>(); 168 final Set<Source> processedSources = new Set<Source>();
150 final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{}; 169 final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{};
151 170
152 BuildMode(this.resourceProvider, this.options, this.stats, 171 BuildMode(this.resourceProvider, this.options, this.stats,
153 {PerformanceLog logger}) 172 {PerformanceLog logger, PackageBundleProvider packageBundleProvider})
154 : logger = logger ?? new PerformanceLog(null); 173 : logger = logger ?? new PerformanceLog(null),
174 packageBundleProvider = packageBundleProvider ??
175 new DirectPackageBundleProvider(resourceProvider);
155 176
156 bool get _shouldOutputSummary => 177 bool get _shouldOutputSummary =>
157 options.buildSummaryOutput != null || 178 options.buildSummaryOutput != null ||
158 options.buildSummaryOutputSemantic != null; 179 options.buildSummaryOutputSemantic != null;
159 180
160 /** 181 /**
161 * Perform package analysis according to the given [options]. 182 * Perform package analysis according to the given [options].
162 */ 183 */
163 Future<ErrorSeverity> analyze() async { 184 Future<ErrorSeverity> analyze() async {
164 return await logger.runAsync('Analyze', () async { 185 return await logger.runAsync('Analyze', () async {
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
296 } 317 }
297 return maxSeverity; 318 return maxSeverity;
298 } 319 }
299 320
300 void _createAnalysisDriver() { 321 void _createAnalysisDriver() {
301 // Read the summaries. 322 // Read the summaries.
302 summaryDataStore = new SummaryDataStore(<String>[]); 323 summaryDataStore = new SummaryDataStore(<String>[]);
303 324
304 // Adds a bundle at `path` to `summaryDataStore`. 325 // Adds a bundle at `path` to `summaryDataStore`.
305 PackageBundle addBundle(String path) { 326 PackageBundle addBundle(String path) {
306 var bundle = 327 PackageBundle bundle = packageBundleProvider.get(path);
307 new PackageBundle.fromBuffer(new io.File(path).readAsBytesSync());
308 summaryDataStore.addBundle(path, bundle); 328 summaryDataStore.addBundle(path, bundle);
309 return bundle; 329 return bundle;
310 } 330 }
311 331
312 int numInputs = options.buildSummaryInputs.length + 332 int numInputs = options.buildSummaryInputs.length +
313 options.buildSummaryUnlinkedInputs.length; 333 options.buildSummaryUnlinkedInputs.length;
314 logger.run('Add $numInputs input summaries', () { 334 logger.run('Add $numInputs input summaries', () {
315 for (var path in options.buildSummaryInputs) { 335 for (var path in options.buildSummaryInputs) {
316 var bundle = addBundle(path); 336 var bundle = addBundle(path);
317 if (bundle.linkedLibraryUris.isEmpty && 337 if (bundle.linkedLibraryUris.isEmpty &&
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 StringSink sink = options.machineFormat ? errorSink : outSink; 481 StringSink sink = options.machineFormat ? errorSink : outSink;
462 sink.write(buffer); 482 sink.write(buffer);
463 } else { 483 } else {
464 new io.File(outputPath).writeAsStringSync(buffer.toString()); 484 new io.File(outputPath).writeAsStringSync(buffer.toString());
465 } 485 }
466 }); 486 });
467 } 487 }
468 } 488 }
469 489
470 /** 490 /**
491 * [PackageBundleProvider] that always reads from the [ResourceProvider].
492 */
493 class DirectPackageBundleProvider implements PackageBundleProvider {
494 final ResourceProvider resourceProvider;
495
496 DirectPackageBundleProvider(this.resourceProvider);
497
498 @override
499 PackageBundle get(String path) {
500 var bytes = new io.File(path).readAsBytesSync();
501 return new PackageBundle.fromBuffer(bytes);
502 }
503 }
504
505 /**
471 * Instances of the class [ExplicitSourceResolver] map URIs to files on disk 506 * Instances of the class [ExplicitSourceResolver] map URIs to files on disk
472 * using a fixed mapping provided at construction time. 507 * using a fixed mapping provided at construction time.
473 */ 508 */
474 class ExplicitSourceResolver extends UriResolver { 509 class ExplicitSourceResolver extends UriResolver {
475 final Map<Uri, File> uriToFileMap; 510 final Map<Uri, File> uriToFileMap;
476 final Map<String, Uri> pathToUriMap; 511 final Map<String, Uri> pathToUriMap;
477 512
478 /** 513 /**
479 * Construct an [ExplicitSourceResolver] based on the given [uriToFileMap]. 514 * Construct an [ExplicitSourceResolver] based on the given [uriToFileMap].
480 */ 515 */
(...skipping 22 matching lines...) Expand all
503 * Build the inverse mapping of [uriToSourceMap]. 538 * Build the inverse mapping of [uriToSourceMap].
504 */ 539 */
505 static Map<String, Uri> _computePathToUriMap(Map<Uri, File> uriToSourceMap) { 540 static Map<String, Uri> _computePathToUriMap(Map<Uri, File> uriToSourceMap) {
506 Map<String, Uri> pathToUriMap = <String, Uri>{}; 541 Map<String, Uri> pathToUriMap = <String, Uri>{};
507 uriToSourceMap.forEach((Uri uri, File file) { 542 uriToSourceMap.forEach((Uri uri, File file) {
508 pathToUriMap[file.path] = uri; 543 pathToUriMap[file.path] = uri;
509 }); 544 });
510 return pathToUriMap; 545 return pathToUriMap;
511 } 546 }
512 } 547 }
548
549 /**
550 * Provider for [PackageBundle]s by file paths.
551 */
552 abstract class PackageBundleProvider {
553 /**
554 * Return the [PackageBundle] for the file with the given [path].
555 */
556 PackageBundle get(String path);
557 }
558
559 /**
560 * Worker input.
561 *
562 * Bazel does not specify the format of the digest, so we cannot assume that
563 * the digest itself is enough to uniquely identify inputs. So, we use a pair
564 * of path + digest.
565 */
566 class WorkerInput {
567 static const _digestEquality = const ListEquality<int>();
568
569 final String path;
570 final List<int> digest;
571
572 WorkerInput(this.path, this.digest);
573
574 @override
575 int get hashCode => _digestEquality.hash(digest);
576
577 @override
578 bool operator ==(Object other) {
579 return other is WorkerInput &&
580 other.path == path &&
581 _digestEquality.equals(other.digest, digest);
582 }
583
584 @override
585 String toString() => '$path @ ${hex.encode(digest)}';
586 }
587
588 /**
589 * Value object for [WorkerPackageBundleCache].
590 */
591 class WorkerPackageBundle {
592 final List<int> bytes;
593 final PackageBundle bundle;
594
595 WorkerPackageBundle(this.bytes, this.bundle);
596
597 /**
598 * Approximation of a bundle size in memory.
599 */
600 int get size => bytes.length * 3;
601 }
602
603 /**
604 * Cache of [PackageBundle]s.
605 */
606 class WorkerPackageBundleCache {
607 final ResourceProvider resourceProvider;
608 final PerformanceLog logger;
609 final Cache<WorkerInput, WorkerPackageBundle> _cache;
610
611 WorkerPackageBundleCache(this.resourceProvider, this.logger, int maxSizeBytes)
612 : _cache = new Cache<WorkerInput, WorkerPackageBundle>(
613 maxSizeBytes, (value) => value.size);
614
615 /**
616 * Get the [PackageBundle] from the file with the given [path] in the context
617 * of the given worker [inputs].
618 */
619 PackageBundle get(Map<String, WorkerInput> inputs, String path) {
620 WorkerInput input = inputs[path];
621
622 // The input must be not null, otherwise we're not expected to read
623 // this file, but we check anyway to be safe.
624 if (input == null) {
625 logger.writeln('Read $path outside of the inputs.');
626 var bytes = resourceProvider.getFile(path).readAsBytesSync();
627 return new PackageBundle.fromBuffer(bytes);
628 }
629
630 return _cache.get(input, () {
631 logger.writeln('Read $input.');
632 var bytes = resourceProvider.getFile(path).readAsBytesSync();
633 var bundle = new PackageBundle.fromBuffer(bytes);
634 return new WorkerPackageBundle(bytes, bundle);
635 }).bundle;
636 }
637 }
638
639 /**
640 * [PackageBundleProvider] that reads from [WorkerPackageBundleCache] using
641 * the request specific [inputs].
642 */
643 class WorkerPackageBundleProvider implements PackageBundleProvider {
644 final WorkerPackageBundleCache cache;
645 final Map<String, WorkerInput> inputs;
646
647 WorkerPackageBundleProvider(this.cache, this.inputs);
648
649 @override
650 PackageBundle get(String path) {
651 return cache.get(inputs, path);
652 }
653 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer_cli/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698