Closed adam-dot-cohen closed 1 year ago
Tagging subscribers to this area: @dotnet/area-system-buffers See info in area-owners.md if you want to be subscribed.
Author: | adam-dot-cohen |
---|---|
Assignees: | - |
Labels: | `api-suggestion`, `area-System.Buffers`, `untriaged` |
Milestone: | - |
Would be nice to have a ref struct flavor as well. Or, at least a few convenience methods on SpanWrite
, TryWrite
, etc...).
I see DotNext presented in Benchmark) SparseBufferWriter
doesn't implement contiguous buffer so its comparison with ArrayBufferWriter
a bit incorrect. @adam-dot-cohen , PoolingBufferWriter is better alternative that exposes contiguous memory block. Also, it already implements IMemoryOwner<T>
.
@SpicyBits , ref counterpart is also presented as BufferWriterSlim from DotNext.
@sakno - agreed on SparseBufferWriter
it's not apples to apples. I switched the benchmarks to use PoolingBufferWriter
. See benchmarks below...
Big fan of the DotNext library. This type is just a toy, not buffer growth optimization, etc... If you think it makes any sense in DotNext, feel free to copy it from the repo, or I'd be happy to pay the value I've received from DotNext backward and create a PR for you. Keep up the awesome work on the library!
Code can be found here...
Code can be found here... https://github.com/adam-dot-cohen/ResizableSpanWriter
BenchmarkDotNet=v0.13.4, OS=Windows 11 (10.0.22621.2283)
Intel Core i9-10980XE CPU 3.00GHz, 1 CPU, 36 logical and 18 physical cores
.NET SDK=8.0.100-preview.6.23330.14
[Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2
Job-YJBPHY : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2
Jit=RyuJit Runtime=.NET 7.0 Arguments=/p:Optimize=true
InvocationCount=1 LaunchCount=1 RunStrategy=Throughput
UnrollFactor=1
| Method | TotalCount | Mean | Error | Ratio | Allocated | Alloc Ratio |
|------------------------------------ |----------- |-----------:|----------:|------:|----------:|------------:|
| 'Proposed MemoryBufferWriter' | 100 | 1.815 us | 0.0856 us | 1.00 | 992 B | 1.00 |
| 'High Perf Toolkit ArrayPoolWriter' | 100 | 1.841 us | 0.0808 us | 1.03 | 976 B | 0.98 |
| 'DotNext PooledBufferWriter' | 100 | 6.345 us | 0.3128 us | 3.56 | 1328 B | 1.34 |
| 'MS RecyclableMemoryStream' | 100 | 6.086 us | 0.2201 us | 3.43 | 872 B | 0.88 |
| | | | | | | |
| 'Proposed MemoryBufferWriter' | 1000 | 2.192 us | 0.0912 us | 1.00 | 992 B | 1.00 |
| 'High Perf Toolkit ArrayPoolWriter' | 1000 | 2.248 us | 0.0632 us | 1.03 | 976 B | 0.98 |
| 'DotNext PooledBufferWriter' | 1000 | 6.730 us | 0.3361 us | 3.12 | 1328 B | 1.34 |
| 'MS RecyclableMemoryStream' | 1000 | 6.458 us | 0.2275 us | 2.99 | 872 B | 0.88 |
| | | | | | | |
| 'Proposed MemoryBufferWriter' | 10000 | 2.437 us | 0.0906 us | 1.00 | 992 B | 1.00 |
| 'High Perf Toolkit ArrayPoolWriter' | 10000 | 3.628 us | 0.1447 us | 1.51 | 976 B | 0.98 |
| 'DotNext PooledBufferWriter' | 10000 | 8.089 us | 0.3166 us | 3.37 | 1328 B | 1.34 |
| 'MS RecyclableMemoryStream' | 10000 | 9.856 us | 0.2774 us | 4.09 | 872 B | 0.88 |
| | | | | | | |
| 'Proposed MemoryBufferWriter' | 100000 | 7.320 us | 0.1485 us | 1.00 | 992 B | 1.00 |
| 'High Perf Toolkit ArrayPoolWriter' | 100000 | 13.107 us | 0.2647 us | 1.79 | 976 B | 0.98 |
| 'DotNext PooledBufferWriter' | 100000 | 19.373 us | 0.3802 us | 2.63 | 1328 B | 1.34 |
| 'MS RecyclableMemoryStream' | 100000 | 43.500 us | 0.8456 us | 5.92 | 872 B | 0.88 |
| | | | | | | |
| 'Proposed MemoryBufferWriter' | 1000000 | 54.994 us | 1.0460 us | 1.00 | 992 B | 1.00 |
| 'High Perf Toolkit ArrayPoolWriter' | 1000000 | 105.674 us | 1.7592 us | 1.93 | 976 B | 0.98 |
| 'DotNext PooledBufferWriter' | 1000000 | 140.283 us | 0.3657 us | 2.55 | 1328 B | 1.34 |
| 'MS RecyclableMemoryStream' | 1000000 | 380.883 us | 1.8720 us | 6.92 | 872 B | 0.88 |
@adam-dot-cohen , it's better to continue conversation to dotnext repo. However, you can already achieve what you want with PoolingBufferWriter
class and MemoryAllocator.GetArrayAllocator<T>()
method to use regular arrays instead of pooling. Also, you can provide your own MemoryAllocator<T>
delegate to override allocation strategy.
Closing this out and moving converation to DotNext
Background and motivation
High-performance, low-allocation, heap-based convience type for constructing Memory structures - implementing IBufferWriter / IMemoryOwner. And manages arbitrarry backing ArrayPool rental length on behalf of user.
API Proposal
API Usage
Alternative Designs
The growth increment could definitely be optimized based upon avg size written via linear growth, exponentiation or logistic regression/additive equation.
Risks
Reference implementation...
https://github.com/adam-dot-cohen/ResizableSpanWriter