Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension methods for combining multiple results into one #18

Merged
merged 9 commits into from
Jul 6, 2024

Conversation

PressXtoChris
Copy link
Contributor

@PressXtoChris PressXtoChris commented Jul 2, 2024

Closes jscarle/LightResults#32

What's the change?

Introducing extension methods to combine multiple results into one

Why is it wanted?

There are some situations where a developer might be concurrently calling multiple methods that return results (e.g. Task.WhenAll), or have a method that calls an operation multiple times (e.g. yield returning into an IEnumerable). Rather than require the developer to inspect each result individually, we can combine them into a single result object for easy processing.

How is it implemented?

Add two new extension methods: Result Collect(this IEnumerable<Result> results) and Result<IReadOnlyList<T>> Collect(this IEnumerable<Result<T>> results). These will turn the enumerable into an array (if it wasn't already an array), then turn that array into a ReadOnlySpan because they're faster to iterate over. We build up a list of errors that all the results had, and return them in a failed result. If there were no failures, we return an OK result (with a ReadOnlyList containing all the values if it was Result<T>).

Added a new LightResults.Extensions.Operations project to the solution to house these new extension methods.

Benchmarking

I've experimented with a couple of different implementations, and from my benchmarks this seems to perform pretty well. I was using an array of 512 results, half of them successful and half of them failed.

Method Mean Error StdDev Median Allocated
Develop_Result_MergeWithLinqArray 308.7 us 18.70 us 52.42 us 330.2 us 163.83 KB
Develop_Result_MergeWithLinqArrayTValue 254.4 us 4.22 us 7.04 us 251.6 us 409.61 KB
Develop_Result_MergeWithLinqList 201.7 us 4.03 us 3.96 us 201.9 us 284.61 KB
Develop_Result_MergeWithLinqListTValue 416.7 us 24.70 us 72.44 us 448.9 us 406.64 KB
Develop_Result_MergeWithFor 201.7 us 15.15 us 39.64 us 223.7 us 242.42 KB
Develop_Result_MergeWithForTValue 199.9 us 2.94 us 2.46 us 199.1 us 386.25 KB
Develop_Result_MergeWithForSized 215.2 us 7.96 us 20.98 us 220.8 us 241.33 KB
Develop_Result_MergeWithForTValueSized 187.9 us 2.00 us 1.77 us 188.1 us 383.91 KB
Develop_Result_MergeWithSpan 119.6 us 1.85 us 1.73 us 119.2 us 120.78 KB
Develop_Result_MergeWithSpanTValueArray 123.1 us 1.54 us 1.29 us 123.0 us 141.33 KB
Develop_Result_MergeWithSpanUnsafe 119.8 us 1.89 us 1.58 us 119.1 us 120.78 KB
Develop_Result_MergeWithSpanUnsafeTValue 123.6 us 1.51 us 1.26 us 123.3 us 141.33 KB
  • The first four Linq ones were done using similar logic to what I've got in the OP of Extension method to collect IEnumerable<Result<T>> into Result<IEnumerable<T>> LightResults#32
  • The For vs ForSized comparisons are using a for loop, and comparing pre-sizing the error/value lists.
  • The Span ones are converting array to a ReadOnlySpan like I have in this PR. The Unsafe versions use MemoryMarshal.GetReference() and Unsafe.Add() to traverse the memory the results are at. Negligible speed differences, and probably not worth the risk even if they were faster.

Comparing to other libraries that offer similar functionality:

Method Categories Mean Ratio Allocated Alloc Ratio
A_LightResults_Result_MergeWithSpan D01: Merging multiple results 120.370 us 1.00 120.78 KB 1.00
B_FluentResults_Result_Merge D01: Merging multiple results 111.001 us 0.92 142.81 KB 1.18
A_LightResults_Result_MergeWithSpanTValue D02: Merging multiple results with values 123.427 us 1.00 141.33 KB 1.00
B_FluentResults_Result_MergeTValue D02: Merging multiple results with values 118.529 us 0.96 185.31 KB 1.31
E_Rascal_Result_Sequence D02: Merging multiple results with values 7.362 us 0.06 22.11 KB 0.16

This implementation is almost on par with FluentResults. Rascal's not really a fair comparison because Sequence() returns early with the first error it comes across, rather than getting all of them.

Let me know if you would like to add a Benchmarks project to this solution too; and I can commit the code for them

Naming

Not sure if you've got a preferred name for this operation. I borrowed Collect from Rust since that's what they use to combine Vec<Result<T, E>> into Result<Vec<T>, E>. I think Merge or Combine could also work (and might be a little more intuitive).

@PressXtoChris PressXtoChris marked this pull request as ready for review July 4, 2024 06:25
@jscarle
Copy link
Owner

jscarle commented Jul 4, 2024

Nevermind, it's my git that's being wonky.

@jscarle jscarle changed the base branch from main to develop July 4, 2024 23:08
@jscarle
Copy link
Owner

jscarle commented Jul 5, 2024

This is for 10, 100, 1,000, and 10,000 results respectively.

When I started, benchmarks looked like this:

| Method                         | Mean         | Allocated |
|------------------------------- |-------------:|----------:|
| Result_Collect_WithEnumerable  |     25.51 ns |     400 B |
| Result_Collect_WithEnumerable  |    157.13 ns |    3280 B |
| Result_Collect_WithEnumerable  |  1,355.77 ns |   32080 B |
| Result_Collect_WithEnumerable  | 63,027.64 ns |  320104 B |
| ResultT_Collect_WithEnumerable |     60.76 ns |     616 B |
| ResultT_Collect_WithEnumerable |    313.86 ns |    4936 B |
| ResultT_Collect_WithEnumerable |  2,950.18 ns |   48136 B |
| ResultT_Collect_WithEnumerable | 84,791.68 ns |  480181 B |

Now they look like this:

| Method                           | Mean         | Allocated |
|--------------------------------- |-------------:|----------:|
| Result_Collect_WithEnumerable    |     97.07 ns |     144 B |
| Result_Collect_WithEnumerable    |    839.76 ns |     144 B |
| Result_Collect_WithEnumerable    |  8,188.73 ns |     144 B |
| Result_Collect_WithEnumerable    | 80,876.02 ns |     144 B |
| ResultT_Collect_WithEnumerable   |     55.89 ns |     536 B |
| ResultT_Collect_WithEnumerable   |    284.62 ns |    4136 B |
| ResultT_Collect_WithEnumerable   |  2,682.77 ns |   40136 B |
| ResultT_Collect_WithEnumerable   | 40,578.89 ns |  400480 B |

@jscarle
Copy link
Owner

jscarle commented Jul 5, 2024

Don't know if you want to take another crack at it now?

@PressXtoChris
Copy link
Contributor Author

I've added the comparisons benchmarks project. That's an impressive jump you got

| Method                                          | Categories                                | Iterations | Mean          | Ratio | Allocated | Alloc Ratio |
|------------------------------------------------ |------------------------------------------ |----------- |--------------:|------:|----------:|------------:|
| A_LightResults_ResultEnumerable_Collect         | A01: Merging multiple results             | 10         |     119.38 ns |  1.00 |     144 B |        1.00 |
| A_LightResults_ResultReadOnlyList_Collect       | A01: Merging multiple results             | 10         |     119.13 ns |  1.00 |     144 B |        1.00 |
| B_FluentResults_ResultEnumerable_Merge          | A01: Merging multiple results             | 10         |     178.03 ns |  1.49 |     160 B |        1.11 |
| B_FluentResults_ResultReadOnlyList_Merge        | A01: Merging multiple results             | 10         |     178.77 ns |  1.50 |     160 B |        1.11 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultEnumerable_Collect         | A01: Merging multiple results             | 100        |   1,047.06 ns |  1.00 |     144 B |        1.00 |
| A_LightResults_ResultReadOnlyList_Collect       | A01: Merging multiple results             | 100        |   1,051.85 ns |  1.01 |     144 B |        1.00 |
| B_FluentResults_ResultEnumerable_Merge          | A01: Merging multiple results             | 100        |   1,171.92 ns |  1.12 |     160 B |        1.11 |
| B_FluentResults_ResultReadOnlyList_Merge        | A01: Merging multiple results             | 100        |   1,253.55 ns |  1.20 |     160 B |        1.11 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultEnumerable_Collect         | A01: Merging multiple results             | 1000       |   9,828.47 ns |  1.00 |     144 B |        1.00 |
| A_LightResults_ResultReadOnlyList_Collect       | A01: Merging multiple results             | 1000       |   9,944.09 ns |  1.01 |     144 B |        1.00 |
| B_FluentResults_ResultEnumerable_Merge          | A01: Merging multiple results             | 1000       |  11,994.10 ns |  1.22 |     160 B |        1.11 |
| B_FluentResults_ResultReadOnlyList_Merge        | A01: Merging multiple results             | 1000       |  12,364.58 ns |  1.26 |     160 B |        1.11 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultEnumerable_Collect         | A01: Merging multiple results             | 10000      |  99,640.99 ns |  1.00 |     144 B |        1.00 |
| A_LightResults_ResultReadOnlyList_Collect       | A01: Merging multiple results             | 10000      | 129,471.61 ns |  1.30 |     144 B |        1.00 |
| B_FluentResults_ResultEnumerable_Merge          | A01: Merging multiple results             | 10000      | 116,054.62 ns |  1.17 |     160 B |        1.11 |
| B_FluentResults_ResultReadOnlyList_Merge        | A01: Merging multiple results             | 10000      | 107,661.98 ns |  1.08 |     160 B |        1.11 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultTValueEnumerable_Collect   | A02: Merging multiple results with values | 10         |      46.64 ns |  1.00 |     416 B |        1.00 |
| A_LightResults_ResultTValueReadOnlyList_Collect | A02: Merging multiple results with values | 10         |      48.33 ns |  1.04 |     416 B |        1.00 |
| B_FluentResults_ResultTValue_Merge              | A02: Merging multiple results with values | 10         |     696.09 ns | 14.95 |    1232 B |        2.96 |
| B_FluentResults_ResultTValueReadOnlyList_Merge  | A02: Merging multiple results with values | 10         |     686.21 ns | 14.73 |    1232 B |        2.96 |
| E_Rascal_ResultEnumerable_Sequence              | A02: Merging multiple results with values | 10         |     104.72 ns |  2.34 |     264 B |        0.63 |
| E_Rascal_ResultReadOnlyList_Sequence            | A02: Merging multiple results with values | 10         |     101.74 ns |  2.18 |     264 B |        0.63 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultTValueEnumerable_Collect   | A02: Merging multiple results with values | 100        |     226.26 ns |  1.00 |    2936 B |        1.00 |
| A_LightResults_ResultTValueReadOnlyList_Collect | A02: Merging multiple results with values | 100        |     222.92 ns |  0.98 |    2936 B |        1.00 |
| B_FluentResults_ResultTValue_Merge              | A02: Merging multiple results with values | 100        |   4,765.19 ns | 21.14 |    7352 B |        2.50 |
| B_FluentResults_ResultTValueReadOnlyList_Merge  | A02: Merging multiple results with values | 100        |   4,829.44 ns | 21.45 |    7352 B |        2.50 |
| E_Rascal_ResultEnumerable_Sequence              | A02: Merging multiple results with values | 100        |     783.77 ns |  3.48 |    1232 B |        0.42 |
| E_Rascal_ResultReadOnlyList_Sequence            | A02: Merging multiple results with values | 100        |     792.34 ns |  3.51 |    1232 B |        0.42 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultTValueEnumerable_Collect   | A02: Merging multiple results with values | 1000       |   1,947.52 ns |  1.00 |   28136 B |        1.00 |
| A_LightResults_ResultTValueReadOnlyList_Collect | A02: Merging multiple results with values | 1000       |   1,938.50 ns |  1.00 |   28136 B |        1.00 |
| B_FluentResults_ResultTValue_Merge              | A02: Merging multiple results with values | 1000       |  45,780.86 ns | 23.66 |   68552 B |        2.44 |
| B_FluentResults_ResultTValueReadOnlyList_Merge  | A02: Merging multiple results with values | 1000       |  46,280.19 ns | 23.96 |   68552 B |        2.44 |
| E_Rascal_ResultEnumerable_Sequence              | A02: Merging multiple results with values | 1000       |   7,581.42 ns |  3.92 |    8472 B |        0.30 |
| E_Rascal_ResultReadOnlyList_Sequence            | A02: Merging multiple results with values | 1000       |   7,599.12 ns |  3.93 |    8472 B |        0.30 |
|                                                 |                                           |            |               |       |           |             |
| A_LightResults_ResultTValueEnumerable_Collect   | A02: Merging multiple results with values | 10000      |  49,855.28 ns |  1.00 |  280274 B |        1.00 |
| A_LightResults_ResultTValueReadOnlyList_Collect | A02: Merging multiple results with values | 10000      |  49,396.78 ns |  0.99 |  280280 B |        1.00 |
| B_FluentResults_ResultTValue_Merge              | A02: Merging multiple results with values | 10000      | 454,647.98 ns |  9.13 |  680553 B |        2.43 |
| B_FluentResults_ResultTValueReadOnlyList_Merge  | A02: Merging multiple results with values | 10000      | 468,210.10 ns |  9.35 |  680553 B |        2.43 |
| E_Rascal_ResultEnumerable_Sequence              | A02: Merging multiple results with values | 10000      |  77,996.20 ns |  1.56 |  131448 B |        0.47 |
| E_Rascal_ResultReadOnlyList_Sequence            | A02: Merging multiple results with values | 10000      |  78,409.02 ns |  1.58 |  131448 B |        0.47 |

@jscarle
Copy link
Owner

jscarle commented Jul 6, 2024

I improved performance and reduced memory allocations. I doubt we'll be able to do much better than this. So I'm pretty confident that this could be merged now, unless you've got more ideas.

Before:

| Method                                  | Iterations | Mean          | Allocated |
|---------------------------------------- |----------- |--------------:|----------:|
| Result_Collect_WithEnumerable_AllOk     | 10         |      94.80 ns |     144 B |
| Result_Collect_WithEnumerable_AllFailed | 10         |     162.61 ns |     440 B |
| Result_Collect_WithEnumerable_Mixed     | 10         |      95.19 ns |     144 B |
| Result_Collect_WithEnumerable_AllOk     | 100        |     836.16 ns |     144 B |
| Result_Collect_WithEnumerable_AllFailed | 100        |   1,390.70 ns |    2304 B |
| Result_Collect_WithEnumerable_Mixed     | 100        |     835.04 ns |     144 B |
| Result_Collect_WithEnumerable_AllOk     | 1000       |   8,093.71 ns |     144 B |
| Result_Collect_WithEnumerable_AllFailed | 1000       |  14,053.57 ns |   16712 B |
| Result_Collect_WithEnumerable_Mixed     | 1000       |   8,167.96 ns |     144 B |
| Result_Collect_WithEnumerable_AllOk     | 10000      |  80,839.38 ns |     144 B |
| Result_Collect_WithEnumerable_AllFailed | 10000      | 174,479.55 ns |  262723 B |
| Result_Collect_WithEnumerable_Mixed     | 10000      |  81,424.23 ns |     144 B |

After:

| Method                                  | Iterations | Mean          | Allocated |
|---------------------------------------- |----------- |--------------:|----------:|
| Result_Collect_WithEnumerable_AllOk     | 10         |      58.88 ns |      48 B |
| Result_Collect_WithEnumerable_AllFailed | 10         |     159.67 ns |     376 B |
| Result_Collect_WithEnumerable_Mixed     | 10         |      59.04 ns |      48 B |
| Result_Collect_WithEnumerable_AllOk     | 100        |     544.91 ns |      48 B |
| Result_Collect_WithEnumerable_AllFailed | 100        |   1,518.92 ns |    2240 B |
| Result_Collect_WithEnumerable_Mixed     | 100        |     544.00 ns |      48 B |
| Result_Collect_WithEnumerable_AllOk     | 1000       |   5,377.07 ns |      48 B |
| Result_Collect_WithEnumerable_AllFailed | 1000       |  14,282.84 ns |   16648 B |
| Result_Collect_WithEnumerable_Mixed     | 1000       |   5,436.39 ns |      48 B |
| Result_Collect_WithEnumerable_AllOk     | 10000      |  54,377.31 ns |      48 B |
| Result_Collect_WithEnumerable_AllFailed | 10000      | 155,986.62 ns |  262661 B |
| Result_Collect_WithEnumerable_Mixed     | 10000      |  53,831.02 ns |      48 B |

Before:

| Method                                   | Iterations | Mean          | Allocated |
|----------------------------------------- |----------- |--------------:|----------:|
| ResultT_Collect_WithEnumerable_AllOk     | 10         |      56.38 ns |     536 B |
| ResultT_Collect_WithEnumerable_AllFailed | 10         |     132.70 ns |     640 B |
| ResultT_Collect_WithEnumerable_Mixed     | 10         |      56.03 ns |     536 B |
| ResultT_Collect_WithEnumerable_AllOk     | 100        |     292.33 ns |    4136 B |
| ResultT_Collect_WithEnumerable_AllFailed | 100        |     926.06 ns |    4960 B |
| ResultT_Collect_WithEnumerable_Mixed     | 100        |     294.17 ns |    4136 B |
| ResultT_Collect_WithEnumerable_AllOk     | 1000       |   2,622.06 ns |   40136 B |
| ResultT_Collect_WithEnumerable_AllFailed | 1000       |   9,045.97 ns |   48160 B |
| ResultT_Collect_WithEnumerable_Mixed     | 1000       |   2,597.39 ns |   40136 B |
| ResultT_Collect_WithEnumerable_AllOk     | 10000      |  48,512.79 ns |  400273 B |
| ResultT_Collect_WithEnumerable_AllFailed | 10000      | 126,375.58 ns |  480421 B |
| ResultT_Collect_WithEnumerable_Mixed     | 10000      |  49,336.12 ns |  400273 B |

After:

| Method                                   | Iterations | Mean         | Allocated |
|----------------------------------------- |----------- |-------------:|----------:|
| ResultT_Collect_WithEnumerable_AllOk     | 10         |     153.9 ns |     384 B |
| ResultT_Collect_WithEnumerable_AllFailed | 10         |     235.7 ns |     488 B |
| ResultT_Collect_WithEnumerable_Mixed     | 10         |     152.9 ns |     384 B |
| ResultT_Collect_WithEnumerable_AllOk     | 100        |   1,201.2 ns |    2248 B |
| ResultT_Collect_WithEnumerable_AllFailed | 100        |   1,901.4 ns |    3072 B |
| ResultT_Collect_WithEnumerable_Mixed     | 100        |   1,203.9 ns |    2248 B |
| ResultT_Collect_WithEnumerable_AllOk     | 1000       |  10,923.8 ns |   16656 B |
| ResultT_Collect_WithEnumerable_AllFailed | 1000       |  17,440.6 ns |   24680 B |
| ResultT_Collect_WithEnumerable_Mixed     | 1000       |  11,085.5 ns |   16656 B |
| ResultT_Collect_WithEnumerable_AllOk     | 10000      | 130,371.0 ns |  262669 B |
| ResultT_Collect_WithEnumerable_AllFailed | 10000      | 205,803.4 ns |  342698 B |
| ResultT_Collect_WithEnumerable_Mixed     | 10000      | 130,835.4 ns |  262662 B |

Before:

| Method                                    | Iterations | Mean          | Allocated |
|------------------------------------------ |----------- |--------------:|----------:|
| Result_Collect_WithReadOnlyList_AllOk     | 10         |      96.69 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllFailed | 10         |     165.09 ns |     440 B |
| Result_Collect_WithReadOnlyList_Mixed     | 10         |      96.77 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllOk     | 100        |     835.62 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllFailed | 100        |   1,395.17 ns |    2304 B |
| Result_Collect_WithReadOnlyList_Mixed     | 100        |     835.09 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllOk     | 1000       |   8,113.94 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllFailed | 1000       |  15,196.40 ns |   16712 B |
| Result_Collect_WithReadOnlyList_Mixed     | 1000       |   8,127.41 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllOk     | 10000      |  80,841.59 ns |     144 B |
| Result_Collect_WithReadOnlyList_AllFailed | 10000      | 172,076.02 ns |  262739 B |
| Result_Collect_WithReadOnlyList_Mixed     | 10000      |  80,697.02 ns |     144 B |

After:

| Method                                    | Iterations | Mean          | Allocated |
|------------------------------------------ |----------- |--------------:|----------:|
| Result_Collect_WithReadOnlyList_AllOk     | 10         |      10.12 ns |         - |
| Result_Collect_WithReadOnlyList_AllFailed | 10         |     127.72 ns |     352 B |
| Result_Collect_WithReadOnlyList_Mixed     | 10         |      10.62 ns |         - |
| Result_Collect_WithReadOnlyList_AllOk     | 100        |      89.06 ns |         - |
| Result_Collect_WithReadOnlyList_AllFailed | 100        |   1,061.89 ns |    2216 B |
| Result_Collect_WithReadOnlyList_Mixed     | 100        |      89.11 ns |         - |
| Result_Collect_WithReadOnlyList_AllOk     | 1000       |     795.09 ns |         - |
| Result_Collect_WithReadOnlyList_AllFailed | 1000       |   9,713.98 ns |   16624 B |
| Result_Collect_WithReadOnlyList_Mixed     | 1000       |     774.94 ns |         - |
| Result_Collect_WithReadOnlyList_AllOk     | 10000      |   8,676.51 ns |         - |
| Result_Collect_WithReadOnlyList_AllFailed | 10000      | 114,186.41 ns |  262626 B |
| Result_Collect_WithReadOnlyList_Mixed     | 10000      |   8,147.36 ns |         - |

Before:

| Method                                     | Iterations | Mean          | Allocated |
|------------------------------------------- |----------- |--------------:|----------:|
| ResultT_Collect_WithReadOnlyList_AllOk     | 10         |      58.90 ns |     536 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 10         |     129.05 ns |     640 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 10         |      57.59 ns |     536 B |
| ResultT_Collect_WithReadOnlyList_AllOk     | 100        |     290.94 ns |    4136 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 100        |   1,046.97 ns |    4960 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 100        |     296.66 ns |    4136 B |
| ResultT_Collect_WithReadOnlyList_AllOk     | 1000       |   2,648.12 ns |   40136 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 1000       |  10,417.64 ns |   48160 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 1000       |   2,644.45 ns |   40136 B |
| ResultT_Collect_WithReadOnlyList_AllOk     | 10000      |  48,123.70 ns |  400271 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 10000      | 122,302.54 ns |  480425 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 10000      |  47,688.52 ns |  400271 B |

After:

| Method                                     | Iterations | Mean         | Allocated |
|------------------------------------------- |----------- |-------------:|----------:|
| ResultT_Collect_WithReadOnlyList_AllOk     | 10         |     135.2 ns |     352 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 10         |     205.0 ns |     456 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 10         |     135.1 ns |     352 B |
| ResultT_Collect_WithReadOnlyList_AllOk     | 100        |   1,028.5 ns |    2216 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 100        |   1,677.1 ns |    3040 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 100        |   1,058.3 ns |    2216 B |
| ResultT_Collect_WithReadOnlyList_AllOk     | 1000       |   9,626.0 ns |   16624 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 1000       |  16,151.7 ns |   24648 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 1000       |   9,564.9 ns |   16624 B |
| ResultT_Collect_WithReadOnlyList_AllOk     | 10000      | 111,237.7 ns |  262624 B |
| ResultT_Collect_WithReadOnlyList_AllFailed | 10000      | 183,753.4 ns |  342683 B |
| ResultT_Collect_WithReadOnlyList_Mixed     | 10000      | 111,720.5 ns |  262626 B |

@jscarle
Copy link
Owner

jscarle commented Jul 6, 2024

You're right, I missed that. Thanks.

@jscarle
Copy link
Owner

jscarle commented Jul 6, 2024

In the end, there's a clear reduction in memory allocations, most significatingly in happy paths. It's a trade off by an increase in execution time. But I'd rather sacrifice execution over memory allocations.

Method Iterations Mean Allocated
Result_Collect_WithEnumerable_AllOk Original 24,241.73 ns 240124 B
Result_Collect_WithEnumerable_AllOk Final 51,208.03 ns 48 B
Result_Collect_WithEnumerable_AllFailed Original 95,533.31 ns 240214 B
Result_Collect_WithEnumerable_AllFailed Final 164,784.59 ns 262639 B
Result_Collect_WithEnumerable_Mixed Original 100,139.42 ns 240214 B
Result_Collect_WithEnumerable_Mixed Final 105,551.57 ns 131432 B
Method Iterations Mean Allocated
Result_Collect_WithReadOnlyList_AllOk Original 24,238.73 ns 240125 B
Result_Collect_WithReadOnlyList_AllOk Final 8,902.23 ns -
Result_Collect_WithReadOnlyList_AllFailed Original 101,700.59 ns 240226 B
Result_Collect_WithReadOnlyList_AllFailed Final 108,654.57 ns 262578 B
Result_Collect_WithReadOnlyList_Mixed Original 98,683.66 ns 240213 B
Result_Collect_WithReadOnlyList_Mixed Final 93,562.10 ns 131384 B
Method Iterations Mean Allocated
ResultT_Collect_WithEnumerable_AllOk Original 52,635.02 ns 400240 B
ResultT_Collect_WithEnumerable_AllOk Final 129,332.74 ns 262629 B
ResultT_Collect_WithEnumerable_AllFailed Original 132,315.58 ns 480362 B
ResultT_Collect_WithEnumerable_AllFailed Final 194,767.92 ns 342683 B
ResultT_Collect_WithEnumerable_Mixed Original 137,844.20 ns 440139 B
ResultT_Collect_WithEnumerable_Mixed Final 215,781.51 ns 171232 B
Method Iterations Mean Allocated
ResultT_Collect_WithReadOnlyList_AllOk Original 52,660.42 ns 400237 B
ResultT_Collect_WithReadOnlyList_AllOk Final 115,393.16 ns 262580 B
ResultT_Collect_WithReadOnlyList_AllFailed Original 130,764.44 ns 480372 B
ResultT_Collect_WithReadOnlyList_AllFailed Final 186,435.66 ns 342631 B
ResultT_Collect_WithReadOnlyList_Mixed Original 135,296.39 ns 440138 B
ResultT_Collect_WithReadOnlyList_Mixed Final 185,865.90 ns 171176 B

@jscarle jscarle merged commit f7289d2 into jscarle:develop Jul 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extension method to collect IEnumerable<Result<T>> into Result<IEnumerable<T>>
2 participants