Fixed ArrayPoolBufferWriter<T> repeated new[] allocations #3524
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
PR Type
What kind of change does this PR introduce?
What is the current behavior?
The
ArrayPoolBufferWriter<T>
uses the givenArrayPool<T>
instance to resize its internal buffer when needed, which only works as expected when the array itself is small. The issue is that theArrayPool<T>.Shared
instance has an internal threshold set to1024 * 1024
, over which it just allocates new arrays every time to avoid keeping very large arrays alive for a long time. That is perfectly fine, except for one little detail: once you get past that threshold,ArrayPool<T>.Shared
stops rounding up the requested size. It instead returns an array withnew[]
of exactly the requested size, which absolutely kills the performance when used in a writer type likeArrayPoolBufferWriter<T>
: this means that as soon as we get past tha threshold, we'll basically end up resizing the whole array for every single new write operation, no matter how large it is. That's super bad for performance and memory usage 🥺What is the new behavior?
The solution for this is pretty simple, this PR includes a simple check for the requested size, and if that's over
1024 * 1024
it just rounds that up to the closest power of 2, so that the array size will effectively just keep being multiplied by 2 every time. This has a huge performance impact when eg. trying to use theArrayPoolBufferWriter<T>
class to write a 10MB buffer, 8KB at a time:In this simple benchmark alone, the updated version is 156x faster and uses 190x less memory 😄
Of course, results will vary a lot on the specific workload, but you can imagine the impact being even more dramatic when working with larger buffers, or with less items being written at any given time. With this change in general, users will not have to worry about the size of the data being written, and the class will automatically use the right approach in all cases.
PR Checklist
Please check if your PR fulfills the following requirements:
Pull Request has been submitted to the documentation repository instructions. Link:Sample in sample app has been added / updated (for bug fixes / features)Icon has been created (if new sample) following the Thumbnail Style Guide and templates