https://github.com/astef/benchmark-dotnet-boxing
Benchmark of the real overhead (including GC) of the value types boxing compared to direct serialization into the byte array.
https://github.com/astef/benchmark-dotnet-boxing
benchmark benchmarkdotnet boxing csharp dotnet-core garbage-collection optimization serialization
Last synced: 5 months ago
JSON representation
Benchmark of the real overhead (including GC) of the value types boxing compared to direct serialization into the byte array.
- Host: GitHub
- URL: https://github.com/astef/benchmark-dotnet-boxing
- Owner: astef
- License: mit
- Created: 2020-09-01T01:54:58.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2020-09-01T17:48:21.000Z (over 5 years ago)
- Last Synced: 2025-06-01T09:52:48.460Z (7 months ago)
- Topics: benchmark, benchmarkdotnet, boxing, csharp, dotnet-core, garbage-collection, optimization, serialization
- Language: C#
- Homepage:
- Size: 13.7 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# benchmark-dotnet-boxing
Benchmark of the real overhead (including GC) of the value types boxing compared to direct serialization into the byte array.
After the performance-critical synchronous procedure, there's basically two non-blocking ways of sharing the result of processing with other threads:
- populate some kind of generic buffer, e.g. `object[]`
- easy to implement (every value is an `object` already)
- extra boxing cost in the main procedure for every value in the message
- extra collection cost in the application for every value in the message
- populate value-typed buffer, e.g. `byte[]`
- need to implement serialization for every possible type (can be tricky for custom reference types)
- extra serialization cost in the main procedure for every value in the message
- no boxing, no collection cost
The goal is to find out the optimum conditions for selecting one of the above strategies.
## Results
``` ini
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1016 (1909/November2018Update/19H2)
Intel Core i7-7820HQ CPU 2.90GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.301
[Host] : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT
Job-IKJAKY : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT
IterationCount=5 WarmupCount=1
```
| Method | IterCount | MessageNum | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------- |---------- |----------- |-------------:|-------------:|------------:|--------------:|--------------:|--------------:|-------------:|
| **RunWithObjects** | **16000** | **5000** | **9,718.0 ms** | **93.25 ms** | **24.22 ms** | **1186000.0000** | **593000.0000** | **593000.0000** | **7324.59 MB** |
| RunWithBytes | 16000 | 5000 | 4,421.4 ms | 46.79 ms | 7.24 ms | 593000.0000 | 593000.0000 | 593000.0000 | 1831.42 MB |
| **RunWithObjects** | **16000** | **10000** | **19,459.8 ms** | **218.79 ms** | **56.82 ms** | **2286000.0000** | **1143000.0000** | **1143000.0000** | **14648.8 MB** |
| RunWithBytes | 16000 | 10000 | 8,881.2 ms | 294.69 ms | 76.53 ms | 1143000.0000 | 1143000.0000 | 1143000.0000 | 3662.48 MB |
| **RunWithObjects** | **16000** | **20000** | **39,103.3 ms** | **673.01 ms** | **174.78 ms** | **4572000.0000** | **2286000.0000** | **2286000.0000** | **29297.24 MB** |
| RunWithBytes | 16000 | 20000 | 17,892.4 ms | 471.22 ms | 72.92 ms | 2286000.0000 | 2286000.0000 | 2286000.0000 | 7324.59 MB |
| **RunWithObjects** | **16000** | **40000** | **93,927.5 ms** | **1,691.43 ms** | **439.26 ms** | **9777000.0000** | **6221000.0000** | **3556000.0000** | **58594.93 MB** |
| RunWithBytes | 16000 | 40000 | 35,392.0 ms | 457.73 ms | 70.83 ms | 4000000.0000 | 4000000.0000 | 4000000.0000 | 14648.8 MB |
| **RunWithObjects** | **16000** | **80000** | **215,643.5 ms** | **1,526.78 ms** | **396.50 ms** | **18644000.0000** | **11494000.0000** | **7019000.0000** | **117188.79 MB** |
| RunWithBytes | 16000 | 80000 | 71,040.3 ms | 631.03 ms | 163.88 ms | 8000000.0000 | 8000000.0000 | 8000000.0000 | 29297.24 MB |
| **RunWithObjects** | **16000** | **160000** | **447,694.4 ms** | **2,373.70 ms** | **616.44 ms** | **38338000.0000** | **24914000.0000** | **15998000.0000** | **234377.38 MB** |
| RunWithBytes | 16000 | 160000 | 140,678.0 ms | 1,639.81 ms | 253.76 ms | 16000000.0000 | 16000000.0000 | 16000000.0000 | 58594.12 MB |
## Conclusions
For the provided message structure and size (three `long` fields), direct serialization is at least two times faster than the cost of boxing.
The bigger the number of messages, the bigger the difference factor (non-linear dependency): x2.2 for 5k, and x3.2 for 160k.
Allocated memory difference is proportional to the number of messages.
Different number of iterations, didn't reveal any effects (was excluded from results for brevity).