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

Commit 587ff2a

Browse files
authored
add benchmarks for normal Dart classes, serialized to JsonBuffers (#153)
1 parent a88aa0b commit 587ff2a

File tree

3 files changed

+251
-2
lines changed

3 files changed

+251
-2
lines changed

pkgs/dart_model/benchmark/main.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'lazy_maps_buffer_wire_benchmark.dart';
1010
import 'lazy_maps_json_wire_benchmark.dart';
1111
import 'lazy_wrappers_buffer_wire_benchmark.dart';
1212
import 'lazy_wrappers_buffer_wire_benchmark.dart' as wrapped;
13+
import 'regular_dart_classes.dart';
14+
import 'regular_dart_classes.dart' as regular;
1315
import 'sdk_maps_buffer_wire_benchmark.dart';
1416
import 'sdk_maps_builder_wire_benchmark.dart';
1517
import 'sdk_maps_json_wire_benchmark.dart';
@@ -19,6 +21,7 @@ void main() {
1921
final lazyMapsBufferWireBenchmark = LazyMapsBufferWireBenchmark();
2022
final lazyWrappersBufferWireBenchmark = LazyWrappersBufferWireBenchmark();
2123
final builderMapsBuilderWireBenchmark = BuilderMapsBuilderWireBenchmark();
24+
final regularClassesBufferWireBenchmark = RegularClassesBufferWireBenchmark();
2225
final serializationBenchmarks = [
2326
sdkMapsJsonWireBenchmark,
2427
SdkMapsBufferWireBenchmark(),
@@ -28,6 +31,7 @@ void main() {
2831
lazyWrappersBufferWireBenchmark,
2932
BuilderMapsJsonWireBenchmark(),
3033
builderMapsBuilderWireBenchmark,
34+
regularClassesBufferWireBenchmark,
3135
];
3236

3337
for (var i = 0; i != 3; ++i) {
@@ -47,6 +51,7 @@ void main() {
4751
lazyMapsBufferWireBenchmark.processBenchmark(),
4852
lazyWrappersBufferWireBenchmark.processBenchmark(),
4953
builderMapsBuilderWireBenchmark.processBenchmark(),
54+
regularClassesBufferWireBenchmark.processBenchmark(),
5055
]) {
5156
final measure = benchmark.measure().toMilliseconds;
5257
final paddedName = benchmark.name.padLeft(36);
@@ -62,6 +67,10 @@ void main() {
6267
deserialized = deserialized.map<String, Object?>(
6368
(k, v) => MapEntry(k, v.toJson()),
6469
);
70+
} else if (deserialized is Map<String, regular.Interface>) {
71+
deserialized = deserialized.map<String, Object?>(
72+
(k, v) => MapEntry(k, v.toJson()),
73+
);
6574
}
6675
if (!const DeepCollectionEquality().equals(
6776
deserialized,
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dart_model/src/json_buffer/json_buffer_builder.dart';
6+
7+
import 'serialization_benchmark.dart';
8+
9+
JsonBufferBuilder? runningBuffer;
10+
11+
/// Benchmark accumulating data directly into a [JsonBufferBuilder] with an
12+
/// indirection through a thin wrapper type (which is a real type, not an
13+
/// extension type).
14+
class RegularClassesBufferWireBenchmark extends SerializationBenchmark {
15+
@override
16+
void run() {
17+
var data = createData();
18+
19+
final buffer = runningBuffer = JsonBufferBuilder();
20+
data.forEach((k, v) => buffer.map[k] = v.toJson());
21+
serialized = runningBuffer!.serialize();
22+
}
23+
24+
/// Creates the data, but its not ready yet to be serialized.
25+
Map<String, Interface> createData() {
26+
final map = <String, Interface>{};
27+
28+
for (final key in mapKeys) {
29+
final intKey = int.parse(key);
30+
var interface = Interface(
31+
members: {
32+
for (final memberName in makeMemberNames(intKey))
33+
memberName: _makeMember(memberName),
34+
},
35+
properties: Properties(
36+
isAbstract: (intKey & 1) == 1,
37+
isClass: (intKey & 2) == 2,
38+
isGetter: (intKey & 4) == 4,
39+
isField: (intKey & 8) == 8,
40+
isMethod: (intKey & 16) == 16,
41+
isStatic: (intKey & 32) == 32,
42+
),
43+
);
44+
map[key] = interface;
45+
}
46+
47+
return map;
48+
}
49+
50+
Member _makeMember(String key) {
51+
final intKey = key.length;
52+
return Member(
53+
properties: Properties(
54+
isAbstract: (intKey & 1) == 1,
55+
isClass: (intKey & 2) == 2,
56+
isGetter: (intKey & 4) == 4,
57+
isField: const [true, false, null][intKey % 3],
58+
isMethod: (intKey & 16) == 16,
59+
isStatic: (intKey & 32) == 32,
60+
),
61+
);
62+
}
63+
64+
@override
65+
void deserialize() {
66+
deserialized = JsonBufferBuilder.deserialize(
67+
serialized!,
68+
).map.map<String, Interface>(
69+
(k, v) => MapEntry(k, Interface.fromJson(v as Map<String, Object?>)),
70+
);
71+
}
72+
}
73+
74+
abstract interface class Serializable {
75+
Map<String, Object?> toJson();
76+
}
77+
78+
/// An interface.
79+
class Interface implements Serializable, Hashable {
80+
final Map<String, Member>? _members;
81+
Map<String, Member> get members => _members!;
82+
83+
final Properties? _properties;
84+
Properties get properties => _properties!;
85+
86+
static TypedMapSchema schema = TypedMapSchema({
87+
'members': Type.growableMapPointer,
88+
'properties': Type.typedMapPointer,
89+
});
90+
91+
Interface({Properties? properties, Map<String, Member>? members})
92+
: _properties = properties,
93+
_members = members;
94+
95+
factory Interface.fromJson(Map<String, Object?> json) => Interface(
96+
properties: Properties.fromJson(json['properties'] as Map<String, Object?>),
97+
members: (json['members'] as Map<String, Object?>).map(
98+
(k, v) => MapEntry(k, Member.fromJson(v as Map<String, Object?>)),
99+
),
100+
);
101+
102+
@override
103+
Map<String, Object?> toJson() {
104+
var membersMap = runningBuffer!.createGrowableMap<Map<String, Object?>>();
105+
_members?.forEach((k, v) => membersMap[k] = v.toJson());
106+
return runningBuffer!.createTypedMap(
107+
schema,
108+
membersMap,
109+
_properties?.toJson(),
110+
);
111+
}
112+
113+
@override
114+
int get deepHash {
115+
var result = 0;
116+
result ^= 'members'.hashCode;
117+
_members?.forEach((k, v) {
118+
result ^= k.hashCode;
119+
result ^= v.deepHash;
120+
});
121+
result ^= 'properties'.hashCode ^ (_properties?.deepHash ?? null.hashCode);
122+
return result;
123+
}
124+
}
125+
126+
/// A member.
127+
class Member implements Serializable, Hashable {
128+
final Properties? _properties;
129+
Properties get properties => _properties!;
130+
131+
static TypedMapSchema schema = TypedMapSchema({
132+
'properties': Type.typedMapPointer,
133+
});
134+
135+
Member({Properties? properties}) : _properties = properties;
136+
137+
factory Member.fromJson(Map<String, Object?> json) => Member(
138+
properties: Properties.fromJson(json['properties'] as Map<String, Object?>),
139+
);
140+
141+
@override
142+
Map<String, Object?> toJson() =>
143+
runningBuffer!.createTypedMap(schema, _properties?.toJson());
144+
145+
@override
146+
int get deepHash {
147+
var result = 0;
148+
result ^= 'properties'.hashCode ^ (_properties?.deepHash ?? null.hashCode);
149+
return result;
150+
}
151+
}
152+
153+
/// Set of boolean properties.
154+
class Properties implements Serializable, Hashable {
155+
/// Whether the entity is abstract, meaning it has no definition.
156+
final bool? _isAbstract;
157+
bool get isAbstract => _isAbstract!;
158+
159+
/// Whether the entity is a class.
160+
final bool? _isClass;
161+
bool get isClass => _isClass!;
162+
163+
/// Whether the entity is a getter.
164+
final bool? _isGetter;
165+
bool get isGetter => _isGetter!;
166+
167+
/// Whether the entity is a field.
168+
final bool? _isField;
169+
bool get isField => _isField!;
170+
171+
/// Whether the entity is a method.
172+
final bool? _isMethod;
173+
bool get isMethod => _isMethod!;
174+
175+
/// Whether the entity is static.
176+
final bool? _isStatic;
177+
bool get isStatic => _isStatic!;
178+
179+
static TypedMapSchema schema = TypedMapSchema({
180+
'isAbstract': Type.boolean,
181+
'isClass': Type.boolean,
182+
'isGetter': Type.boolean,
183+
'isField': Type.boolean,
184+
'isMethod': Type.boolean,
185+
'isStatic': Type.boolean,
186+
});
187+
188+
Properties({
189+
bool? isAbstract,
190+
bool? isClass,
191+
bool? isGetter,
192+
bool? isField,
193+
bool? isMethod,
194+
bool? isStatic,
195+
}) : _isAbstract = isAbstract,
196+
_isClass = isClass,
197+
_isGetter = isGetter,
198+
_isField = isField,
199+
_isMethod = isMethod,
200+
_isStatic = isStatic;
201+
202+
factory Properties.fromJson(Map<String, Object?> json) => Properties(
203+
isAbstract: json['isAbstract'] as bool?,
204+
isClass: json['isClass'] as bool?,
205+
isGetter: json['isGetter'] as bool?,
206+
isField: json['isField'] as bool?,
207+
isMethod: json['isMethod'] as bool?,
208+
isStatic: json['isStatic'] as bool?,
209+
);
210+
211+
@override
212+
Map<String, Object?> toJson() => runningBuffer!.createTypedMap(
213+
schema,
214+
_isAbstract,
215+
_isClass,
216+
_isGetter,
217+
_isField,
218+
_isMethod,
219+
_isStatic,
220+
);
221+
222+
@override
223+
int get deepHash {
224+
var result = 0;
225+
result ^= 'isAbstract'.hashCode ^ _isAbstract.hashCode;
226+
result ^= 'isClass'.hashCode ^ _isClass.hashCode;
227+
result ^= 'isGetter'.hashCode ^ _isGetter.hashCode;
228+
result ^= 'isField'.hashCode ^ _isField.hashCode;
229+
result ^= 'isMethod'.hashCode ^ _isMethod.hashCode;
230+
result ^= 'isStatic'.hashCode ^ _isStatic.hashCode;
231+
return result;
232+
}
233+
}

pkgs/dart_model/benchmark/serialization_benchmark.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'dart:typed_data';
66

77
import 'package:benchmark_harness/benchmark_harness.dart';
88

9-
import 'lazy_wrappers_buffer_wire_benchmark.dart';
9+
import 'lazy_wrappers_buffer_wire_benchmark.dart' as wrapped;
1010

1111
const mapSize = 10000;
1212
final mapKeys = List.generate(mapSize, (i) => i.toString());
@@ -64,12 +64,19 @@ class ProcessBenchmark extends BenchmarkBase {
6464
final value = entry.value;
6565
if (value is Map) {
6666
result ^= deepHash(value);
67-
} else if (value is Serializable) {
67+
} else if (value is wrapped.Serializable) {
6868
result ^= deepHash(value.toJson());
69+
} else if (value is Hashable) {
70+
result ^= value.deepHash;
6971
} else {
7072
result ^= value.hashCode;
7173
}
7274
}
7375
return result;
7476
}
7577
}
78+
79+
/// Interface for computing a hash, when the underlying object isn't a Map.
80+
abstract interface class Hashable {
81+
int get deepHash;
82+
}

0 commit comments

Comments
 (0)