-
Notifications
You must be signed in to change notification settings - Fork 193
Improve enum decoding -- alternative #981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This is an alternative to google#980 that doesn't make a big difference in terms of performance of AOT compiled benchmarks, but makes a big difference when compiling to Wasm, comapred to google#980. When decoding an enum value, we call a callback in the enum field's `FieldInfo`. The callback then indexes a map mapping enum numbers to Dart values. When these conditions hold: - The known enum numbers are all positive. (so that we can use a list and index it with the number) - The known enum numbers are more than 70% of the large known enum number. (so that the list won't have a lot of `null` entries, wasting heap space) We now generate a list instead of a map to map enum numbers to Dart values. Note: similar to the map, the list is runtime allocated. No new code generated per message or enum type. Wasm benchmarks: - Before: `protobuf_PackedEnumDecoding(RunTimeRaw): 48200.0 us` - PR google#980: `protobuf_PackedEnumDecoding(RunTimeRaw): 42120.0 us` - Diff: -12.6% - After: `protobuf_PackedEnumDecoding(RunTimeRaw): 35733.3 us` - Diff against PR google#980: -15% - Diff against master: -25% AOT benchmarks: - Before: `protobuf_PackedEnumDecoding(RunTimeRaw): 49180.0 us` - PR google#980: `protobuf_PackedEnumDecoding(RunTimeRaw): 45726.82 us` - Diff: -7% - This PR: `protobuf_PackedEnumDecoding(RunTimeRaw): 42929.7 us` - Diff against PR google#980: -6% - Diff agianst master: -12%
@mkustermann this change performs better, and while it's also a breaking change (adds a new member to a public class that can be extended), in practice this won't break anything and it doesn't require major version bump. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be ok with landing this, but see my comments on the other PR. If they make sense, maybe that could be an even better solution?
With upcoming change we'll improve decoding performance of enums, but there will be a difference between "sparse" and "dense" enum decoding performance even though they'll both be faster. To be able to measure the difference add a "sparse" enum type and a benchmark for decoding it. "Sparse" means the enum has large gaps between known enum values, or negative enum values. When decoding this kind of enums, the mapping from the wire `varint` to the Dart value for the enum needs to be done by binary search, map lookup, or similar. For "dense" enums, we can have a list of enum values and index the list directly with the `varint` value, after a range check. These changes will be done in the follow-up PR(s).
@mkustermann I tried:
Among these, this PR makes most sense to merge, as it's backwards compatible, and it performs the best. If you could take another look I'd like to merge this. In the meantime I'll test it internally. |
Internal testing reveals a failure which may be related. I'll investigate further before merging this. |
This turned out to be some unsound user code compiled with dart2js unsound mode. Not an issue with this change. Merging. |
@osa1 - I'm seeing some issue w/ this change locally when generating the firestore API. For ...
static final $core.List<TargetChange_TargetChangeType?> _byValue = $pb.ProtobufEnum.$_initByValueList(values, 4);
... But the
It looks like |
@devoncarew right, sorry, I should've documented this in the changelog probably. This changes both the library and plugin. Library changes are backwards compatible, if you don't change your generated classes you can update the library. But if you update the plugin and re-generate your protos, then you need the new version of the library. So I think this should be a minor version bump in the library and major one in the plugin. |
When decoding an enum value, we call a callback in the enum field's
FieldInfo
. The callback then indexes a map mapping enum numbers to Dart values.When these conditions hold:
The known enum numbers are all positive. (so that we can use a list and index it with the number)
The known enum numbers are more than 70% of the large known enum number. (so that the list won't have a lot of
null
entries, wasting heap space)We now generate a list instead of a map to map enum numbers to Dart values.
Similar to the map, the list is runtime allocated. No new code generated per message or enum type.
AOT benchmarks:
protobuf_PackedEnumDecoding(RunTimeRaw): 47585.14 us.
protobuf_PackedEnumDecoding(RunTimeRaw): 38974.566666666666 us.
Wasm benchmarks:
protobuf_PackedEnumDecoding(RunTimeRaw): 52225.0 us.
protobuf_PackedEnumDecoding(RunTimeRaw): 34283.33333333333 us.
Alternatives considered:
valueOf
closure.valueOf
closure it stores an extra field inFieldInfo
s for whether to binary search or directly index.These are all slower than the current PR.
cl/757724889