Skip to content

Commit

Permalink
Remove fold() closures from FieldSet._hashCode. (#554)
Browse files Browse the repository at this point in the history
The fold() calls come with significant runtime cost, and make it more
difficult to hoist accesses to commonly used variables. Just rewriting
this pattern shows a significant performance improvement for both JS and
VM benchmarks.

Baseline

```
JS
HashCode(RunTime): 9833.333333333334 us.
HashCode(RunTime): 9891.625615763547 us.
HashCode(RunTime): 9607.655502392345 us.
HashCode(RunTime): 9661.835748792271 us.
HashCode(RunTime): 9765.853658536585 us.
VM
HashCode(RunTime): 4527.384615384615 us.
HashCode(RunTime): 4534.151583710407 us.
HashCode(RunTime): 4546.556818181818 us.
HashCode(RunTime): 4490.65470852017
```

Results

```
JS
HashCode(RunTime): 8004 us.
HashCode(RunTime): 7980.0796812749 us.
HashCode(RunTime): 7976.095617529881 us.
HashCode(RunTime): 7824.21875 us.
HashCode(RunTime): 7847.058823529412 us.
VM
HashCode(RunTime): 2474.5451174289246 us.
HashCode(RunTime): 2533.7037974683544 us.
HashCode(RunTime): 2532.4556962025317 us.
HashCode(RunTime): 2420.072551390568 us.
HashCode(RunTime): 2521.3198992443326
```

Co-authored-by: Loren Van Spronsen <[email protected]>
  • Loading branch information
LorenVS and Loren Van Spronsen authored Apr 23, 2022
1 parent 99bc541 commit 767ce81
Showing 1 changed file with 17 additions and 16 deletions.
33 changes: 17 additions & 16 deletions protobuf/lib/src/protobuf/field_set.dart
Original file line number Diff line number Diff line change
Expand Up @@ -678,26 +678,27 @@ class _FieldSet {
return hash;
}

int hashEachField(int hash) {
//non-extension fields
hash = _infosSortedByTag.where((fi) => _values[fi.index!] != null).fold(
hash, (int h, FieldInfo fi) => hashField(h, fi, _values[fi.index!]));

if (!_hasExtensions) return hash;
// Hash with descriptor.
var hash = _HashUtils._combine(0, _meta.hashCode);

hash =
_sorted(_extensions!._tagNumbers).fold(hash, (int h, int tagNumber) {
var fi = _extensions!._getInfoOrNull(tagNumber)!;
return hashField(h, fi, _extensions!._getFieldOrNull(fi));
});
// Hash with non-extension fields.
final values = _values;
for (final fi in _infosSortedByTag) {
final value = values[fi.index!];
if (value == null) continue;
hash = hashField(hash, fi, value);
}

return hash;
// Hash with extension fields.
if (_hasExtensions) {
final extensions = _extensions!;
final sortedByTagNumbers = _sorted(extensions._tagNumbers);
for (final tagNumber in sortedByTagNumbers) {
final fi = extensions._getInfoOrNull(tagNumber)!;
hash = hashField(hash, fi, extensions._getFieldOrNull(fi));
}
}

// Hash with descriptor.
var hash = _HashUtils._combine(0, _meta.hashCode);
// Hash with fields.
hash = hashEachField(hash);
// Hash with unknown fields.
if (_hasUnknownFields) {
hash = _HashUtils._combine(hash, _unknownFields.hashCode);
Expand Down

0 comments on commit 767ce81

Please sign in to comment.