-
-
Notifications
You must be signed in to change notification settings - Fork 122
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
DryIoc cold start performance #27
Comments
Hi @Fruchuxs , v3 does not reuse expressions across the object graph, so if you have In upcoming v3.1 the expression reuse (cache) is back. But it has a different transient nature, which is simple to manage. It should improve cold-start performance. For the other use-cases, I am not sure. Could you provide some notable parts of the object graphs, where you see an increase comparing to v2? |
You can use |
Hello again, thanks for your quick responses. I think I will await v3.1. With your explanations it makes sense, why the graphs seems more complex, because of the nested graphs. So no problems here I think. Can you give a time span you think you are ready with v3.1? Just to examinate if we need to downgrade. |
Next week, hopefully. |
Another idea, I can release a preview of 3.1 now. Would you be able to check performance using it? |
Sounds nice. I can test it, maybe till monday. |
The late update.. While working in 3.1 preview release, I have found (remembered) other problem with .net core targeting. That .net core won't use FEC, oops :( That was done close to v3 because of complexity (new projects needed) to support .netstandard 1.3 and 2.0 versions. But the main obstacle is that I have used old csproj system. I went ahead, and started to bring everything under new csprojs with multi-targeting. At the moment I am almost complete to release the preview. Hopefully, tomorrow. |
Preview is out: https://www.nuget.org/packages/DryIoc.dll/3.1.0-preview-01 |
Thank you for the prerelease.
I checked all our projects and can't find a reference to 3.0.2.0, but VS generates 3.0.2.0 references in *.assembly.cache and *.deps.json files. Looks strange to me. |
This is strange, the problem either with assembly versions in preview, or with Di.Ms.Di deps. Will check. |
Try the new DI.MS.DI preview: |
Got the same exception with different assembly version number (3.1.0.0). We looked into the .net standard 2.0 assembly with ILSpy and it seems, that the assembly version is not set ("0.0.0.0"). Dunno how this happened, your cproj Files seems to be correct. |
But now I know a possible reason. Thank you. I will release new DryIoc preview 2 with fix. |
Not so fast as 2.12.8, but a greatly increase to 3.0 in our cases (~0,5 seconds difference). Over all it looks great. Thank you. |
Interesting, |
Here it is. |
Thank you very much. It is a deep tree indeed. Could you try to add the rule to the container: new Container(rules => rules.WithoutDependencyDepthToSplitObjectGraph()); or via: container.With(rules => rules.WithoutDependencyDepthToSplitObjectGraph()); or with container.With(rules => rules.WithDependencyDepthToSplitObjectGraph(20)); and check the results. The reason is the default The number is used to split an object sub-graph deeper than the 8th level to the separate BTW, The mechanizm in v2 for splitting the large object graph was different, based on whole amount of dependencies rather than on the depth. |
Thank you for the information and explanation. But with |
Okay, I tested with another graph (but with a similar depth). WithDependencyDepthToSplitObjectGraph(20) WithDependencyDepthToSplitObjectGraph(2) // just for the science WithDependencyDepthToSplitObjectGraph(8) With Default Constructor WithoutDependencyDepthToSplitObjectGraph |
But the measurements fluctating .. As I said, I don't think it matters in our cases. |
Thank you for measuring! Seems like using the depth split does not add any value in your case. Need to go deeper.. |
Could you use a ExpressionToCodeLib nuget package and dump the expression without depth argument? |
Here the Tree To Code output from the tree I used for measuring. |
That's what I need! Thank you. |
Shame on me, the FastExpressionCompiler was not included yet because of different compile constant :-( |
Btw. (I don't tested pv03 yet) we investigated with pv02, that we got an lifespan exception in our web project:
The problem is, that AuthStateManager is added via dryIoc as reuse: Reuse.Scoped and AuthenticationEvents via .net DI with AddScoped. We replaced the .net DI registration with a dryIoc equivalent and we don't got the exception anymore. We checked the Code in Di.Ms.Di and discovered that AddScoped is registred in dryIoc as Reuse.ScopedOrSingleton. So we changed our replaced service registration with Reuse.Scoped to Reuse.ScopedOrSingletone and the exception occured again. We tested out if our services was added as singleton by dryioc, because maybe no scope was open and the new version is just more restrictive, but we had to recognize that the object is created at every request and not just first. |
Mm, ok. Another thing to check. Have an idea, though... |
Same, I even tried to diff both files: exactly the same. ~17346kB |
Ok, could you try a different value for |
Tried with |
Maybe... try to do just |
Tried with |
Ohh sorry, a var expr = container.Resolve<LambdaExpression>(typeof(MyControllerWithLotOfDependencies)); You need to open a scope if using (var scope = container.OpenScope(Reuse.WebRequestScopeName))
var expr = scope.Resolve<LambdaExpression>(typeof(MyControllerWithLotOfDependencies)); |
@dadhi Does that mean that I should use |
Thanks for results. The value is greatly depends on context. Like how many and what are types of reuse, do you have a lazy/func deps, how deep or wide a graph. Try to peek some sensible value between 5 and 8 for instance. Previous DryIoc default was 8 (v3.0). I did other changes to decrease a result expression size (see the whole thread). I am planning a new changes/options as well (#40). Maybe I will return back the default 8. So your and @Fruchuxs inputs are very valuable here. |
I wanted to add on resolving as var expr = scope.Resolve<LambdaExpression>(typeof(MyControllerWithLotOfDependencies));
var factory = (FactoryDelegate)expr.Compile();
var controller = (MyControllerWithLotOfDependencies)factory(container); Replace container with scope for scoped controller. Btw, this also excludes FEC from the equatation, see #39. |
Just in case, I would also love to see a speed/memory comparison on your real graph between |
Hi @dadhi, As you requested, here is benchmark of Benchmark parameters:
With
With
With
With
Seems that |
Thanks for the tests, very interesting. I would try the latest FEC v2 (after it is released) and will decide what to do. Btw, seems that actual problems in your case was caused by FEC. Hopefully, will fix this soon(ish). |
May be related to dadhi/FastExpressionCompiler#89 (comment). Once I proposed to get real graphs into benchmark, created issue for voting, but it was closed without consideration danielpalme/IocPerformance#86. |
Sot it next will not work for some reasons?
|
|
I have released v3.1-preview-06 which sets a default depth to 5 for now, and moreover, simplifies the expressions with root scoped service (controller in your case). The result expression should have one less nested lambda (minus one compile step). |
Hi @dadhi, here is benchmark results for
My observations (container created with
|
As I mentioed before, but our graph is not as big as @ahydrax Graph.
I don't want to critisize your code nor your approaches, but with 14 dependencies there are maybe other problems here. If your actions uses all of these dependencies (high cohesion) it's okay, but if not, you shall split your controller into different ones. The Dependency injection pattern respectively the inversion of control principle assumes, that your classes fulfill the single responsibility principle. This doesn't mean, that DryIoc has problems here, but an IoC system has some constraints. |
I agree with you: having 14 dependencies might be an indicator that something wrong with the code itself. |
Performance of containers depends on the use-case. Here, we likely hit the bug + specific case situation. But I glad for feetback, cause it pushed the boundaries for me. |
@ahydrax Hi, I have released a new preview v4.0.0-preview-01 with all the improvements from #45 |
Hi @dadhi , sure, approximately on Monday I'll post the results. |
@ahydrax Do you have any updates to share with us? |
Here is the latest results: #26 (comment) I will consider this issue closed, please open a new one if you see the problem. |
After one week of investigations we found out, that DryIoc 3.0.2 is rather slow in comparison to 2.12.8 at the first execution time (cold start). In most cases the first call of a C# application doesn't count. But we measured a time increase of ca. six seconds for greater object graphs in our application. So for example our asp.net core 2.1 application starts, DryIoc resolves some small Services, than the applications tries to resolve a controller and .. we can make a cup of coffee. For a web applications this is just uncomfortable (because after the JIT compiling it's fast), but for one of our console applications this is really painful.
We looked at the expression trees which dryioc generates in version 3.0.2 and 2.12.8 for one of our object graphs. Too much to post here and to analyze in depth, but we can say, that the dryioc 3.0.2 expression tree is roundabout 90% bigger and seems even more complex.
We didn't changed the service registrations much. We just replaced Reuse.InWebrequest through Reuse.Scoped and two RegisterDelegates through RegisterMany as you mentioned out here.
Maybe you can say more about this? In most of the cases we use simple service registrations like
Register<IA, A>
, or factories, ormade: Parameters.Of.Type
... We also use theIEnumerable
-resolve Feature.The text was updated successfully, but these errors were encountered: