From 767ce8152ab2b56d1f9bb3554f0e5f9552cc15d2 Mon Sep 17 00:00:00 2001 From: Loren Van Spronsen Date: Sat, 23 Apr 2022 09:21:12 -0700 Subject: [PATCH] Remove fold() closures from `FieldSet._hashCode`. (#554) 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 --- protobuf/lib/src/protobuf/field_set.dart | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/protobuf/lib/src/protobuf/field_set.dart b/protobuf/lib/src/protobuf/field_set.dart index 980056380..9e702b69e 100644 --- a/protobuf/lib/src/protobuf/field_set.dart +++ b/protobuf/lib/src/protobuf/field_set.dart @@ -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);