-
Notifications
You must be signed in to change notification settings - Fork 76
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
Parallel notifications, correct ordering for switch statement cases #145
Conversation
Note to future self: Putting more of the code into |
src/Mediator.SourceGenerator/Implementation/Analysis/CompilationAnalyzer.cs
Outdated
Show resolved
Hide resolved
src/Mediator.SourceGenerator/Implementation/Models/NotificationPublisherTypeModel.cs
Outdated
Show resolved
Hide resolved
Impressive changes, haven't done a major deep dive yet. It might be possible to create a more memory efficient awaiter for known lengths. Instead Mediator could have methods for common array lengths where instead of having a nullable array we have task variables, one for each possibly asynchronous task. This way the array is inlined inside of the generated async state machine saving the 24 bytes for the array. I think this will always use less memory than the array equivialent, up to 4 handlers. Thereafter unlikely setups may use slightly more memory. async ValueTask Publish2(INotificationHandler[] handlers, TNotification notification, CancellationToken cancellationToken)
{
Task task0 = handler[0].Handle(notification, cancellationToken).AsTask();
Task task1 = handler[1].Handle(notification, cancellationToken).AsTask();
List<Exception>? exceptions = null;
try
{
await task0;
}
catch (Exception ex)
{
exceptions ??= new List<Exception>(1);
exceptions.Add(ex);
}
// await each one etc
} |
Took me a while to be able to benchmark this properly... But yes it makes a difference, atleast for the synchronous case Top: before Now I just have to cleanup a bit before I can push... made some significant config changes to make it easier to benchmark and test different things based on compile time config |
Thanks for sharing the benchmarks! Looks like it saved a small amount of memory for the async case, although I can't tell why it saves 32 (35) bytes and not the expected 24. I'll look at the lowered code in the morning, benchmarkdotnet might be measuring memory usage incorrectly. The synchronous case getting a speed boost is unexpected. Perhaps it's due to loop unrolling. |
Yeah when it's manually unrolled like that I'm guessing it can elide bounds checks as well, I kind of doubt it is able to do so for enumerators even if they are plain structs (not boxed, no virtual calls) EDIT: nvm that was not the custom enumerator, codegen looks identical. I guess it just saves the control flow of the loop? branches and jumps, even if they are predictable Updated the PR desc with a full run through benchmarks as well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looked through most of it, LGTM. No idea what you did with ForeachAwaitPublisher
😅
I did think sorting with InheritanceComparer
could cause big O issues (had a similar issue with Mapperly). But I don't think these issue will happen in practice.
True it's pretty naive, I guess I'm betting there aren't many inheritance hierarchies out there, atleast there isn't in my code :P We can improve it in subsequent PRs, just want to get a preview out now so that I can test it some more |
Solves #115 and #138
INotificationPublisher
abstraction similar to that of MediatR, apart from making the interface slightly worse while allowing better performance (avoiding closures, same as for the pipeline behavior interface)Mediator.Abstractions
references .NET Standard 2.0 + NET 6.0 with conditional package references toMicrosoft.Bcl.AsyncInterfaces
forIAsyncEnumerable<T>
System.Runtime.CompilerServices.Unsafe
to avoid cost of castingPolySharp
for some attributesStill unsure if netstandard2.0 is the way to go.. that might chage
TODO
ValueTask.WhenAll
variant based onIValueTaskSource
Benchmarks