-
-
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
Improve parallelism / reduce thread blocking #137
Comments
Hi, thanks for context and for analysis.The only lock DryIoc is using inside the Scope to ensure single creation of Scoped / Singleton services. Currently there is one locker object per Scope. So probably it need to be scaled. |
I don't know much about dryIoc, but I solved similar issue previously by using .Net built-in Concurrent* types. Looks like |
It is different. The lock here is used on a purpose. We just need to make it less a bottleneck. |
I have been thinking about this issue lately. Is the lock there because IMTools AVLTree implementation is not thread safe? There are many white papers about non blocking binary search trees. fe: Or is the lock fixing some dryIoc related issue? I noticed you have done benchmark here: This benchmark however does not consider DryIoc way of using given data structure. ConcurrentDictionary would not need lock for writing / reading. When doing load testing DryIoc's
Maybe this is the reason why this lock matters so much web application. DryIoc WebAPI extension registers Controllers with WebRequestScope, and then we have a lot singleton services. I think new benchmark is needed which tests IoC containers in multi threaded environment. public void Demo()
{
// Configs
// INTEL® XEON® W-3275M PROCESSOR, 28 Cores, 56 Threads
int threadCount = 56;
int iterations = 10;
int i = 0;
Thread[] threads = new Thread[threadCount];
// Create threads
for (i = 0; i < threadCount; i++)
{
threads[i] = new Thread(new ThreadStart(delegate ()
{
for (int j = 0; j < iterations; j++)
{
// Benchmark loop
// Call method which: Open Scope, Resolve scoped service type with large number of singleton dependencies
}
}));
}
// Start all
for (i = 0; i < threadCount; i++)
{
threads[i].Start();
}
// Join all
for (i = 0; i < threadCount; i++)
{
threads[i].Join();
}
} What do you think? |
The sample code above could be also used to test thread safety. Inside the loop we would add results into concurrentStack/concurrentDictonary and after test is finished verify it resolved services correctly. Then we change thread count to insanely big number to make sure collisions happen. |
Hi @Havunen
No,
The
Benchmark is just for comparison of two collections of different nature, the lock here is just to establish artificial common ground.
I hope to improve
I would only appreciate such a benchmark, maybe a PR? |
Consider its fixing by splitting Scope to SingletonScope with 16 locks assigned by hash. Scope wil proceed to use a single lock. |
Working on #139 I found a better way to scale locks, using Ref slots for storing the created value in the ImMap and then locking on the slot itself. So we don't need a separate locker object and instead scaling to lock per service. |
Btw, have you seen these videos from Federico Lois. He shares some cool optimization techniques for C#. |
Yeah, I have saw. Some if them appeared on Ayende's blog first. I think this was brought by development of RavenDB. I have also chat with Federico once regarding DryIoc perf :-) |
Cool :) |
… enough if we use Slot for locking - see comments in #137 changed: GetDefaultValueExpression to use Constant(null, type) for the reference type to Interpret without reflection Invoke
…hat's mean the lock per stored item; cleanup
* added: IScope.GetOrAddWithResolverContext but without usage yet; added: Request.ResolverContextParamExpr without usages * removed: SingletonScope and I am back to single Scope which should be enough if we use Slot for locking - see comments in #137 changed: GetDefaultValueExpression to use Constant(null, type) for the reference type to Interpret without reflection Invoke * added: work for #137 - making Scope to lock on EACH item reference, that's mean the lock per stored item; cleanup * BM results are worse than before and SO is still there * new version of FEC with a bit less memory and fixed short var addressing; cleanup WIP stuff * changing the CurrentScopeReuse to use FactoryDelegate as value factory instead of CreateScopedValue for #154 * changing the SingletonReuse to use FactoryDelegate as value factory instead of CreateScopedValue for #154 - SO is still there :( * fixed: DependencyDepthToSplitObjectGraph was not applied when using different With... overloads * fixed: failing test relying on reflection members layout
* added: IScope.GetOrAddWithResolverContext but without usage yet; added: Request.ResolverContextParamExpr without usages * removed: SingletonScope and I am back to single Scope which should be enough if we use Slot for locking - see comments in dadhi#137 changed: GetDefaultValueExpression to use Constant(null, type) for the reference type to Interpret without reflection Invoke * added: work for dadhi#137 - making Scope to lock on EACH item reference, that's mean the lock per stored item; cleanup * BM results are worse than before and SO is still there * new version of FEC with a bit less memory and fixed short var addressing; cleanup WIP stuff * changing the CurrentScopeReuse to use FactoryDelegate as value factory instead of CreateScopedValue for dadhi#154 * changing the SingletonReuse to use FactoryDelegate as value factory instead of CreateScopedValue for dadhi#154 - SO is still there :( * fixed: DependencyDepthToSplitObjectGraph was not applied when using different With... overloads * fixed: failing test relying on reflection members layout
Hey,
We are using DryIoc in web application which is very multi threaded environment. When doing load testing in our application I noticed DryIoc is locking quite a lot. 19% of threads are blocked.
This report is based on analyzing w3wp process during high load.
I used windows task manager to take memory dump of the process and analysed it using DebugDiagx64 (Microsoft tool)
Here are the results:
Thread 30 - System ID 10676
Entry point clr!Thread::intermediateThreadProc
Create time 6/12/2019 5:57:28 PM
Time spent in user mode 0 Days 00:00:03.015
Time spent in kernel mode 0 Days 00:00:00.765
This thread is not fully resolved and may or may not be a problem. Further analysis of these threads may be required.
The largest lock is here, pointing to dryioc
.Net Call Stack
Full Call Stack
The text was updated successfully, but these errors were encountered: