How slow is MediatR really?
HTML-код
- Опубликовано: 8 фев 2025
- Check out my courses: dometrain.com
Become a Patreon and get source code access: / nickchapsas
Hello everybody I'm Nick and in this video I will investigate a claim around the library MediatR and how it can affect your application's performance. This video will also introduce you to performance measuring and memory profiling techniques.
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: bit.ly/ChapsasG...
Follow me on Twitter: bit.ly/ChapsasT...
Connect on LinkedIn: bit.ly/ChapsasL...
Keep coding merch: keepcoding.shop
#csharp #dotnet #mediatr
Something that I forgot to mention in the video is that, when you add some IO into the mix (database call, api call etc) the difference will get even smaller because the IO will loadbalance it out of the equation.
Yeah is was wanting to make this comment as well. As realistic as you tried to make it, it is still not really reflecting real-world scenarios. Would love to see an addition to this where some real work is being done, eg calling a DB or external service
And this is not even the beginning of looking for performance - first you need to figure out exactly what part of pipeline slows you down. You can make an educated guess that more often than not it will be the IO. But you may have a really non-performing middleware/container and so on. Unless you find the real culprit it's pointless to sacrifice the architectural cleanliness vs. performance.
Thanks for the video.
This is something you should say in all your performance related videos. But great video always something new to learn :)
However, the gap also may get bigger, if you don't have the SSL overhead.
We use MediatR in a production environment with good results, but I never got this far down into the weeds about it. This was a very interesting video, thanks!
Nick your videos are pure gold. Your presentation and pacing are perfect. Also your approach is so easy to follow! Thanks again for the great content.
I feel like most developers are always seeking the best practices overall and forget to ask what is the best for their situation, which can lead to hard to handle cannon that is used to kill an ant.
It all comes to the problem you need to solve. In my company, we have some smaller applications that use MediatR. We never faced performance issues and my team just like to work with this library, because how the code feels cleaner and outweighs the performance "cost". It works for us in that situation and we are happy with maintening it.
I agree, cleaner better code is almost always beneficial, and my experience is that cleaner code can lead you to find better optimizations that saves even more or code that scales better.
Getting 5 % more speed with more complex code, or double the performance by adding another instance due to easier to scale code :)
100% agree with you !
And happy developers that can easily change code saves money as well. Good for you!
100% agree. 80% of the time, we use only 20% of application. Martin fowler refactoring books also suggested the same.. we should focus on writing clean code and fix performance issue when needed.
it is my experience that there are other ways to keep your code "clean" and remain testable without the complexity, and clutter, that this pattern inherently introduces.
Super interesting how it affects in terms of memory allocation, thanks again Nick!
Hey Nick, awesome to see a video on this content! As always really well done :). I agree that the benefits of MediatR generally speaking outweigh the performance cost. That said, the memory allocation issue related to the service can almost be completely removed by making the service a singleton, if your use case permits. It would be interesting if MediatR also allowed you to register handlers as singletons.
Singleton is almost never a viable option bacause it heavily depends on whether you want to support scoped services in the handlers, which is almost always the case, since the API request itself is scoped.
Registring as Singleton will create a memory leak in most cases. Variables injected with a shorter lifetime will never be collected because the root will still exist. While it is possible, I think you will get into all sorts of trouble 🙂
@@tiebird That is interesting! I never thought that a singleton would create a memory leak. How would this happen?
@@nickchapsas That is completely fair. Good point 😊
@@RENAUDADAM like earlier mentioned, you register a service/class as a Singleton. Every service/class that is injected in to the constructor of this Singleton will never be disposed in the lifetime of the application. As long as the root object exists, this means the Singleton object in this case, underlying objects will not be disposed by the automatic garbage collection. Therefore, you will see your .NET Core ServiceCollction grow larger and larger. It can be pretty hard to find these kind of memory leaks if you don't know what you're looking for. Memory consumption can rise fast if you call some kind of repository frequently.
I think many people have a hard time understanding that not everything is about raw performance, if that were the case we would only write with low-level languages like ASM, C or Rust. We use high-level languages because we want to increase the readability of our code as much as possible and reduce human error at the maximum possible rate, the objective with this is to make the product something agile to develop and with a low error rate. If you worry too much about performance trying to cut costs, you will end up impacting your bottom line and making those lower costs less valuable in and of themselves.
very good evidences bro, thanks for best clarifications
Great thorough tests, explanations, and video! Thank you!
Thank you for putting so much effort into it. I learned a lot from this video.
I'm gonna stick with Mediatr :-)
Thanks for a great video. I think when you add some IO bound code to the sample this overhead becomes minimum and I also think in 99% of cases doing such low level optimization as using services instead of the Mediatr makes the code more complex. It is then much more difficult to maintain, bugfix and refactor for high level optimization
Very informative video. Learned about the profiling tool too. :)
Thank you for this extraordinary example!
Do one SQL query or lazy load too many, then optimize that away, and you've gained much more than replacing MediatR.
It was quite obvious that with all the additional plumbing that Mediatr and CQRS in general require there was going to be some performance impact. I think 8% is not terribly significant. However, we have to remember why CQRS is really in place. The theory is that it improves readability, testing paradigms, and keeps a cleaner more manageable architecture. You can make a case for that I think. In my situation, our company builds enterprise applications for large companies and government with 10's of thousands of potential concurrent users possible. In that scenario, we have been hard-pressed to think that all of the extra plumbing (dozens and dozens of extra nearly empty classes and interface points), that this requires gives a tangible benefit. It does keep code out of the WebAPI controllers which is great. But there are a number of other scenarios including just a straight N layer interface that do the same thing and do not degrade performance. This pattern and some of the others like it then are relegated to really specific situations that most projects do not need. I very much appreciate the quantifiable info that Nick has provided, it further confirms for me in my mind, that Mediatr outside of some very specific situations, is purely an academic exercise.
Really nice and deep insights. Sadly most people just believe what others say without trying it out first.
I think from a performance perspective this doesn't matter that much. After all realistically processing a request will do MUCH more memory allocation (DB query, Read/Write XML, create in memory data structures). But I do think that there is a tendency to add more and more indirections into C# projects. And every level of indirection adds more complexity. While the original author of a code might be familiar with all these advance concepts and remember about all the introduced indirections and abstractions someone who needs to maintain the code later will have a very hard time to figure out how things in a code base depend on each other, especially if introducing such an indirection happens in a code file that isn't obviously linked to the code file that depends on it. You must know that e.g. the Mediator package has been added. You must know that this one line of code in the Program.cs is responsible for the coupling. You must know that the service runs transient. All this adds complexity without delivering any direct business value. It is all pluming. This is the wrong direction IMHO.
Do you think these issues could be solved by writing documentation?
@@roko567 so we use MediatR which causes increase of indirections into our project. these indirections create us a problem to solve by documentation... which is also a problem because documentation is something that we have to keep updated and if you have outdated documentation it can be even worse than total absence of documentation
Also MediatR makes a great job of hiding code smells. e.g. if you have a controller with 10+ services you can see that something somewhere went wrong and you should do refactoring. While with MediatR you have only instance of IMediatr and miss that your code is not as great as it seems
I see these pros and cons about using MediatR
+ less dependencies in costructors
+ nice and easy in-process pub-sub implementation
+ way to decorate your calls with logging and etc
- indirect usage of methods/services
- slower navigation across code base
- hiding code smells
I agree that there are cases when usage of MediatR is great. But I doubt that it should be used in every single API project.
Honestly, I like this video much more than many others. Mostly because of conclusion “always measure and then make decisions”.
It would be nice to see even more realistic example - some json (de)serialization, maybe call to DB, some calculations and data transformations, logging, DI. Where are bottlenecks etc?
You are awesome Nick 👍🏼
But the problem with MediatR is when you have a large amount of request handlers, notification handlers and behaviors, of course calling a single request handler is closely comparable to direct method call.
The same applies to normal DI though.
@@peteruelimaa4973 I think that the performance of MediatR relay on performance of DI container, the much request and handdler you are using the much slow will be.
Perfect demo 👌
Very very cool video ! Super interesting.
Nick great presentation !!! But it is also great the comments that this video triggered, if someone read them can gain some more knowledge
I'm always weirded out by people micro-benchmarking in C#. "Oh... you'll get 3.8% better performance if you don't use mediatr"... Sure, and i'll get an order of magnitude improvement if I use Go or Rust. I'm using C# in the first place because, to a certain degree, I'm prioritizing development time performance over runtime performance. And yeah i'd echo the point you made in the pin that i'd like to see this test re-run with EF... i'm definitely expecting that the differences would shrink to basically nothing as soon as you hit a database (particularly if you're performing the correct async rituals).
For me... bottom line is that, at worst, Mediatr is taking away 4% of performance, probably less in a real example, and the very fact that i'm using C# means that 4% in return for objectively cleaner architecture is a good deal.
I didn't think the slowness complaint was the basic Mediatr setup. I think it was more when you start adding things to its pipeline/middleware (decorators) that it started to become a problem.
MassTransit also has an implementation of the Mediator Pattern, I have a feeling that there will be a huge difference in terms of performance, but I'm too lazy to benchmark it to see the difference. Is there a chance you can make a video for that?
By the way, I'm currently experimenting with MassTransit's Mediator
@@kblyr I asked the Author of Masstransit about that and he says he hasnt used it himself LOL
Great content!
your content is good. thanks
It will be interesting to run this test with the MediatR request being of type record or struct or maybe record struct to see how that impacts allocation.
It can't be a struct because it needs to inherit from the IRequest interface
Hi Nick! Thanks for the nice videos & material you make & provide to the public :)
I have never used MediatR library before, however I believe there is another, that might be negative, impact in using this pattern in such a generic way as the library provide. How can you control the lifetime of the handler's instance? Does every time you send a message through the mediator instantiate a new instance of the handler? What happens if you send the message from a loop?
I'm a fan of MediatR and agree that it's all relative to what you gain plus, as others have commented, one bad SQL query makes all of this irrelevant.
However, just in terms of MediatR overhead, a more likely scenario is probably a controller calling a method in a service class and that method calling multiple other methods in that same class - compared to the controller calling a MediatR class and that class calling multiple other MediatR classes.
I haven't done the test, but I suspect the difference will be far greater in a case like that.
Otherwise you have to duplicate common code in each MediatR class and that's not good.
Hi Nick,
Thank you for your free content really appreciate it.
I am wondering how will the Fastenpoints perform against mediator, I guess that the Fastenpoints also allocate ram in order to map the objects.
Thank you.
They are way more efficient
Having looked at the mediatr codebase, I believe the performance issue can be alleviated by Introducing object pooling for the request handler and the notification handler. This will reduce the overall memory used and thus reduce GC time.
MediatR is as efficient as it can go with the handlers while respecting .NET's DI scopes. Both scoped and transient use the service provider to retrieve the service, which is exactly the same as injecting them and singletons are cached. All of them come from a dictionary. There isn't much to optimize past that, since a lot depends on how elaborate your pipeline is (pre/post processors etc)
Hi Nick, I'm a bit confused here and I was wondering if you could help me. Both methods use the MediatR framework. Calling it directly just means that you're NOT using MediatR injecting dependency, you're just creating an instance of the object and then calling the Method directly?
At the beginning you think about a performance.
In the middle you think about an architecture.
In the end you try to think about the performance keeping in mind the architecture.
After the end - you want to use the ECS.
But if you are a senior - you have to know and have love to use the MediatR, the benefits in Enterprise is huges.
Better than unit of work + service + repository?
Hello Nick! Cool demo! What program you use to create your videos? I mean this great feature when you can stop demo at any time and draw lines to highlight useful moments on screen.
It's called ZoomIt
@@nickchapsas Thank you!
Is the Activator call can cause this little overhead in mediatr?
And try and throw some middleware (native) / mediatr pipeline behaviours that do logging and some other useful stuff and it gets really crazy :)
What do you recommend instead of using MediatR write own solution ?
Just don't use MediatR
is MassTransit MediatR ok? because using the cqrs pattern without MediatR is not that simple for me and everyone is using MediatR
I think for most corporative applications, that performance difference would not be a big deal.
Nice
If we add a least some noticeable business logic to the test apps then the overhead of creating little mediator messages will disappear and will be minor comparing to other allocations related to DB, MessageBus, what ever could be in the logic. So for me such testing has very little value or I would say it actually hurts because ppl with insufficient qualification will make decisions based on this or at least will be biased doing own selection of the approach.
I think the first benchmark was not fair. You are using a single instance of the service/handler, effectively a singleton for the non-mediator test. But for the mediator test, you are using DI to allocate the handler on each iteration. Was it medator or DI allocating the order of magnitude more memory? Each benchmark should be required to resolve the handler 'service'.
The comparison is being fair to how you would use MediatR in your app. You wouldn't resolve the handler. You would resolve IMediatr. On that front they are both singletons. They handler is out of bounds since mediatr is doing the instantiation internally. It can be overriden, but that was never what we're testing.
@@nickchapsas Guess it is fair, I rewrote it as I suggested but the relative performance and memory allocations were inline with what you showed. I should have validated my assumption before my original comment. Thanks for all the great videos.
Interesting, as usual, but I've never used MediatR as "dry" as that. For instance, the pipeline model is so much worthy, and appart from the obvious "cleaner code" standpoint, I'd bet the impact wouldn't be as important if you'd have to put your behavior logic in all your services...
Check the pinned comment
Can you make a vid about Dataflow (Task Parallel Library) ?
How's your command line called ??? It looks so utterly insane
I’m using Microsoft Terminal and oh-my-posh
Can you try and do this with Blazor and Fluxor compared to a home made Blazor with Invoke? Right now Fluxor is making it so much easier to work with UI state.
record please video about your pc setup
I was blinded around 7:30 min in, could not watch the rest of the video, RIP
Hahahahahaha :)
applications should almost always prioritize speed before other. So the question should be is this safe enough. If true then go speed
Greate content!
How do you comment that fast
Notification squad
Even slower due to slow code navigation 😴😴😴
pre 301 squad :D
Hi Nick
Have you considered opening a discord community?
Would also be an opportunity for your potential patreon subscribers
Hey Jacob. I have but I don't want the Discord server to end up being total chaos since I don't have time to moderate it so for now, I'm just part of the official C# Discord server
Slower than me posting a comment
Cmon
OMG. I can't believe Nick is using 69 and 420 as test values 🤣🤣🤣.
👍👍👍👍👍!! You really need Promo>SM!!!
Poorer performance is an objective number but besides that MediatR is an extra dependency to maintain and align with the team, its an added layer of complexity which you dont NEED. I dont feel like it makes code "clean", Its just a popular style at the moment off the back of the popularity of events. Basically teams which dont have event queues can play play events with MediatR lol jk
In reality that’s not the case.
Look at it this way.
The complexity of your unit testing is dramatically reduced. You only need to test your handlers instead of mocking and testing controllers and contexts etc. In that example case, why would you go out of your way to indirectly test parts of .net outside the domain of your project?
We don’t want to mock controllers, we don’t
Want to create a dummy context. Why should we? We only want to test our code. Hence, the handlers
And this example works all over.
Not only this but it allows associates all the way through senior to work on core parts of the system in isolation because you’re only working on a single request contract / response object implementation.
If anything I think it makes it easier to maintain your code base while also keeping it simpler
Now the question is, is mediatr the best. I dunno. If it’s the only thing you have in a service maybe but if you’re also using something like masstransit then you can use Chris Patterson’s in memory mediatr which does the same thing
Mediatr is my go to unless I’m using MAssTransit then I use a combination of the MAssTransit bus with in memory bus similar to SendLocal in Nservicebus.
MediatR behaviors are the secret sause, which used effectively, does contribute to clean extensible architecture. I would not agree that this is not NEEDED.
@@prom3theu581 yeah i agree with you regarding the tests but you dont need mediatr for any of that. Ie thats not a legit reason to use mediatr, hence why i say its just the popular thing nowadays days.
@@krajekdev yes id agree behaviors is a good reason for mediatr. But in my experience most companies that use mediatr dont use behaviors and have no need for it(that could be argued but the fact that they never used them shows it was not needed). But yes if you have a strong need for behaviors, i would endorse mediatr for that case. But thats why i say it just a popularity contest, everyone uses something because its shiney and not because they actually need it or even know what it does. Some things have become "always use it because it fits every project".
@@brandonpearman9218 agreed 100%, cheers
Lol! MediatR … like to “mediate”; sounds like politics… We usually save that for our meetings not our code. ;)
Irrelevant