[dart2js] Fix resource usage of binary to json converter tool.

Improves binary -> json conversion from >900s to ~52s for a large program.

This change contains 2 major fixes:
1) Make element name deduping be O(1) rather than O(n) by using a hash map. I didn't calculate how many duplicated names there were but this change improved the runtime of the conversion process by ~10x.

2) Use chunked string writing for the json encoding/file writing process. By doing chunked conversion with buffered writes we avoid having the entire JSON blob in memory and fewer midsized write operations end up being faster. Without this the program took ~87s for the same large program.

Change-Id: I2d3cddbbb96895b68086fce2256937d567beff11
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345940
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Nate Biggs 2024-01-12 17:11:51 +00:00 committed by Commit Queue
parent c46b63d2c7
commit 06adfb8d6b
2 changed files with 31 additions and 9 deletions

View file

@ -51,7 +51,33 @@ class ToJsonCommand extends Command<void> with PrintUsageException {
var json = AllInfoJsonCodec(isBackwardCompatible: isBackwardCompatible)
.encode(info);
String outputFilename = args['out'] ?? '$filename.json';
File(outputFilename)
.writeAsStringSync(const JsonEncoder.withIndent(" ").convert(json));
final sink = File(outputFilename).openWrite();
final converterSink = const JsonEncoder.withIndent(" ")
.startChunkedConversion(_BufferedStringOutputSink(sink));
converterSink.add(json);
converterSink.close();
await sink.close();
}
}
class _BufferedStringOutputSink implements Sink<String> {
StringBuffer buffer = StringBuffer();
final StringSink outputSink;
static const int _maxLength = 1024 * 1024 * 500;
_BufferedStringOutputSink(this.outputSink);
@override
void add(String data) {
buffer.write(data);
if (buffer.length > _maxLength) {
outputSink.write(buffer.toString());
buffer.clear();
}
}
@override
void close() {
outputSink.write(buffer.toString());
}
}

View file

@ -346,7 +346,7 @@ class AllInfoToJsonConverter extends Converter<AllInfo, Map>
final bool isBackwardCompatible;
final Map<Info, Id> ids = HashMap<Info, Id>();
final Set<String> usedIds = <String>{};
final Map<String, int> idCounter = <String, int>{};
AllInfoToJsonConverter({this.isBackwardCompatible = false});
@ -373,14 +373,10 @@ class AllInfoToJsonConverter extends Converter<AllInfo, Map>
name = longName(info, useLibraryUri: true, forId: true);
}
Id id = Id(info.kind, name);
// longName isn't guaranteed to create unique serializedIds for some info
// constructs (such as closures), so we disambiguate here.
int count = 0;
while (!usedIds.add(id.serializedId)) {
id = Id(info.kind, '$name%${count++}');
}
final count = idCounter.update(name, (v) => v + 1, ifAbsent: () => 0);
final id = Id(info.kind, count == 0 ? name : '$name%$count');
return ids[info] = id;
}