Commit 9fa20b9
NCBC-3904: Improve OperationBuilder perf using IBufferWriter<byte>
Motivation
----------
Using the `IBufferWriter<T>` pattern can significantly improve
performance, especially when using the System.Text.Json serializer, by
avoiding unnecessary intermediate buffers which add additional heap
allocations and memory copies.
Modifications
-------------
- Refactor `OperationBuilder` to expose the `IBufferWriter<byte>`
interface and to use backing buffers from the ArrayPool instead of a
backing `MemoryStream`
- Update `RawJsonTranscoder` and `RawStringTranscoder` to write strings
directly to the `OperationBuilder` rather than an intermediate buffer
or a `StreamWriter` with intermediate buffers
- When binary data is encoded using `RawBinaryTranscoder` or
`RawJsonTranscoder` on down-level runtimes, use `IBufferWriter<byte>`
to write directly to the `OperationBuilder` rather than trying to
extract an array and possibly needing to use an intermediate buffer
- When serializing JSON to `OperationBuilder` via `DefaultTranscoder` or
`JsonTranscoder`, if the serializer supports `IBufferedTypeSerializer`
and is not the Newtonsoft serializer then serialize via
`IBufferWriter<byte>`.
- Refactor all operations to build themselves using
`IBufferWriter<byte>` methods instead of intermediate arrays or
stackallocs.
- Refactor ramge scan and sampling scan to serialize themselves using
`IBufferWriter<byte>`
- Add a suite of unit tests for `OperationBuilder`
- Add an option to enable Snappy compression when running integration
tests
- Fixup benchmarks and add a `RawStringTranscoder` benchmark
- Target the .NET 8 SDK via global.json to keep behaviors consistent
until we choose to use the .NET 9 SDK for builds
Results
-------
General performance improvements on key/value operations, most notably
when using the System.Text.Json serializer, which offers approximately
a 20% reduction that generally scales with the size of the document.
BenchmarkDotNet v0.13.12, Windows 11 (10.0.26100.2314)
Unknown processor
.NET SDK 9.0.100
[Host] : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
Job-UITXDI : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
Runtime=.NET 8.0 Toolchain=net8.0
| Method | DocSize | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen0 | Allocated | Alloc Ratio |
|------- |-------- |-------------:|-----------:|-----------:|------:|--------:|-----:|-------:|----------:|------------:|
| Old | 128 | 114.92 ns | 0.495 ns | 0.463 ns | 1.00 | 0.00 | 2 | - | - | NA |
| New | 128 | 88.35 ns | 0.238 ns | 0.211 ns | 0.77 | 0.00 | 1 | 0.0095 | 120 B | NA |
| | | | | | | | | | | |
| Old | 10240 | 1,183.61 ns | 6.884 ns | 6.102 ns | 1.00 | 0.00 | 2 | 0.0114 | 152 B | 1.00 |
| New | 10240 | 956.63 ns | 6.595 ns | 6.169 ns | 0.81 | 0.01 | 1 | 0.0095 | 120 B | 0.79 |
| | | | | | | | | | | |
| Old | 131072 | 15,293.04 ns | 256.661 ns | 240.081 ns | 1.00 | 0.00 | 2 | - | 376 B | 1.00 |
| New | 131072 | 12,716.78 ns | 106.768 ns | 94.647 ns | 0.83 | 0.02 | 1 | - | 120 B | 0.32 |
Strings passed via `RawStringTranscoder` or `RawJsonTranscoder` also see
improvements:
BenchmarkDotNet v0.13.12, Windows 11 (10.0.26100.2314)
Unknown processor
.NET SDK 9.0.100
[Host] : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
Job-AVGOBO : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
Runtime=.NET 8.0 Toolchain=net8.0
| Method | Length | Mean | Error | StdDev | Ratio | Rank | Gen0 | Allocated | Alloc Ratio |
|------- |------- |------------:|----------:|----------:|------:|-----:|-------:|----------:|------------:|
| Old | 128 | 46.01 ns | 0.120 ns | 0.112 ns | 1.00 | 2 | - | - | NA |
| New | 128 | 27.09 ns | 0.273 ns | 0.255 ns | 0.59 | 1 | - | - | NA |
| | | | | | | | | | |
| Old | 10240 | 809.19 ns | 5.271 ns | 4.930 ns | 1.00 | 2 | 0.4244 | 5328 B | 1.00 |
| New | 10240 | 477.67 ns | 1.698 ns | 1.589 ns | 0.59 | 1 | - | - | 0.00 |
| | | | | | | | | | |
| Old | 131072 | 8,083.21 ns | 44.767 ns | 39.685 ns | 1.00 | 2 | 0.4120 | 5328 B | 1.00 |
| New | 131072 | 7,896.41 ns | 61.938 ns | 54.906 ns | 0.98 | 1 | - | - | 0.00 |
Additionally, the backing buffers for the OperationBuilder are returned
to the ArrayPool when the builder is resized or the builder is disposed,
unlike the previous case where the backing MemoryStream used arrays that
were simply garbage collected. This was mitigated somewhat before by the
pool of OperationBuilders, but the pool would still sometimes discard
builders or create new builders that needed to scale up for larger
operations.
Change-Id: Id8ca603b9de9cdfa81b03282597408a2e737659b
Reviewed-on: https://review.couchbase.org/c/couchbase-net-client/+/219679
Reviewed-by: Jeffry Morris <jeffrymorris@gmail.com>
Tested-by: Build Bot <build@couchbase.com>1 parent 28491ad commit 9fa20b9
39 files changed
Lines changed: 1196 additions & 281 deletions
File tree
- src/Couchbase
- Core/IO
- Operations
- Errors
- RangeScan
- SubDocument
- Serializers/SystemTextJson
- Transcoders
- KeyValue/RangeScan
- Utils
- tests
- Couchbase.IntegrationTests
- Couchbase.LoadTests
- Core/IO
- Operations
- Transcoder
- Helpers
- Couchbase.Test.Common/Fixtures
- Couchbase.UnitTests
- Core/IO/Operations
- KeyValue/KeyRange
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | | - | |
| 77 | + | |
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
16 | | - | |
17 | | - | |
| 15 | + | |
18 | 16 | | |
19 | 17 | | |
20 | 18 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
20 | | - | |
| 19 | + | |
21 | 20 | | |
22 | | - | |
23 | | - | |
| 21 | + | |
| 22 | + | |
24 | 23 | | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
| 24 | + | |
| 25 | + | |
38 | 26 | | |
| 27 | + | |
| 28 | + | |
39 | 29 | | |
40 | 30 | | |
41 | 31 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
20 | 22 | | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
25 | 38 | | |
26 | 39 | | |
27 | 40 | | |
| |||
Lines changed: 4 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
44 | | - | |
45 | | - | |
| 43 | + | |
46 | 44 | | |
47 | 45 | | |
48 | 46 | | |
| |||
51 | 49 | | |
52 | 50 | | |
53 | 51 | | |
54 | | - | |
55 | 52 | | |
56 | | - | |
| 53 | + | |
| 54 | + | |
57 | 55 | | |
58 | 56 | | |
59 | 57 | | |
60 | 58 | | |
61 | 59 | | |
62 | 60 | | |
63 | 61 | | |
64 | | - | |
| 62 | + | |
65 | 63 | | |
66 | 64 | | |
67 | 65 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
46 | 45 | | |
47 | | - | |
48 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
| |||
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
68 | | - | |
| 69 | + | |
69 | 70 | | |
70 | 71 | | |
71 | 72 | | |
| |||
0 commit comments