Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 56cad89

Browse files
authored
Speed up first asset load by encoding asset manifest in binary rather than JSON (#113637)
1 parent d52e2de commit 56cad89

File tree

14 files changed

+2077
-919
lines changed

14 files changed

+2077
-919
lines changed

dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
// found in the LICENSE file.
44

55
import 'dart:convert';
6+
import 'dart:typed_data';
67

7-
import 'package:flutter/foundation.dart';
8-
import 'package:flutter/services.dart' show PlatformAssetBundle;
8+
import 'package:flutter/services.dart' show PlatformAssetBundle, StandardMessageCodec;
99
import 'package:flutter/widgets.dart';
1010

1111
import '../common.dart';
@@ -18,16 +18,14 @@ void main() async {
1818
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
1919
WidgetsFlutterBinding.ensureInitialized();
2020
final Stopwatch watch = Stopwatch();
21-
final PlatformAssetBundle bundle = PlatformAssetBundle();
2221

23-
final ByteData assetManifestBytes = await bundle.load('money_asset_manifest.json');
22+
final ByteData assetManifest = await loadAssetManifest();
23+
2424
watch.start();
2525
for (int i = 0; i < _kNumIterations; i++) {
26-
bundle.clear();
27-
final String json = utf8.decode(assetManifestBytes.buffer.asUint8List());
28-
// This is a test, so we don't need to worry about this rule.
26+
// This is effectively a test.
2927
// ignore: invalid_use_of_visible_for_testing_member
30-
await AssetImage.manifestParser(json);
28+
AssetImage.parseAssetManifest(assetManifest);
3129
}
3230
watch.stop();
3331

@@ -40,3 +38,49 @@ void main() async {
4038

4139
printer.printToStdout();
4240
}
41+
42+
final RegExp _extractRatioRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
43+
44+
Future<ByteData> loadAssetManifest() async {
45+
double parseScale(String key) {
46+
final Uri assetUri = Uri.parse(key);
47+
String directoryPath = '';
48+
if (assetUri.pathSegments.length > 1) {
49+
directoryPath = assetUri.pathSegments[assetUri.pathSegments.length - 2];
50+
}
51+
final Match? match = _extractRatioRegExp.firstMatch(directoryPath);
52+
if (match != null && match.groupCount > 0) {
53+
return double.parse(match.group(1)!);
54+
}
55+
return 1.0;
56+
}
57+
58+
final Map<String, dynamic> result = <String, dynamic>{};
59+
final PlatformAssetBundle bundle = PlatformAssetBundle();
60+
61+
// For the benchmark, we use the older JSON format and then convert it to the modern binary format.
62+
final ByteData jsonAssetManifestBytes = await bundle.load('money_asset_manifest.json');
63+
final String jsonAssetManifest = utf8.decode(jsonAssetManifestBytes.buffer.asUint8List());
64+
65+
final Map<String, dynamic> assetManifest = json.decode(jsonAssetManifest) as Map<String, dynamic>;
66+
67+
for (final MapEntry<String, dynamic> manifestEntry in assetManifest.entries) {
68+
final List<dynamic> resultVariants = <dynamic>[];
69+
final List<String> entries = (manifestEntry.value as List<dynamic>).cast<String>();
70+
for (final String variant in entries) {
71+
if (variant == manifestEntry.key) {
72+
// With the newer binary format, don't include the main asset in it's
73+
// list of variants. This reduces parsing time at runtime.
74+
continue;
75+
}
76+
final Map<String, dynamic> resultVariant = <String, dynamic>{};
77+
final double variantDevicePixelRatio = parseScale(variant);
78+
resultVariant['asset'] = variant;
79+
resultVariant['dpr'] = variantDevicePixelRatio;
80+
resultVariants.add(resultVariant);
81+
}
82+
result[manifestEntry.key] = resultVariants;
83+
}
84+
85+
return const StandardMessageCodec().encodeMessage(result)!;
86+
}

dev/integration_tests/flutter_gallery/test/example_code_parser_test.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
6+
57
import 'package:flutter/services.dart';
68
import 'package:flutter_gallery/gallery/example_code_parser.dart';
79
import 'package:flutter_test/flutter_test.dart';
@@ -58,4 +60,9 @@ class TestAssetBundle extends AssetBundle {
5860

5961
@override
6062
String toString() => '$runtimeType@$hashCode()';
63+
64+
@override
65+
Future<T> loadStructuredBinaryData<T>(String key, FutureOr<T> Function(ByteData data) parser) async {
66+
return parser(await load(key));
67+
}
6168
}

0 commit comments

Comments
 (0)