-
Notifications
You must be signed in to change notification settings - Fork 56
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
Investigate Improvements to NatsMsg<T> struct size #282
Comments
Potentially another piece of savings: I know it'd be a breaking API change - but do we really need |
I can think of reasons but I'm not a normal end user 😅. Getting rid of |
I think this is the worst type of breaking change where you remove a feature. We might get away with it because it's early days. |
I guess I can see the benefits of
As an optimization could maintain a look-up table of the last N (maybe 1000?) messages that were received on the connection, so calling |
Interesting idea!
How? serialize previously deserialized message data? this is not always invertible.
How do we identify a given message? ...just trying to understand 😃 |
Just a note on the message size: it's there because we needed to check the total size for pull requests when consuming. Also, what about connection? Can't we have something like |
Yes, that's what we would have to do. True, we don't track the Deserializer (which may not even have complementing Serializer) so you'd either have to supply the matching Serializer that tyou used, or get the connection default
Wouldn't be trivial. We'd have to use some sort of look-up on a hash of the Data object or something, and we could easily have misses in that case.
I guess we could, but then we'd lose the convenience of being able to do
Maybe we should just leave it then, and note that it's only automatically set when a message is received. Same with |
FWIW, This is the current Object Layouts, based on the struct as of 5f9dc5a on x64:
|
I did just have a possibly -terrifying- idea... Still need to check if this works with how Jetstream uses things but... Idea being, We could use a Wrapper around ChannelReader to only provide the final This wouldn't necessarily shrink the final size of Edit: Looks Like |
I guess if we take it a step further, we could also wait to call
One benefit I could see here is that de-serialization would happen outside of the Read Processing Loop, so it could make more use of more cores. |
Two concerns with that:
However I think I got some inspiration today... I'll update my gist over the weekend or just push a branch out for feedback. |
Proposed change
In #276, the size of
NatsMsg<T>
came up as a concern for large buffer sizes. @mtmk asked the question:The zero-allocation case is worthy (As well as keeping our APIs stable,) so it may be worth investigating opportunities to save on the size of the struct, while only paying a (smaller) allocation penalty for additional features.
Essentially, what we can do is, rather than have separate
ReplyTo
,Headers
, andConnection
properties, use a singleprivate object _data
field that acts as a form of DU; If only one of the above is needed, it is placed directly in the field; if the additional fields are needed the appropriate container object(s) are allocated. The getters and initters of course will have to deal with the behavior of the DU (i.e. as-type-checking to get to the appropriate result), but that shaves off 16b from the struct (at a cost of up to 16-24b+header alloced for unhappy cases)Use case
The big win for the trouble is a 33% reduction in size of our struct (Based on showing 48 bytes for
NatsMsg<string>
via ObjectLayoutInspector). Our new struct size of 32b is a nice power of two as well, so we're also less likely to have to cross cache lines on writes (also, now 2 to a line instead of 1.? to a line) and reads toBoundedChannel<T>
's underlying buffer.The potential downsides:
a. I don't think this is a huge issue, the BCL does similar things frequently enough for structs
a. It's likely that for everyone's sake, it is better for us to have just one 'union' value which would mean each message using more than one 'extra' field will now cause allocation.
Contribution
I am happy to contribute, including building a proof of concept branch if it will aid in helping decide whether this proposal is viable.
The text was updated successfully, but these errors were encountered: