Commit d434f43
msftbot[bot]
Fixed ArrayPoolBufferWriter<T> repeated new[] allocations (#3524)
## PR Type
What kind of change does this PR introduce?
<!-- Please uncomment one or more that apply to this PR. -->
- Optimization
<!-- - Bugfix -->
<!-- - Feature -->
<!-- - Code style update (formatting) -->
<!-- - Refactoring (no functional changes, no api changes) -->
<!-- - Build or CI related changes -->
<!-- - Documentation content changes -->
<!-- - Sample app changes -->
<!-- - Other... Please describe: -->
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
The `ArrayPoolBufferWriter<T>` uses the given `ArrayPool<T>` instance to resize its internal buffer when needed, which only works as expected when the array itself is small. The issue is that the `ArrayPool<T>.Shared` instance has an internal threshold set to `1024 * 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 with `new[]` of **exactly the requested size**, which absolutely kills the performance when used in a writer type like `ArrayPoolBufferWriter<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?
<!-- Describe how was this issue resolved or changed? -->
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 the `ArrayPoolBufferWriter<T>` class to write a 10MB buffer, 8KB at a time:
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------------- |-------------:|-----------:|-----------:|------:|------------:|------------:|------------:|-----------:|
| Before | 1,380.236 ms | 19.9806 ms | 17.7122 ms | 1.000 | 355000.0000 | 355000.0000 | 355000.0000 | 5754.44 MB |
| After | **8.820 ms** | 0.1757 ms | 0.4838 ms | **0.006** | 640.6250 | 640.6250 | 640.6250 | **30 MB** |
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:
- [X] Tested code with current [supported SDKs](../readme.md#supported)
- [ ] ~~Pull Request has been submitted to the documentation repository [instructions](..\contributing.md#docs). Link: <!-- docs PR 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](https://github.com/windows-toolkit/WindowsCommunityToolkit-design-assets)~~
- [X] Tests for the changes have been added (for bug fixes / features) (if applicable)
- [X] Header has been added to all new source files (run *build/UpdateHeaders.bat*)
- [X] Contains **NO** breaking changesFile tree
5 files changed
+120
-36
lines changed- Microsoft.Toolkit.HighPerformance
- Buffers
- Helpers/Internals
5 files changed
+120
-36
lines changedLines changed: 30 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
233 | 234 | | |
234 | 235 | | |
235 | 236 | | |
236 | | - | |
| 237 | + | |
237 | 238 | | |
238 | 239 | | |
239 | 240 | | |
240 | 241 | | |
241 | 242 | | |
242 | 243 | | |
243 | 244 | | |
244 | | - | |
| 245 | + | |
245 | 246 | | |
246 | 247 | | |
247 | 248 | | |
| |||
251 | 252 | | |
252 | 253 | | |
253 | 254 | | |
254 | | - | |
| 255 | + | |
255 | 256 | | |
256 | | - | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
257 | 260 | | |
258 | 261 | | |
259 | 262 | | |
| |||
268 | 271 | | |
269 | 272 | | |
270 | 273 | | |
271 | | - | |
| 274 | + | |
272 | 275 | | |
273 | | - | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
274 | 279 | | |
275 | | - | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
276 | 297 | | |
| 298 | + | |
| 299 | + | |
277 | 300 | | |
278 | 301 | | |
279 | 302 | | |
| |||
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
247 | 252 | | |
248 | 253 | | |
249 | 254 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | 8 | | |
12 | 9 | | |
13 | 10 | | |
14 | 11 | | |
15 | 12 | | |
16 | 13 | | |
| 14 | + | |
17 | 15 | | |
18 | 16 | | |
19 | 17 | | |
| |||
79 | 77 | | |
80 | 78 | | |
81 | 79 | | |
82 | | - | |
83 | | - | |
| 80 | + | |
| 81 | + | |
84 | 82 | | |
85 | 83 | | |
86 | 84 | | |
| |||
130 | 128 | | |
131 | 129 | | |
132 | 130 | | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | 131 | | |
158 | 132 | | |
159 | 133 | | |
| |||
Lines changed: 43 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
Lines changed: 39 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
20 | 59 | | |
21 | 60 | | |
22 | 61 | | |
| |||
0 commit comments