[analyzer] Less duplicated line starts data with multiple contexts

When having multiple contexts we have duplicates of some things.
For instance line starts data.
This CL deduplicates some of it, and makes use of `Uint16List` and
`Uint32List` as appropriate (some where regular `List<int>`s before),
reducing the heap usage for analyzing `flutter/flutter` (80+ contexts)
when analyzing with an empty cache with about 165 MB.

Details:

```
_GrowableList (dart:core) (bytes):
Difference at 95.0% confidence
   -451040.00 +/- 0.00
   -0.79% +/- 0.00%

_Uint16List (dart:typed_data) (bytes):
Difference at 95.0% confidence
   6558032.00 +/- 0.00
   696.69% +/- 0.00%

_List (dart:core) (bytes):
Difference at 95.0% confidence
   -70840960.00 +/- 231371.19
   -9.58% +/- 0.03%

_Uint32List (dart:typed_data) (bytes):
Difference at 95.0% confidence
   -103560448.00 +/- 2829564.24
   -23.56% +/- 0.64%

heapUsage:
Difference at 95.0% confidence
   -173020528.00 +/- 7787712.50
   -4.78% +/- 0.21%
```
Change-Id: I49e23dde14a2faa7c8af94515b0ad1c669867c78
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/324501
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Jens Johansen 2023-09-07 07:11:42 +00:00 committed by Commit Queue
parent 36d6676833
commit 2bbf99351e
3 changed files with 43 additions and 26 deletions

View file

@ -2,6 +2,8 @@
// for details. 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:typed_data';
import 'package:_fe_analyzer_shared/src/scanner/errors.dart'
show translateErrorToken;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' as fasta;
@ -28,6 +30,8 @@ export 'package:analyzer/src/dart/error/syntactic_errors.dart';
/// have any context, so it always resolves such conflicts by scanning the
/// longest possible token.
class Scanner {
static final Uint8List _lineStartsZero = Uint8List(0);
final Source source;
/// The text to be scanned.
@ -47,9 +51,7 @@ class Scanner {
/// The flag specifying whether documentation comments should be parsed.
bool _preserveComments = true;
final List<int> lineStarts = <int>[];
List<int>? _lineStarts;
late final Token firstToken;
Version? _overrideVersion;
@ -72,9 +74,7 @@ class Scanner {
}
Scanner._(
this.source, this._contents, this._readerOffset, this._errorListener) {
lineStarts.add(0);
}
this.source, this._contents, this._readerOffset, this._errorListener);
/// The features associated with this scanner.
///
@ -86,6 +86,8 @@ class Scanner {
/// Use [configureFeatures] to set the features.
FeatureSet get featureSet => _featureSet;
List<int> get lineStarts => _lineStarts ?? _lineStartsZero;
/// The language version override specified for this compilation unit using a
/// token like '// @dart = 2.7', or `null` if no override is specified.
Version? get overrideVersion => _overrideVersion;
@ -116,18 +118,6 @@ class Scanner {
);
}
void setSourceStart(int line, int column) {
int offset = _readerOffset;
if (line < 1 || column < 1 || offset < 0 || (line + column - 2) >= offset) {
return;
}
lineStarts.removeAt(0);
for (int i = 2; i < line; i++) {
lineStarts.add(1);
}
lineStarts.add(offset - column + 1);
}
/// The fasta parser handles error tokens produced by the scanner
/// but the old parser used by angular does not
/// and expects that scanner errors to be reported by this method.
@ -138,13 +128,15 @@ class Scanner {
includeComments: _preserveComments,
languageVersionChanged: _languageVersionChanged);
// fasta pretends there is an additional line at EOF
result.lineStarts.removeLast();
// fasta pretends there is an additional line at EOF so we skip the last one.
if (result.lineStarts.last > 65535) {
Uint32List list = _lineStarts = Uint32List(result.lineStarts.length - 1);
list.setRange(0, result.lineStarts.length - 1, result.lineStarts);
} else {
Uint16List list = _lineStarts = Uint16List(result.lineStarts.length - 1);
list.setRange(0, result.lineStarts.length - 1, result.lineStarts);
}
// for compatibility, there is already a first entry in lineStarts
result.lineStarts.removeAt(0);
lineStarts.addAll(result.lineStarts);
fasta.Token token = result.tokens;
// The fasta parser handles error tokens produced by the scanner

View file

@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart' as graph
show DependencyWalker, Node;
@ -1923,7 +1924,14 @@ class _File {
void _readFileDeclarationsFromBytes(List<int> bytes) {
var idlFile = idl.AvailableFile.fromBuffer(bytes);
lineStarts = idlFile.lineStarts.toList();
lineStarts = idlFile.lineStarts;
if (lineStarts.last > 65535) {
Uint32List list = lineStarts = Uint32List(lineStarts.length);
list.setRange(0, lineStarts.length, lineStarts);
} else {
Uint16List list = lineStarts = Uint16List(lineStarts.length);
list.setRange(0, lineStarts.length, lineStarts);
}
lineInfo = LineInfo(lineStarts);
isLibrary = idlFile.isLibrary;

View file

@ -1858,7 +1858,8 @@ class _InfoUnit {
return _InfoUnit._(
codeOffset: reader.readUInt30(),
codeLength: reader.readUInt30(),
lineStarts: reader.readUInt30List(),
// Having duplicated line-starts adds up --- deduplicate if possible.
lineStarts: _readUint30ListPossiblyFromCache(cache, reader),
libraryName: _InfoLibraryName(reader),
libraryConstantOffsets: reader.readUInt30List(),
docComment: reader.readStringUtf8(),
@ -1929,6 +1930,22 @@ class _InfoUnit {
required this.mixinDeclarations,
required this.topLevelVariable,
});
static Uint32List _readUint30ListPossiblyFromCache(
InfoDeclarationStore cache, SummaryDataReader reader) {
final initialOffset = reader.offset;
final cacheKey = cache.createKey(reader, initialOffset);
final cachedLineStarts =
cache.get<Uint32List>(reader, cacheKey, initialOffset);
if (cachedLineStarts != null) {
return cachedLineStarts;
} else {
// Add to cache.
var lineStarts = reader.readUInt30List();
cache.put(reader, cacheKey, initialOffset, lineStarts);
return lineStarts;
}
}
}
class _OffsetsApplier extends _OffsetsAstVisitor {