I am doing some profiling on a WCF service that uses EF (System.Data.Entities)
to read from a SQL DB. When I spin up multiple parallel clients that hit the service, the CPUs all go to 100%, performance generally tanks, and everything bogs down.
In profiling this with the concurrency profiler, I found 85% of the time is spent in synchronization, with only about 4% being actual code execution. Looking deeper into the stack trace, most of the synchronization seems to be from a call to WaitForSingleObject
in System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync
. The stack shows that the call goes to a native method wrapper and then winds up at kernel32.dll!_WaitForSingleObject
.
Has anyone experienced this before? Is there any way to do something about this? I'm not really throwing absurd load at this, only about 20 parallel clients, and it's all read-only, so I'm surprised the threads would even bother to synchronize.
I've been fighting with this for a week now and I just can't explain it. Any help would be appreciated!
Are you able to distill this to a small code sample that reproduces the problem? What version of EF are you using?
Here are a few observations based on the information you've given so far.
Anything less than EF 6 is always synchronous. With EF 6 you have the option to use async methods instead. However, don't do this unless your WCF service also uses the async pattern.
You can write a WCF service whose implementation is asynchronous. See this documentation for more information.
If you use one of the above methods, but not both, your code will not be asynchronous but will incur unnecessary synchronization overhead. Especially avoid Task.Run()
or equivalents, since these will simply move work to another thread without actually improving throughput.
Finally, another unrelated idea. Could your issue be related to EF initialization? When EF builds the metadata for a model it does this once per connection string. If multiple threads attempt to use the same model and that model has not yet been initialized, all threads will block until initialization is complete. To see if this is your issue, make one call to the service and allow it to complete. Then submit your 20 parallel requests. Do they still max out the CPU?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With