[dart2js] Remove need for individual closures in Deferrable serialized data.
This new approach uses static tear-offs where possible to avoid allocating a new Closure for each Deferrable. Note: This doesn't scale to every case but as of today the one case not covered is a singleton and so only 1 closure should be allocated there regardless. Data from a fairly large app: Before: Total => 3.8GB Closures => 85.6MB Closure Context => 71.0MB After: Total => 3.7GB Closures => 28.9MB Closure Context => 49.8MB Diff: Closures => 56.7MB Closure Context => 21.2MB Change-Id: If233f958df2822708b51c0d14c7439b5d3a5a07b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/313340 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
@@ -271,26 +271,26 @@ class GlobalTypeInferenceResultsImpl implements GlobalTypeInferenceResults {
|
||||
|
||||
source.begin(tag);
|
||||
Deferrable<Map<MemberEntity, GlobalTypeInferenceMemberResult>>
|
||||
memberResults = source.readDeferrable(() => source.readMemberMap(
|
||||
memberResults = source.readDeferrable((source) => source.readMemberMap(
|
||||
(MemberEntity member) =>
|
||||
GlobalTypeInferenceMemberResult.readFromDataSource(
|
||||
source,
|
||||
elementMap.getMemberContextNode(member),
|
||||
closedWorld.abstractValueDomain)));
|
||||
Deferrable<Map<Local, AbstractValue>> parameterResults =
|
||||
source.readDeferrable(() => source.readLocalMap(() => closedWorld
|
||||
source.readDeferrable((source) => source.readLocalMap(() => closedWorld
|
||||
.abstractValueDomain
|
||||
.readAbstractValueFromDataSource(source)));
|
||||
Set<Selector> returnsListElementTypeSet =
|
||||
source.readList(() => Selector.readFromDataSource(source)).toSet();
|
||||
Deferrable<Map<ir.TreeNode, AbstractValue>> allocatedLists =
|
||||
source.readDeferrable(() => source.readTreeNodeMap(() => closedWorld
|
||||
.abstractValueDomain
|
||||
.readAbstractValueFromDataSource(source)));
|
||||
source.readDeferrable((source) => source.readTreeNodeMap(() =>
|
||||
closedWorld.abstractValueDomain
|
||||
.readAbstractValueFromDataSource(source)));
|
||||
Deferrable<Map<ir.TreeNode, AbstractValue>> allocatedRecords =
|
||||
source.readDeferrable(() => source.readTreeNodeMap(() => closedWorld
|
||||
.abstractValueDomain
|
||||
.readAbstractValueFromDataSource(source)));
|
||||
source.readDeferrable((source) => source.readTreeNodeMap(() =>
|
||||
closedWorld.abstractValueDomain
|
||||
.readAbstractValueFromDataSource(source)));
|
||||
source.end(tag);
|
||||
return GlobalTypeInferenceResultsImpl._deserialized(
|
||||
closedWorld,
|
||||
|
||||
@@ -66,21 +66,40 @@ class ClosureDataImpl implements ClosureData {
|
||||
this._localClosureRepresentationMap,
|
||||
this._enclosingMembers);
|
||||
|
||||
static Map<MemberEntity, ScopeInfo> _readScopeMap(DataSourceReader source) {
|
||||
return source.readMemberMap(
|
||||
(MemberEntity member) => ScopeInfo.readFromDataSource(source));
|
||||
}
|
||||
|
||||
static Map<ir.TreeNode, CapturedScope> _readCapturedScopesMap(
|
||||
DataSourceReader source) {
|
||||
return source
|
||||
.readTreeNodeMap(() => CapturedScope.readFromDataSource(source));
|
||||
}
|
||||
|
||||
static Map<MemberEntity, CapturedScope> _readCapturedScopeForSignatureMap(
|
||||
DataSourceReader source) {
|
||||
return source
|
||||
.readMemberMap((_) => CapturedScope.readFromDataSource(source));
|
||||
}
|
||||
|
||||
static Map<ir.LocalFunction, ClosureRepresentationInfo>
|
||||
_readLocalClosureRepresentationMap(DataSourceReader source) {
|
||||
return source.readTreeNodeMap<ir.LocalFunction, ClosureRepresentationInfo>(
|
||||
() => ClosureRepresentationInfo.readFromDataSource(source));
|
||||
}
|
||||
|
||||
/// Deserializes a [ClosureData] object from [source].
|
||||
factory ClosureDataImpl.readFromDataSource(
|
||||
JsToElementMap elementMap, DataSourceReader source) {
|
||||
source.begin(tag);
|
||||
// TODO(johnniwinther): Support shared [ScopeInfo].
|
||||
final scopeMap = source.readDeferrable(() => source.readMemberMap(
|
||||
(MemberEntity member) => ScopeInfo.readFromDataSource(source)));
|
||||
final capturedScopesMap = source.readDeferrable(() =>
|
||||
source.readTreeNodeMap(() => CapturedScope.readFromDataSource(source)));
|
||||
final capturedScopeForSignatureMap = source.readDeferrable(() =>
|
||||
source.readMemberMap(
|
||||
(MemberEntity member) => CapturedScope.readFromDataSource(source)));
|
||||
final localClosureRepresentationMap = source.readDeferrable(() =>
|
||||
source.readTreeNodeMap<ir.LocalFunction, ClosureRepresentationInfo>(
|
||||
() => ClosureRepresentationInfo.readFromDataSource(source)));
|
||||
final scopeMap = source.readDeferrable(_readScopeMap);
|
||||
final capturedScopesMap = source.readDeferrable(_readCapturedScopesMap);
|
||||
final capturedScopeForSignatureMap =
|
||||
source.readDeferrable(_readCapturedScopeForSignatureMap);
|
||||
final localClosureRepresentationMap =
|
||||
source.readDeferrable(_readLocalClosureRepresentationMap);
|
||||
Map<MemberEntity, MemberEntity> enclosingMembers =
|
||||
source.readMemberMap((member) => source.readMember());
|
||||
source.end(tag);
|
||||
@@ -1205,6 +1224,10 @@ class ClosureFunctionData extends ClosureMemberData
|
||||
ClosureFunctionData._deserialized(super.definition, super.memberThisType,
|
||||
this.functionType, this._functionNode, this.classTypeVariableAccess);
|
||||
|
||||
static ir.FunctionNode _readFunctionNode(DataSourceReader source) {
|
||||
return source.readTreeNode() as ir.FunctionNode;
|
||||
}
|
||||
|
||||
factory ClosureFunctionData.readFromDataSource(DataSourceReader source) {
|
||||
source.begin(tag);
|
||||
ClosureMemberDefinition definition =
|
||||
@@ -1213,7 +1236,7 @@ class ClosureFunctionData extends ClosureMemberData
|
||||
source.readDartTypeOrNull() as InterfaceType?;
|
||||
FunctionType functionType = source.readDartType() as FunctionType;
|
||||
Deferrable<ir.FunctionNode> functionNode =
|
||||
source.readDeferrable(() => source.readTreeNode() as ir.FunctionNode);
|
||||
source.readDeferrable(_readFunctionNode);
|
||||
ClassTypeVariableAccess classTypeVariableAccess =
|
||||
source.readEnum(ClassTypeVariableAccess.values);
|
||||
source.end(tag);
|
||||
|
||||
@@ -450,11 +450,13 @@ class SpecialMemberDefinition implements MemberDefinition {
|
||||
|
||||
SpecialMemberDefinition._deserialized(this._node, this.kind);
|
||||
|
||||
static ir.TreeNode _readNode(DataSourceReader source) =>
|
||||
source.readTreeNode();
|
||||
|
||||
factory SpecialMemberDefinition.readFromDataSource(
|
||||
DataSourceReader source, MemberKind kind) {
|
||||
source.begin(tag);
|
||||
Deferrable<ir.TreeNode> node =
|
||||
source.readDeferrable(() => source.readTreeNode());
|
||||
Deferrable<ir.TreeNode> node = source.readDeferrable(_readNode);
|
||||
source.end(tag);
|
||||
return SpecialMemberDefinition._deserialized(node, kind);
|
||||
}
|
||||
@@ -497,12 +499,14 @@ class ClosureMemberDefinition implements MemberDefinition {
|
||||
: assert(
|
||||
kind == MemberKind.closureCall || kind == MemberKind.closureField);
|
||||
|
||||
static ir.TreeNode _readNode(DataSourceReader source) =>
|
||||
source.readTreeNode();
|
||||
|
||||
factory ClosureMemberDefinition.readFromDataSource(
|
||||
DataSourceReader source, MemberKind kind) {
|
||||
source.begin(tag);
|
||||
SourceSpan location = source.readSourceSpan();
|
||||
Deferrable<ir.TreeNode> node =
|
||||
source.readDeferrable(() => source.readTreeNode());
|
||||
Deferrable<ir.TreeNode> node = source.readDeferrable(_readNode);
|
||||
source.end(tag);
|
||||
return ClosureMemberDefinition._deserialized(location, kind, node);
|
||||
}
|
||||
|
||||
@@ -639,6 +639,11 @@ abstract class JMemberDataImpl implements JMemberData {
|
||||
|
||||
JMemberDataImpl._deserialized(this.node, this.definition, this._staticTypes);
|
||||
|
||||
static StaticTypeCache _readStaticTypeCache(
|
||||
DataSourceReader source, ir.Member node) {
|
||||
return StaticTypeCache.readFromDataSource(source, node);
|
||||
}
|
||||
|
||||
@override
|
||||
InterfaceType? getMemberThisType(JsToElementMap elementMap) {
|
||||
MemberEntity member = elementMap.getMember(node);
|
||||
@@ -770,8 +775,8 @@ class FunctionDataImpl extends JMemberDataImpl
|
||||
"Unexpected member node $node (${node.runtimeType}).");
|
||||
}
|
||||
MemberDefinition definition = MemberDefinition.readFromDataSource(source);
|
||||
Deferrable<StaticTypeCache> staticTypes = source
|
||||
.readDeferrable(() => StaticTypeCache.readFromDataSource(source, node));
|
||||
Deferrable<StaticTypeCache> staticTypes = source.readDeferrableWithArg(
|
||||
JMemberDataImpl._readStaticTypeCache, node);
|
||||
source.end(tag);
|
||||
return FunctionDataImpl._deserialized(
|
||||
node, functionNode, definition, staticTypes);
|
||||
@@ -822,13 +827,18 @@ class SignatureFunctionData implements FunctionData {
|
||||
SignatureFunctionData._deserialized(this.definition, this.memberThisType,
|
||||
this._typeParameters, this.classTypeVariableAccess);
|
||||
|
||||
static List<ir.TypeParameter> _readTypeParameterNodes(
|
||||
DataSourceReader source) {
|
||||
return source.readTypeParameterNodes();
|
||||
}
|
||||
|
||||
factory SignatureFunctionData.readFromDataSource(DataSourceReader source) {
|
||||
source.begin(tag);
|
||||
MemberDefinition definition = MemberDefinition.readFromDataSource(source);
|
||||
InterfaceType? memberThisType =
|
||||
source.readDartTypeOrNull() as InterfaceType?;
|
||||
Deferrable<List<ir.TypeParameter>> typeParameters =
|
||||
source.readDeferrable(() => source.readTypeParameterNodes());
|
||||
source.readDeferrable(_readTypeParameterNodes);
|
||||
ClassTypeVariableAccess classTypeVariableAccess =
|
||||
source.readEnum(ClassTypeVariableAccess.values);
|
||||
source.end(tag);
|
||||
@@ -975,8 +985,8 @@ class JConstructorData extends FunctionDataImpl {
|
||||
"Unexpected member node $node (${node.runtimeType}).");
|
||||
}
|
||||
MemberDefinition definition = MemberDefinition.readFromDataSource(source);
|
||||
Deferrable<StaticTypeCache> staticTypes = source
|
||||
.readDeferrable(() => StaticTypeCache.readFromDataSource(source, node));
|
||||
Deferrable<StaticTypeCache> staticTypes = source.readDeferrableWithArg(
|
||||
JMemberDataImpl._readStaticTypeCache, node);
|
||||
source.end(tag);
|
||||
return JConstructorData._deserialized(
|
||||
node, functionNode, definition, staticTypes);
|
||||
@@ -1023,8 +1033,8 @@ class ConstructorBodyDataImpl extends FunctionDataImpl {
|
||||
"Unexpected member node $node (${node.runtimeType}).");
|
||||
}
|
||||
MemberDefinition definition = MemberDefinition.readFromDataSource(source);
|
||||
Deferrable<StaticTypeCache> staticTypes = source
|
||||
.readDeferrable(() => StaticTypeCache.readFromDataSource(source, node));
|
||||
Deferrable<StaticTypeCache> staticTypes = source.readDeferrableWithArg(
|
||||
JMemberDataImpl._readStaticTypeCache, node);
|
||||
source.end(tag);
|
||||
return ConstructorBodyDataImpl._deserialized(
|
||||
node, functionNode, definition, staticTypes);
|
||||
@@ -1067,8 +1077,8 @@ class JFieldDataImpl extends JMemberDataImpl implements JFieldData {
|
||||
source.begin(tag);
|
||||
ir.Member node = source.readMemberNode();
|
||||
MemberDefinition definition = MemberDefinition.readFromDataSource(source);
|
||||
Deferrable<StaticTypeCache> staticTypes = source
|
||||
.readDeferrable(() => StaticTypeCache.readFromDataSource(source, node));
|
||||
Deferrable<StaticTypeCache> staticTypes = source.readDeferrableWithArg(
|
||||
JMemberDataImpl._readStaticTypeCache, node);
|
||||
source.end(tag);
|
||||
return JFieldDataImpl._deserialized(
|
||||
node as ir.Field, definition, staticTypes);
|
||||
|
||||
@@ -44,8 +44,8 @@ class GlobalLocalsMap {
|
||||
Map<MemberEntity, Deferrable<KernelToLocalsMap>> _localsMaps = {};
|
||||
int mapCount = source.readInt();
|
||||
for (int i = 0; i < mapCount; i++) {
|
||||
Deferrable<KernelToLocalsMap> localsMap = source.readDeferrable(
|
||||
() => KernelToLocalsMapImpl.readFromDataSource(source));
|
||||
Deferrable<KernelToLocalsMap> localsMap =
|
||||
source.readDeferrable(KernelToLocalsMapImpl.readFromDataSource);
|
||||
List<MemberEntity> members = source.readMembers();
|
||||
for (MemberEntity member in members) {
|
||||
_localsMaps[member] = localsMap;
|
||||
|
||||
@@ -19,6 +19,10 @@ import 'package:compiler/src/serialization/serialization.dart';
|
||||
/// `Deferrable<E>` to initialize `_m1`. This internal constructor should be
|
||||
/// called from the readFromSource method/factory to create the instance
|
||||
/// of `C`. `d` should be obtained using [DataSourceReader.readDeferrable].
|
||||
/// Note: [DataSourceReader.readDeferrable] passes the correct
|
||||
/// [DataSourceReader] to the reader function so that the [DataSourceReader]
|
||||
/// does not have to be closed over. If necessary a static tear-off should be
|
||||
/// used as the argument so a closure isn't created for every read.
|
||||
/// 3) Any existing constructors of `C` should maintain the same signature
|
||||
/// and initialize `_m1` passing `m` to [Deferrable.eager] where m is the value
|
||||
/// previously used to initialize `m0`.
|
||||
@@ -31,11 +35,14 @@ import 'package:compiler/src/serialization/serialization.dart';
|
||||
///
|
||||
/// class Foo {
|
||||
/// final Bar bar;
|
||||
/// final String name;
|
||||
///
|
||||
/// Foo(this.bar);
|
||||
/// Foo(this.bar, this.name);
|
||||
///
|
||||
/// factory Foo.readFromSource(DataSourceReader reader) {
|
||||
/// return Foo(Bar.readFromSource(reader));
|
||||
/// final bar = Bar.readFromSource(reader);
|
||||
/// final name = reader.readString();
|
||||
/// return Foo(bar, name);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@@ -45,22 +52,36 @@ import 'package:compiler/src/serialization/serialization.dart';
|
||||
/// Bar get bar => _bar.loaded();
|
||||
/// final Deferrable<Bar> _bar;
|
||||
///
|
||||
/// Foo(Bar bar) : _bar = Deferrable.eager(bar);
|
||||
/// Foo._deserialized(this._bar);
|
||||
/// String get name => _name.loaded();
|
||||
/// final Deferrable<String> _name;
|
||||
///
|
||||
/// Foo(Bar bar, String name) :
|
||||
/// _bar = Deferrable.eager(bar), _name = Deferrable.eager(name);
|
||||
/// Foo._deserialized(this._bar, this._name);
|
||||
///
|
||||
/// static String readName(DataSourceReader reader) => reader.readString();
|
||||
///
|
||||
/// factory Foo.readFromSource(DataSourceReader reader) {
|
||||
/// return Foo._deserialized(
|
||||
/// reader.readDeferrable(() => Bar.readFromSource(reader)));
|
||||
/// final bar = reader.readDeferrable(Bar.readFromSource);
|
||||
/// final name = reader.readDeferrable(readName);
|
||||
/// return Foo._deserialized(bar, name);
|
||||
/// }
|
||||
/// }
|
||||
abstract class Deferrable<E> {
|
||||
E loaded();
|
||||
|
||||
factory Deferrable.deferred(DataSourceReader reader, E f(), int offset,
|
||||
factory Deferrable.deferred(
|
||||
DataSourceReader reader, E f(DataSourceReader source), int offset,
|
||||
{bool cacheData = true}) =>
|
||||
cacheData
|
||||
? _DeferredCache(reader, f, offset)
|
||||
: _Deferred(reader, f, offset);
|
||||
static Deferrable<E> deferredWithArg<E, A>(DataSourceReader reader,
|
||||
E f(DataSourceReader source, A arg), A arg, int offset,
|
||||
{bool cacheData = true}) =>
|
||||
cacheData
|
||||
? _DeferredCacheWithArg(reader, f, arg, offset)
|
||||
: _DeferredWithArg(reader, f, arg, offset);
|
||||
const factory Deferrable.eager(E data) = _Eager;
|
||||
|
||||
const Deferrable();
|
||||
@@ -73,12 +94,48 @@ class _Eager<E> extends Deferrable<E> {
|
||||
const _Eager(this._data);
|
||||
}
|
||||
|
||||
class _Deferred<E> extends Deferrable<E> {
|
||||
class _DeferredWithArg<E, A> extends Deferrable<E> {
|
||||
final DataSourceReader _reader;
|
||||
final E Function() _dataLoader;
|
||||
final E Function(DataSourceReader source, A arg) _dataLoader;
|
||||
final A _arg;
|
||||
final int _dataOffset;
|
||||
@override
|
||||
E loaded() => _reader.readWithOffset(_dataOffset, _dataLoader);
|
||||
E loaded() =>
|
||||
_reader.readWithOffset(_dataOffset, () => _dataLoader(_reader, _arg));
|
||||
_DeferredWithArg(this._reader, this._dataLoader, this._arg, this._dataOffset);
|
||||
}
|
||||
|
||||
class _DeferredCacheWithArg<E, A> extends Deferrable<E> {
|
||||
final int _dataOffset;
|
||||
// Below fields are nullable so they can be cleared after loading.
|
||||
DataSourceReader? _reader;
|
||||
E Function(DataSourceReader source, A arg)? _dataLoader;
|
||||
A? _arg;
|
||||
late final E _data = _loadData();
|
||||
|
||||
@override
|
||||
E loaded() => _data;
|
||||
|
||||
E _loadData() {
|
||||
final reader = _reader!;
|
||||
final dataLoader = _dataLoader!;
|
||||
final arg = _arg!;
|
||||
_reader = null;
|
||||
_dataLoader = null;
|
||||
_arg = null;
|
||||
return reader.readWithOffset(_dataOffset, () => dataLoader(reader, arg));
|
||||
}
|
||||
|
||||
_DeferredCacheWithArg(
|
||||
this._reader, this._dataLoader, this._arg, this._dataOffset);
|
||||
}
|
||||
|
||||
class _Deferred<E> extends Deferrable<E> {
|
||||
final DataSourceReader _reader;
|
||||
final E Function(DataSourceReader source) _dataLoader;
|
||||
final int _dataOffset;
|
||||
@override
|
||||
E loaded() => _reader.readWithOffset(_dataOffset, () => _dataLoader(_reader));
|
||||
_Deferred(this._reader, this._dataLoader, this._dataOffset);
|
||||
}
|
||||
|
||||
@@ -86,7 +143,7 @@ class _DeferredCache<E> extends Deferrable<E> {
|
||||
final int _dataOffset;
|
||||
// Below fields are nullable so they can be cleared after loading.
|
||||
DataSourceReader? _reader;
|
||||
E Function()? _dataLoader;
|
||||
E Function(DataSourceReader source)? _dataLoader;
|
||||
late final E _data = _loadData();
|
||||
|
||||
@override
|
||||
@@ -97,7 +154,7 @@ class _DeferredCache<E> extends Deferrable<E> {
|
||||
final dataLoader = _dataLoader!;
|
||||
_reader = null;
|
||||
_dataLoader = null;
|
||||
return reader.readWithOffset(_dataOffset, dataLoader);
|
||||
return reader.readWithOffset(_dataOffset, () => dataLoader(reader));
|
||||
}
|
||||
|
||||
_DeferredCache(this._reader, this._dataLoader, this._dataOffset);
|
||||
|
||||
@@ -287,13 +287,28 @@ class DataSourceReader {
|
||||
return _sourceReader.readAtOffset(offset, f);
|
||||
}
|
||||
|
||||
Deferrable<E> readDeferrable<E>(E f(), {bool cacheData = true}) {
|
||||
Deferrable<E> readDeferrable<E>(E f(DataSourceReader source),
|
||||
{bool cacheData = true}) {
|
||||
return enableDeferredStrategy
|
||||
? (useDeferredStrategy
|
||||
? Deferrable<E>.deferred(this, f, _sourceReader.readDeferred(),
|
||||
cacheData: cacheData)
|
||||
: Deferrable<E>.eager(_sourceReader.readDeferredAsEager(f)))
|
||||
: Deferrable<E>.eager(f());
|
||||
: Deferrable<E>.eager(
|
||||
_sourceReader.readDeferredAsEager(() => f(this))))
|
||||
: Deferrable<E>.eager(f(this));
|
||||
}
|
||||
|
||||
Deferrable<E> readDeferrableWithArg<E, A>(
|
||||
E f(DataSourceReader source, A arg), A arg,
|
||||
{bool cacheData = true}) {
|
||||
return enableDeferredStrategy
|
||||
? (useDeferredStrategy
|
||||
? Deferrable.deferredWithArg<E, A>(
|
||||
this, f, arg, _sourceReader.readDeferred(),
|
||||
cacheData: cacheData)
|
||||
: Deferrable<E>.eager(
|
||||
_sourceReader.readDeferredAsEager(() => f(this, arg))))
|
||||
: Deferrable<E>.eager(f(this, arg));
|
||||
}
|
||||
|
||||
/// Invoke [f] in the context of [member]. This sets up support for
|
||||
|
||||
Reference in New Issue
Block a user