[CSHARP-2505] Reduce array allocations in BsonStreamAdapter Created: 06/Feb/19  Updated: 08/Jun/23

Status: Backlog
Project: C# Driver
Component/s: Performance, Serialization
Affects Version/s: 2.7.3
Fix Version/s: None

Type: Improvement Priority: Major - P3
Reporter: Daniel Hegener Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: PNG File image-2019-02-06-14-01-41-774.png    
Issue Links:
Related

 Description   

BsonStreamAdapter already uses some kind of an array reuse concept using the _tempUtf8 variable. This approach could, however, be significantly improved by dropping this helper variable and instead switching to System.Buffers.ArrayPool<byte>. The reason being that e.g. for the UTF8 conversions in WriteString() and WriteCString, the buffers are only needed for a very short time and can immediately (at the end of the very same method) be returned back to the pool once the buffer has been written into the output stream.



 Comments   
Comment by Daniel Hegener [ 12/Feb/19 ]

I was thinking about how to implement this and came up with a bunch of options:

1) Simply increase the length of the existing _tempUtf8 array to something considerably bigger than its current length of 128 bytes. This would probably not hurt in any noticeable way and it would reduce memory allocations for documents that contain "larger" strings. But it would not eliminate the memory problem in cases of "really large" strings (as in strings too long to fit into the increased _tempUtf8 buffer).

2) Reference System.Buffers (which would be a new dependency) and then

    a) replace the _tempUtf8 buffer completely with an ArrayPool<byte>.Shared.Rent'ed buffer. I personally like the cleanliness of this approach but (beyond adding a new dependency) it introduces a little overhead (ArrayPool e.g. does some magic to ensure thread-safety)

    b) keep the _tempUtf8 buffer but use the ArrayPool approach only for all places where we currently do a "new byte[some length]" call. This would increase the complexity of the code a little and would still keep the optimization for small strings using the hardcoded limit of 128 bytes which feels a bit arbitrary.

3) A combination of 1 and 2b where the _tempUt8 buffer would be increased and only for even larger strings the ArrayPool concept would kick in.

4) Some magic hand-written ArrayPool implementation which would not force us to reference System.Buffers and which could be fine-tuned.

Comments anyone?

Comment by Daniel Hegener [ 06/Feb/19 ]

Here is the corresponding profiler output after the serialization of 2m+ complex documents:

Generated at Wed Feb 07 21:42:44 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.