Don't encode strings up front; don't toString uris

Change kernel serialization so that
a) We don't UTF8 encode strings up front, but only as needed
   (if nothing else it makes it more obvious where the cost of stuff is)
b) Don't call toString on URIs to save them in a StringIndexer, only to
   parse the string later to get a URI back again.

This shaves a bit of the serialization time.
Timings from running `flutter test` in flutter/packages/flutter:

Without change:
```
02:14 +2438 ~18: All tests passed!
02:12 +2438 ~18: All tests passed!
02:14 +2438 ~18: All tests passed!
```

with change:
```
02:10 +2438 ~18: All tests passed!
02:11 +2438 ~18: All tests passed!
02:09 +2438 ~18: All tests passed!
```

 => -2.5% +/- 1.83615%

Change-Id: I03e651bbef23445fb1901452a4efcc732789a168
Reviewed-on: https://dart-review.googlesource.com/51302
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
This commit is contained in:
Jens Johansen 2018-05-02 07:42:40 +00:00 committed by commit-bot@chromium.org
parent 4a0ac85cc3
commit 47e9039512
3 changed files with 48 additions and 32 deletions

View file

@ -14,9 +14,9 @@ main() {
var summary = await summarize(['a.dart'], allSources);
var component = loadComponentFromBytes(summary);
// Note: the kernel representation always has an empty '' key in the map,
// Note: the kernel representation always has a null key in the map,
// but otherwise no other data is included here.
expect(component.uriToSource.keys.single, Uri.parse(""));
expect(component.uriToSource.keys.single, null);
});
test('summary includes declarations, but no method bodies', () async {

View file

@ -523,7 +523,9 @@ class BinaryBuilder {
Map<Uri, Source> uriToSource = <Uri, Source>{};
for (int i = 0; i < length; ++i) {
List<int> uriBytes = readByteList();
Uri uri = Uri.parse(const Utf8Decoder().convert(uriBytes));
Uri uri = uriBytes.isEmpty
? null
: Uri.parse(const Utf8Decoder().convert(uriBytes));
_sourceUriTable[i] = uri;
List<int> sourceCode = readByteList();
int lineCount = readUInt();

View file

@ -22,7 +22,7 @@ class BinaryPrinter implements Visitor<void>, BinarySink {
final TypeParameterIndexer _typeParameterIndexer = new TypeParameterIndexer();
final StringIndexer stringIndexer;
ConstantIndexer _constantIndexer;
final StringIndexer _sourceUriIndexer = new StringIndexer();
final UriIndexer _sourceUriIndexer = new UriIndexer();
final Set<Uri> _knownSourceUri = new Set<Uri>();
Map<LibraryDependency, int> _libraryDependencyIndex =
<LibraryDependency, int>{};
@ -103,15 +103,22 @@ class BinaryPrinter implements Visitor<void>, BinarySink {
_binaryOffsetForStringTable = getBufferOffset();
// Write the end offsets.
writeUInt30(indexer.numberOfStrings);
writeUInt30(indexer.index.length);
int endOffset = 0;
for (var entry in indexer.entries) {
endOffset += entry.utf8Bytes.length;
List<List<int>> data = new List.filled(indexer.index.length, null);
int i = 0;
Utf8Encoder utf8Encoder = const Utf8Encoder();
for (String key in indexer.index.keys) {
List<int> utf8Bytes = utf8Encoder.convert(key);
data[i] = utf8Bytes;
endOffset += utf8Bytes.length;
writeUInt30(endOffset);
i++;
}
// Write the UTF-8 encoded strings.
for (var entry in indexer.entries) {
writeBytes(entry.utf8Bytes);
for (var entry in data) {
writeBytes(entry);
}
}
@ -206,11 +213,11 @@ class BinaryPrinter implements Visitor<void>, BinarySink {
// Returns the new active file uri.
Uri writeUriReference(Uri uri) {
if (_knownSourceUri.contains(uri)) {
final int index = _sourceUriIndexer.put(uri == null ? "" : "$uri");
final int index = _sourceUriIndexer.put(uri);
writeUInt30(index);
return uri;
} else {
final int index = 0; // equivalent to index = _sourceUriIndexer[""];
final int index = 0; // equivalent to index = _sourceUriIndexer[null];
writeUInt30(index);
return null;
}
@ -487,19 +494,19 @@ class BinaryPrinter implements Visitor<void>, BinarySink {
void writeUriToSource(Map<Uri, Source> uriToSource) {
_binaryOffsetForSourceTable = getBufferOffset();
int length = _sourceUriIndexer.numberOfStrings;
int length = _sourceUriIndexer.index.length;
writeUInt32(length);
List<int> index = new List<int>(_sourceUriIndexer.entries.length);
List<int> index = new List<int>(length);
// Write data.
for (int i = 0; i < length; ++i) {
int i = 0;
Utf8Encoder utf8Encoder = const Utf8Encoder();
for (Uri uri in _sourceUriIndexer.index.keys) {
index[i] = getBufferOffset();
StringTableEntry uri = _sourceUriIndexer.entries[i];
Source source = uriToSource[Uri.parse(uri.value)] ??
new Source(<int>[], const <int>[]);
Source source = uriToSource[uri] ?? new Source(<int>[], const <int>[]);
writeByteList(uri.utf8Bytes);
writeByteList(utf8Encoder.convert(uri == null ? "" : "$uri"));
writeByteList(source.source);
List<int> lineStarts = source.lineStarts;
writeUInt30(lineStarts.length);
@ -508,6 +515,7 @@ class BinaryPrinter implements Visitor<void>, BinarySink {
writeUInt30(lineStart - previousLineStart);
previousLineStart = lineStart;
});
i++;
}
// Write index for random access.
@ -2101,29 +2109,17 @@ class TypeParameterIndexer {
int operator [](TypeParameter parameter) => index[parameter];
}
class StringTableEntry {
final String value;
final List<int> utf8Bytes;
StringTableEntry(String value)
: value = value,
utf8Bytes = const Utf8Encoder().convert(value);
}
class StringIndexer {
final List<StringTableEntry> entries = <StringTableEntry>[];
final LinkedHashMap<String, int> index = new LinkedHashMap<String, int>();
// Note the LinkedHashMap: The iteration order is important.
final Map<String, int> index = new LinkedHashMap<String, int>();
StringIndexer() {
put('');
}
int get numberOfStrings => index.length;
int put(String string) {
var result = index[string];
if (result == null) {
entries.add(new StringTableEntry(string));
result = index.length;
index[string] = result;
}
@ -2133,6 +2129,24 @@ class StringIndexer {
int operator [](String string) => index[string];
}
class UriIndexer {
// Note the LinkedHashMap: The iteration order is important.
final Map<Uri, int> index = new LinkedHashMap<Uri, int>();
UriIndexer() {
put(null);
}
int put(Uri uri) {
var result = index[uri];
if (result == null) {
result = index.length;
index[uri] = result;
}
return result;
}
}
/// Computes and stores the index of a library, class, or member within its
/// parent list.
class GlobalIndexer extends TreeVisitor {