DryIoc: Performance degradation with ImportMany in v4 compared to v2
Hi Maksim,
I’ve recently upgraded an application running DryIoc v2.12.6 to DryIoc v4.8.1
and noticed substantial performance degradation (it runs about ~70x slower).
The application is a simple intranet web server that hosts web APIs implemented as C# scripts. Server loads scripts from the database and recompiles them on-the-fly when they are changed. It uses DryIoc to wire them up to the built-in services and to the other scripts. This feature relies on DryIoc’s dynamic registrations.
I trimmed down the project to reproduce the isolated issue. My benchmark basically imports and calls the same web service 1000 times in a row. I also tested a v3 version of DryIoc to check if it performs any better.
Here is the log:
v2.12.6.0
D:\Externals\DryIocPerf\Bin\v2\net46>DryIoc2Perf.exe
[02:53:07 INF] Starting up...
[02:53:07 INF] Started.
[02:53:07 INF] Hello there. Starting the benchmark.
[02:53:07 INF] Imported web services: 104
[02:53:07 INF] GetNow: 2021-07-13T02:53:07.0000000+03:00
[02:53:07 INF] Warming up...
[02:53:08 INF] Running the benchmark...
[02:53:08 INF] Time elapsed: 00:00:00.0756021
v3.0.2.0
D:\Externals\DryIocPerf\Bin\v3\net46>DryIoc3Perf.exe
[02:53:13 INF] Starting up...
[02:53:14 INF] Started.
[02:53:14 INF] Hello there. Starting the benchmark.
[02:53:14 INF] Imported web services: 104
[02:53:14 INF] GetNow: 2021-07-13T02:53:14.0000000+03:00
[02:53:14 INF] Warming up...
[02:53:14 INF] Running the benchmark...
[02:54:20 INF] Time elapsed: 00:01:05.5922111
v4.8.1.0
D:\Externals\DryIocPerf\Bin\v4\net46>DryIoc4Perf.exe
[03:04:06 INF] Starting up...
[03:04:06 INF] Started.
[03:04:06 INF] Hello there. Starting the benchmark.
[03:04:06 INF] Imported web services: 104
[03:04:06 INF] GetNow: 2021-07-13T03:04:06.0000000+03:00
[03:04:06 INF] Warming up...
[03:04:06 INF] Running the benchmark...
[03:04:12 INF] Time elapsed: 00:00:05.4144036
- v4.8.1 performs ~72 times slower than v2.12.6
- v3.0.2 performs ~876 times slower than v2.12.6
Perhaps I’m doing something wrong in my dynamic registration resolver…
Upd. No, it seems to reproduce even if dynamic callback does nothing.
I’m going to clean up the code and upload it to a separate github repo. The benchmark is actually quite small, but the dependencies are heavy.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 35 (35 by maintainers)
Commits related to this issue
- fixing: #417 in V5; changed: DefaultDynamicRegistrationFlags to Service only added: DynamicRegistrationFlags.DecoratorOfAnyTypeViaObjectServiceType; added: overrides with `flags` parameter for the bas... — committed to dadhi/DryIoc by dadhi 3 years ago
- axing the #417 - applying the changes from the v5, when marging back we just need to keep the v5 Container.cs version — committed to dadhi/DryIoc by dadhi 3 years ago
- added test for #417 — committed to dadhi/DryIoc by dadhi 3 years ago
- small things for #417 @wip — committed to dadhi/DryIoc by dadhi 2 years ago
- optimizing the Lazy with Metadata with the direct wrapper implementaion which may use the already resolved service factory #417 — committed to dadhi/DryIoc by dadhi 2 years ago
- notes for #417 — committed to dadhi/DryIoc by dadhi 2 years ago
- cleanup and todos #417 — committed to dadhi/DryIoc by dadhi 2 years ago
- simplify for #417 — committed to dadhi/DryIoc by dadhi 2 years ago
@yallie I have burned it down to the propagating of the already resolved
DynamicRegistrationfactory through the wrappers chain. But it requires the explicit support in the wrapper factory implementation. Currently, the factory is propagated up toFunc/Actionwith or without arguments and toLazy<T>andLazy<T, TMeta>. The factory is propagated from and through theArrayand collection wrappers,KeyValuePairand theMeta/Tuplewrappers.ExportFactory<T>andExportFactory<T, TMeta>is not supported yet. I postponing it for the next version or the PRs.The factory won’t propagate up-to but not through the
LazyandFuncinto the resolution calls - essentially it means theDynamicRegistrationwill be called again. It is because of the dynamic nature of those wrappers - they may be used for the late registration done after the wrappers are resolved but not yet consumed.@yallie Hello,
Currently the expressions cached only for services and not for wrappers, I’ve experimented with it and number of tests start to fail, so it is for the reason. Though I am planning to look at it closer later. For now I am simplifying the story of combining dynamic and normal registrations, removing the one problem from the picture before diving back to the caching.
@yallie
No, it was actually 1.1m.
I’ve improved it further to 330k, not good whatsover 😕
So, as the next step I will add the cache for the dynamic factories into the resolution request.
@yallie Thanks, it was me not updating the GitHub page, forgot that GH is half-SPA app.
I suspect this too and will try to cut to absolute minimum. But generally the dynamic provider is treated as the black box possibly reconfigurable at runtime (“dynamic”). So the client should memoize on their end.
@yallie It is a great insite into the problem. I have started profiling but kind of suck in this topic…
Will check what’s happening.
Hi @dadhi, thank you 😃
Looks like v4 calls dynamic registration lookup callback much more frequently than v2:
I’m surprised that while v4 has almost the same number of calls as v3, it’s still 10x faster than v3.
My guess was that v4 tries more types to look up. But it turned out not to be true.
The types being looked up are the same in both versions:
I’m surprised that it looks up
System.Object,System.Stringand open generics likeSystem.Lazy[T]. It also tries all kind of wrappers. I was assuming it should look up unwrapped/undecorated types.