- Windows 11
- AMD Ryzen 5 5500
- 16 GB RAM
- MSVC 19.43 (Release /DWIN32 /D_WINDOWS /W3 /GR /EHsc /MD /O2 /Ob2 /DNDEBUG -std:c++latest)
- llvm-MinGW 18.1.6 (-O3 -DNDEBUG -std=gnu++23 -fansi-escape-codes -fcolor-diagnostics)
- MinGW 13 (-O3 -DNDEBUG -std=gnu++23 -fdiagnostics-color=always)
- MinGW 14 (-O3 -DNDEBUG -std=gnu++23 -fdiagnostics-color=always)
If you think it's unfair to compare MSVC's O2 with O3 from other compilers...that's what I get when I configure "Release build" with C++23
Input is filled with numbers 1..size (std::iota)
Intermediate values are generated by creating an iota again for each number:
1 -> 1
2 -> 1,2
3 -> 1,2,3
etc.
This is done with 3 levels of nesting, so
3 -> 1,2,3 -> 1,1,2,1,2,3 -> 1,1,1,2,1,1,2,1,2,3
etc.
This blows up the intermediate range nicely in a fashion that is not deterministic in order (because the original input is random_shuffle'd every time) but that is deterministic in which values exist.
Then, the intermediate values are folded to a single result, the results collected, summed up and averaged, and the final result printed. The point of the exercise is to prevent the compiler from optimizing away the whole loop (which might happen if the results were never used, for example).
Only the segment which calculates the per-iteration result from the initial input is benchmarked.
- Classic loop to build intermediate vector, loop to accumulate result
- Same as (1), but with a pre-allocated vector
- Classic loop with integrated accumulation of final result
- Create view on intermediate range, loop to accumulate result
- Like (1), but use a view to build the initial vector
- 50 items in initial range
- 1000 Iterations
| | MSVC 19.43 | llvm-MinGW 18.1.6 | MinGW 13 | MinGW 14 |
| ------------------------------------------- | ---------- | ----------------- | -------- | -------- |
| Calculate result in loop | 61 | 50 | 73 | 73 |
| Build view→Reduce to result | 307 | 662 | 169 | 178 |
| Build vector in loop→Reduce | 893 | 1240 | 782 | 883 |
| Like above, but with preallocated vector | 557 | 406 | 378 | 367 |
| View→Vector→Reduce | 1130 | 1880 | 829 | 954 |
| | MSVC 19.43 | llvm-MinGW 18.1.6 | MinGW 13 | MinGW 14 |
| ------------------------------------------- | ---------- | ----------------- | -------- | -------- |
| Calculate result in loop | Baseline | | | |
| Build view→Reduce to result | 5,03 | 13,24 | 2,32 | 2,44 |
| Build vector in loop→Reduce | 14,64 | 24,80 | 10,71 | 12,10 |
| Like above, but with preallocated vector | 9,13 | 8,12 | 5,18 | 5,03 |
| View→Vector→Reduce | 18,52 | 37,60 | 11,36 | 13,07 |
| | MSVC 19.43 | llvm-MinGW 18.1.6 | MinGW 13 | MinGW 14 |
| ------------------------------------------- | ---------- | ----------------- | -------- | -------- |
| Calculate result in loop | 0,21 | 0,17 | 0,25 | 0,25 |
| Build view→Reduce to result | 1,05 | 2,26 | 0,58 | 0,61 |
| Build vector in loop→Reduce | 3,05 | 4,23 | 2,67 | 3,02 |
| Like above, but with preallocated vector | 1,90 | 1,39 | 1,29 | 1,25 |
| View→Vector→Reduce | 3,86 | 6,42 | 2,83 | 3,26 |
An input value is passed into a function with 5 gates.
Each gate accepts everything BUT a certain value:
- Gate 1 rejects '1' but accepts any other number
- Gate 2 rejects '2' but accepts any other number
...and so on.
If a value is accepted, it is forwarded to the next gate. This is repeated for a number of input values and iterations. Because of the way the gates are configured, you can create a random sequence of numbers with a statistically clear rejection rate: Just choose the desired ration between numbers 1 to 5 (rejected) and numbers above 5 (accepted).
So, an all happy-path run will have (different) numbers 6 or higher. And all-sad-path run will have numbers 1 to 5. A 50:50 happy/sad path run will have an equal distribution of numbers 1 to 10.
- Baseline (only simulated work, no error handling)
- Value-based: bool returned from function indicates success or failure
- std::expected with and_then
- std::optional with and_then
- Exceptions
- Input length: 10000 (each number passing through up to 5 gates, depending on rejection rate)
- Number of iterations: 100
- Rejection rates: 0%, 2%, 100%
| | | 0% rejection | 2% rejection | 100% rejection |
| ----------------- | ------------- | ------------ | ------------ | -------------- |
| MinGW 14 | Value-based | 2130 | 2130 | 2130 |
| llvm-MinGW 18.1.6 | Value-based | 2840 | 2840 | 2840 |
| MSVC 19.43 | Value-based | 9020 | 9580 | 7660 |
| | | | | |
| MinGW 14 | std::optional | 2130 | 2450 | 5900 |
| llvm-MinGW 18.1.6 | std::optional | 3110 | 3750 | 3070 |
| MSVC 19.43 | std::optional | 9860 | 1060 | 12250 |
| | | | | |
| MinGW 14 | std::expected | 2140 | 2430 | 5610 |
| llvm-MinGW 18.1.6 | std::expected | 4400 | 4060 | 7020 |
| MSVC 19.43 | std::expected | 10240 | 10820 | 11610 |
| | | | | |
| MinGW 14 | Exceptions | 3930 | 28660 | 1320000 |
| llvm-MinGW 18.1.6 | Exceptions | 3070 | 4157 | 1920000 |
| MSVC 19.43 | Exceptions | 9270 | 52920 | 2230000 |