Why Are WCF Responses Slow and SetMinThreads Does Not Work?
Customers reported the following WCF performance issue recently:
· A WCF client sends 10 requests concurrently to a self-hosted WCF service on 10 different threads. The service operation is very simple and it simply sleeps for 1 second. But the latencies for the 10 requests on the client side were distributed from 1 to 3.3 seconds. Why is this?
First of all, WCF uses managed I/O threads to handle requests. The CLR ThreadPool keeps a certain number of idle I/O threads from being destroyed. When more I/O threads are needed, they are created by the ThreadPool, which is kind of expensive. The number of idle threads is specified by the “MinIOThreads” setting. You can use the ThreadPool.GetMinThreads() API to check what settings you have for your application. By default in a standalone application, this setting is the number of the CPUs that you have on the machine. For example, on my laptop with 2-core, this setting is 2. I observed the following delay numbers in the above scenario:
Thread 0 takes: 1009 ms
Thread 2 takes: 1286 ms
Thread 3 takes: 1799 ms
Thread 4 takes: 2016 ms
Thread 6 takes: 2241 ms
Thread 5 takes: 2256 ms
Thread 7 takes: 2752 ms
Thread 8 takes: 2766 ms
Thread 9 takes: 2967 ms
Thread 1 takes: 3315 ms
Because of this, you would want to bump up this MinIOThreads setting with the ThreadPool.SetMinThreads() API on the service side, for example:
ThreadPool.SetMinThreads(Environment.ProcessorCount, 10);
You may notice that this still does not solve the above problem with .NET 3.5. Why?
Eric, who is the CLR ThreadPool expert, told me that there was a bug in .NET 3.5. ThreadPool does not honor what SetMinThreads() provides. So the above logic does not work. Fortunately this has been fixed in .NET 4.0 and the Microsoft has provided the following QFE for 3.5:
https://support.microsoft.com/kb/976898
Once you have this QFE installed, you would get much better result:
Thread 1 takes: 1026 ms
Thread 0 takes: 1028 ms
Thread 3 takes: 1081 ms
Thread 4 takes: 1074 ms
Thread 2 takes: 1084 ms
Thread 6 takes: 1072 ms
Thread 5 takes: 1073 ms
Thread 8 takes: 1029 ms
Thread 9 takes: 1021 ms
Thread 7 takes: 1154 ms
The sample code is attached.
Comments
Anonymous
November 30, 2010
Have you guys done anything about concurrency and "reliable sessions"? In 3.5 it was impossible to do what your example does in the case where your server is using BasicHTTPBinding (w/o ReliableSession set).Anonymous
September 28, 2011
Great, It works fine with Http but after changing type to TcpPipe getting some unknown exception after executing 8-9 threads.. any idea?