Skip to content

Commit a676b7f

Browse files
bwilkersonCommit Queue
authored andcommitted
Improve the performance of the bulk fix processor
This improvement was found by Jens. The bulk fix processor needs to be able to revert a partial set of edits that can't be completed because of a conflicting pre-existing edit. In order to do that efficiently it needs to know whether there are any edits to be reverted, which it used to do by computing a hash from the edits both before and after running a correction producer. Computing the hashes is expensive. This CL changes the detection to use a modification counter that's incremented any time new edits are added, removing the computation of the hash values. I don't yet have a benchmark for the case that uncovered the issue, but I do have code that generates a file containing 10,000 violations of the prefer_single_quotes lint, requests bulk fix results 5 times to warm up the VM, then requests fixes 5 more times and prints an average (the times between runs are fairly close, so the standard deviation is small). The code uses the legacy protocol to request the fixes, but it doesn't execute any of the code in dartdev.) Before this change the average was 20670.6 ms. After this change the average ise 13979.0 ms. This represents a 32% improvement. Change-Id: I74c079bef8e095acf4ad36de6d0c0241934f79a5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425221 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Reviewed-by: Jens Johansen <[email protected]>
1 parent 108e40f commit a676b7f

File tree

3 files changed

+22
-31
lines changed

3 files changed

+22
-31
lines changed

pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -862,12 +862,11 @@ class BulkFixProcessor {
862862
CorrectionProducer producer,
863863
String codeName,
864864
) async {
865-
int computeChangeHash() => (builder as ChangeBuilderImpl).changeHash;
866-
867-
var oldHash = computeChangeHash();
865+
var oldCount = (builder as ChangeBuilderImpl).modificationCount;
866+
// Apply the producer, which might re-assign the `builder`.
868867
await _applyProducer(producer);
869-
var newHash = computeChangeHash();
870-
if (newHash != oldHash) {
868+
var newCount = (builder as ChangeBuilderImpl).modificationCount;
869+
if (newCount != oldCount) {
871870
changeMap.add(context.path, codeName.toLowerCase());
872871
}
873872
}

pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ class ChangeBuilderImpl implements ChangeBuilder {
5959
/// A map of absolute normalized path to YAML file edit builders.
6060
final Map<String, YamlFileEditBuilderImpl> _yamlFileEditBuilders = {};
6161

62+
/// The number of times that any of the file edit builders in this change have
63+
/// been modified.
64+
///
65+
/// A file builder is considered to be modified when the list of edits is
66+
/// modified in any way, such as by adding a new edit to the list. For Dart
67+
/// file edit builders this includes changes to the list of libraries that
68+
/// will be imported by creating edits at a later point.
69+
///
70+
/// This can be used, for example, to determine whether any edits were added
71+
/// by a given correction producer.
72+
int modificationCount = 0;
73+
6274
/// Initialize a newly created change builder. If the builder will be used to
6375
/// create changes for Dart files, then either a [session] or a [workspace]
6476
/// must be provided (but not both).
@@ -67,26 +79,6 @@ class ChangeBuilderImpl implements ChangeBuilder {
6779
: assert(session == null || workspace == null),
6880
workspace = workspace ?? _SingleSessionWorkspace(session!);
6981

70-
/// Return a hash value that will change when new edits have been added to
71-
/// this builder.
72-
int get changeHash {
73-
// The hash value currently ignores edits to import directives because
74-
// finalizing the builders needs to happen exactly once and this getter
75-
// needs to be invoked repeatedly.
76-
//
77-
// In addition, we should consider implementing our own hash function for
78-
// file edits because the `hashCode` defined for them might not be
79-
// sufficient to detect all changes to the list of edits.
80-
return Object.hashAll([
81-
for (var builder in _genericFileEditBuilders.values)
82-
if (builder.hasEdits) builder.fileEdit,
83-
for (var builder in _dartFileEditBuilders.values)
84-
if (builder.hasEdits) builder.fileEdit,
85-
for (var builder in _yamlFileEditBuilders.values)
86-
if (builder.hasEdits) builder.fileEdit,
87-
]);
88-
}
89-
9082
/// Return `true` if this builder has edits to be applied.
9183
bool get hasEdits {
9284
return _dartFileEditBuilders.isNotEmpty ||
@@ -270,6 +262,7 @@ class ChangeBuilderImpl implements ChangeBuilder {
270262
for (var entry in _yamlFileEditBuilders.entries) {
271263
copy._yamlFileEditBuilders[entry.key] = entry.value.copyWith(copy);
272264
}
265+
copy.modificationCount = modificationCount;
273266
return copy;
274267
}
275268

@@ -625,6 +618,7 @@ class FileEditBuilderImpl implements FileEditBuilder {
625618
var delta = _editDelta(edit);
626619
changeBuilder._updatePositions(edit.offset, delta);
627620
changeBuilder._lockedPositions.clear();
621+
changeBuilder.modificationCount++;
628622
}
629623

630624
/// Add the edit from the given [builder] to the edits associated with the

pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,8 +1228,8 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
12281228
}
12291229
}
12301230

1231-
void _writeSuperMemberInvocation(ExecutableElement element,
1232-
String memberName, List<FormalParameterElement> parameters) {
1231+
void _writeSuperMemberInvocation(ExecutableElement element, String memberName,
1232+
List<FormalParameterElement> parameters) {
12331233
var isOperator = element is MethodElement && element.isOperator;
12341234
write(isOperator ? ' ' : '.');
12351235
write(memberName);
@@ -1728,9 +1728,7 @@ class DartFileEditBuilderImpl extends FileEditBuilderImpl
17281728

17291729
// Queued change.
17301730
var importChange = (libraryChangeBuilder ?? this).librariesToImport[uri];
1731-
if (importChange != null) return true;
1732-
1733-
return false;
1731+
return importChange != null;
17341732
}
17351733

17361734
@override
@@ -2814,7 +2812,7 @@ class _LibraryImport implements Comparable<_LibraryImport> {
28142812
'${allHiddenNames.isNotEmpty ? 'hide ${allHiddenNames.join(', ')}' : ''};';
28152813
}
28162814

2817-
/// Ensures [name] is visible for this import.
2815+
/// Ensures that [name] is visible for this import.
28182816
///
28192817
/// If the import already has a show combinator, this name will be added.
28202818
/// If the import hides this name, it will be unhidden.

0 commit comments

Comments
 (0)