-
-
Notifications
You must be signed in to change notification settings - Fork 3
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
Add extension methods for combining multiple results into one #18
Conversation
@PressXtoChris Can you allow me to make edits to the pull request branch? |
Nevermind, it's my git that's being wonky. |
tests/LightResults.Extensions.Operations.Tests/LightResults.Extensions.Operations.Tests.csproj
Outdated
Show resolved
Hide resolved
This is for 10, 100, 1,000, and 10,000 results respectively. When I started, benchmarks looked like this:
Now they look like this:
|
Don't know if you want to take another crack at it now? |
I've added the comparisons benchmarks project. That's an impressive jump you got
|
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:
After:
Before:
After:
Before:
After:
Before:
After:
|
You're right, I missed that. Thanks. |
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.
|
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 return
ing into anIEnumerable
). 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)
andResult<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 aReadOnlySpan
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 wasResult<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.
Linq
ones were done using similar logic to what I've got in the OP of Extension method to collectIEnumerable<Result<T>>
intoResult<IEnumerable<T>>
LightResults#32For
vsForSized
comparisons are using a for loop, and comparing pre-sizing the error/value lists.Span
ones are converting array to aReadOnlySpan
like I have in this PR. TheUnsafe
versions useMemoryMarshal.GetReference()
andUnsafe.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:
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 combineVec<Result<T, E>>
intoResult<Vec<T>, E>
. I thinkMerge
orCombine
could also work (and might be a little more intuitive).