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

Unified 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, 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer_cli/pubspec.yaml » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer_cli/lib/src/build_mode.dart
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index aa7afd476ad83e75815682dafde9dad89c3a15e6..ab6f1cd8440eb975e56738572622dd0f66b66103 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -29,8 +29,11 @@ import 'package:analyzer_cli/src/error_formatter.dart';
import 'package:analyzer_cli/src/error_severity.dart';
import 'package:analyzer_cli/src/options.dart';
import 'package:bazel_worker/bazel_worker.dart';
+import 'package:collection/collection.dart';
+import 'package:convert/convert.dart';
import 'package:front_end/src/base/performace_logger.dart';
import 'package:front_end/src/byte_store/byte_store.dart';
+import 'package:front_end/src/byte_store/cache.dart';
/**
* Persistent Bazel worker.
@@ -39,13 +42,17 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
final ResourceProvider resourceProvider;
final PerformanceLog logger = new PerformanceLog(null);
final String dartSdkPath;
+ WorkerPackageBundleCache packageBundleCache;
final StringBuffer errorBuffer = new StringBuffer();
final StringBuffer outBuffer = new StringBuffer();
AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection,
{this.dartSdkPath})
- : super(connection: connection);
+ : super(connection: connection) {
+ packageBundleCache = new WorkerPackageBundleCache(
+ resourceProvider, logger, 256 * 1024 * 1024);
+ }
factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider,
{io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) {
@@ -58,10 +65,13 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
/**
* Performs analysis with given [options].
*/
- Future<Null> analyze(CommandLineOptions options) async {
+ Future<Null> analyze(
+ CommandLineOptions options, Map<String, WorkerInput> inputs) async {
+ var packageBundleProvider =
+ new WorkerPackageBundleProvider(packageBundleCache, inputs);
var buildMode = new BuildMode(
resourceProvider, options, new AnalysisStats(),
- logger: logger);
+ logger: logger, packageBundleProvider: packageBundleProvider);
await buildMode.analyze();
AnalysisEngine.instance.clearCaches();
}
@@ -75,6 +85,12 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
errorBuffer.clear();
outBuffer.clear();
try {
+ // Prepare inputs with their digests.
+ Map<String, WorkerInput> inputs = {};
+ for (var input in request.inputs) {
+ inputs[input.path] = new WorkerInput(input.path, input.digest);
+ }
+
// Add in the dart-sdk argument if `dartSdkPath` is not null,
// otherwise it will try to find the currently installed sdk.
var arguments = request.arguments.toList();
@@ -82,13 +98,15 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
!arguments.any((arg) => arg.startsWith('--dart-sdk'))) {
arguments.add('--dart-sdk=$dartSdkPath');
}
+
// Prepare options.
CommandLineOptions options =
CommandLineOptions.parse(arguments, printAndFail: (String msg) {
throw new ArgumentError(msg);
});
+
// Analyze and respond.
- await analyze(options);
+ await analyze(options, inputs);
String msg = _getErrorOutputBuffersText();
return new WorkResponse()
..exitCode = EXIT_CODE_OK
@@ -136,6 +154,7 @@ class BuildMode {
final CommandLineOptions options;
final AnalysisStats stats;
final PerformanceLog logger;
+ final PackageBundleProvider packageBundleProvider;
SummaryDataStore summaryDataStore;
AnalysisOptions analysisOptions;
@@ -150,8 +169,10 @@ class BuildMode {
final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{};
BuildMode(this.resourceProvider, this.options, this.stats,
- {PerformanceLog logger})
- : logger = logger ?? new PerformanceLog(null);
+ {PerformanceLog logger, PackageBundleProvider packageBundleProvider})
+ : logger = logger ?? new PerformanceLog(null),
+ packageBundleProvider = packageBundleProvider ??
+ new DirectPackageBundleProvider(resourceProvider);
bool get _shouldOutputSummary =>
options.buildSummaryOutput != null ||
@@ -303,8 +324,7 @@ class BuildMode {
// Adds a bundle at `path` to `summaryDataStore`.
PackageBundle addBundle(String path) {
- var bundle =
- new PackageBundle.fromBuffer(new io.File(path).readAsBytesSync());
+ PackageBundle bundle = packageBundleProvider.get(path);
summaryDataStore.addBundle(path, bundle);
return bundle;
}
@@ -467,6 +487,21 @@ class BuildMode {
}
}
+/**
+ * [PackageBundleProvider] that always reads from the [ResourceProvider].
+ */
+class DirectPackageBundleProvider implements PackageBundleProvider {
+ final ResourceProvider resourceProvider;
+
+ DirectPackageBundleProvider(this.resourceProvider);
+
+ @override
+ PackageBundle get(String path) {
+ var bytes = new io.File(path).readAsBytesSync();
+ return new PackageBundle.fromBuffer(bytes);
+ }
+}
+
/**
* Instances of the class [ExplicitSourceResolver] map URIs to files on disk
* using a fixed mapping provided at construction time.
@@ -510,3 +545,109 @@ class ExplicitSourceResolver extends UriResolver {
return pathToUriMap;
}
}
+
+/**
+ * Provider for [PackageBundle]s by file paths.
+ */
+abstract class PackageBundleProvider {
+ /**
+ * Return the [PackageBundle] for the file with the given [path].
+ */
+ PackageBundle get(String path);
+}
+
+/**
+ * Worker input.
+ *
+ * Bazel does not specify the format of the digest, so we cannot assume that
+ * the digest itself is enough to uniquely identify inputs. So, we use a pair
+ * of path + digest.
+ */
+class WorkerInput {
+ static const _digestEquality = const ListEquality<int>();
+
+ final String path;
+ final List<int> digest;
+
+ WorkerInput(this.path, this.digest);
+
+ @override
+ int get hashCode => _digestEquality.hash(digest);
+
+ @override
+ bool operator ==(Object other) {
+ return other is WorkerInput &&
+ other.path == path &&
+ _digestEquality.equals(other.digest, digest);
+ }
+
+ @override
+ String toString() => '$path @ ${hex.encode(digest)}';
+}
+
+/**
+ * Value object for [WorkerPackageBundleCache].
+ */
+class WorkerPackageBundle {
+ final List<int> bytes;
+ final PackageBundle bundle;
+
+ WorkerPackageBundle(this.bytes, this.bundle);
+
+ /**
+ * Approximation of a bundle size in memory.
+ */
+ int get size => bytes.length * 3;
+}
+
+/**
+ * Cache of [PackageBundle]s.
+ */
+class WorkerPackageBundleCache {
+ final ResourceProvider resourceProvider;
+ final PerformanceLog logger;
+ final Cache<WorkerInput, WorkerPackageBundle> _cache;
+
+ WorkerPackageBundleCache(this.resourceProvider, this.logger, int maxSizeBytes)
+ : _cache = new Cache<WorkerInput, WorkerPackageBundle>(
+ maxSizeBytes, (value) => value.size);
+
+ /**
+ * Get the [PackageBundle] from the file with the given [path] in the context
+ * of the given worker [inputs].
+ */
+ PackageBundle get(Map<String, WorkerInput> inputs, String path) {
+ WorkerInput input = inputs[path];
+
+ // The input must be not null, otherwise we're not expected to read
+ // this file, but we check anyway to be safe.
+ if (input == null) {
+ logger.writeln('Read $path outside of the inputs.');
+ var bytes = resourceProvider.getFile(path).readAsBytesSync();
+ return new PackageBundle.fromBuffer(bytes);
+ }
+
+ return _cache.get(input, () {
+ logger.writeln('Read $input.');
+ var bytes = resourceProvider.getFile(path).readAsBytesSync();
+ var bundle = new PackageBundle.fromBuffer(bytes);
+ return new WorkerPackageBundle(bytes, bundle);
+ }).bundle;
+ }
+}
+
+/**
+ * [PackageBundleProvider] that reads from [WorkerPackageBundleCache] using
+ * the request specific [inputs].
+ */
+class WorkerPackageBundleProvider implements PackageBundleProvider {
+ final WorkerPackageBundleCache cache;
+ final Map<String, WorkerInput> inputs;
+
+ WorkerPackageBundleProvider(this.cache, this.inputs);
+
+ @override
+ PackageBundle get(String path) {
+ return cache.get(inputs, path);
+ }
+}
« 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