ASP.NET 2.0 Performance Guidelines - Threading
- J.D. Meier, Srinath Vasireddy, Ashish Babbar, John Allen, and Alex Mackman
Tune the thread pool by using the formula to reduce contention
Tune ASP.NET thread pool using maxWorkerThreads, minWorkerThreads, maxIoThreads, minFreeThreads, minLocalRequestFreeThreads and maxconnection attributes in Machine.config file for reducing contention. If requests are being queued with low processor utilization levels, this is a strong indication that ASP.NET is performing non-CPU bound work.
Why
When your application uses the common language runtime (CLR) thread pool, it is important to tune the thread pool to avoid contention issues, performance problems, and possible deadlocks.
When
Use this guideline when all of the following is true for your application:
- Your application uses the CLR thread pool
- You have available CPU cycles
- Your application performs I/O bound operations such as calling a Web method or accessing the file system
- The ASP.NET Applications/Requests In Application Queue performance counter indicates that you have queued requests
If you are unsure whether your application is using the CLR threat pool you can check as follows. Your application may be using the CLR thread pool if any of these conditions is true:
- Your application makes Web service calls.
- Your application uses the WebRequest or HttpWebRequest classes to make outgoing Web requests.
- Your application explicitly queues work to the thread pool by calling the QueueUserWorkItem method.
How
You can tune ASP.NET thread pool using maxWorkerThreads, minWorkerThreads, maxIoThreads, minFreeThreads, minLocalRequestFreeThreads and maxconnection attributes in Machine.config file. Here are the default settings.
</system.net> ... <connectionManagement> <add address="*" maxconnection="2" /> </connectionManagement> .... </system.net> <system.web> ... <httpRuntime minFreeThreads="8" minLocalRequestFreeThreads="4" ... /> .... <processModel maxWorkerThreads="20" maxIoThreads="20" ... /> .... </.system.web>
Here is the formula to reduce contention. Apply the recommended changes that are described below, across the settings and not in isolation.
- Configuration setting Default value (.NET Framework 1.1) Recommended value
- maxconnection 2 12 * #CPUs
- maxIoThreads 20 100
- maxWorkerThreads 20 100
- minFreeThreads 8 88 * #CPUs
- minLocalRequestFreeThreads 4 76 * #CPUs
- Set maxconnection to 12 * # of CPUs. This setting controls the maximum number of outgoing HTTP connections that you can initiate from a client. In this case, ASP.NET is the client. Set maxconnection to 12 * # of CPUs.
- Set maxIoThreads to 100. This setting controls the maximum number of I/O threads in the .NET thread pool. This number is automatically multiplied by the number of available CPUs. Set maxloThreads to 100.
- Set maxWorkerThreads to 100. This setting controls the maximum number of worker threads in the thread pool. This number is then automatically multiplied by the number of available CPUs. * Set maxWorkerThreads to 100.
- Set minFreeThreads to 88 * # of CPUs. This setting is used by the worker process to queue all the incoming requests if the number of available threads in the thread pool falls below the value for this setting. This setting effectively limits the number of requests that can run concurrently to maxWorkerThreads – minFreeThreads. Set minFreeThreads to 88 * # of CPUs. This limits the number of concurrent requests to 12 (assuming maxWorkerThreads is 100).
- Set minLocalRequestFreeThreads to 76 * # of CPUs. This setting is used by the worker process to queue requests from localhost (where a Web application sends requests to a local Web service) if the number of available threads in the thread pool falls below this number. This setting is similar to minFreeThreads but it only applies to localhost requests from the local computer. Set minLocalRequestFreeThreads to 76 * # of CPUs.
Note The recommendations that are provided in this section are not rules. They are a starting point. Test to determine the appropriate settings for your scenario. If you move your application to a new computer, ensure that you recalculate and reconfigure the settings based on the number of CPUs in the new computer.
Problem Example
A high traffic ASP.NET application is deployed on a 2 proc machine. When the following performance counters are monitored, it displays low CPU utilization however requests are becoming queued up resulting in low throughput.
ASP.NET\Requests Queued Process\% Processor Time (aspnet_wp.exe or w3wp.exe) The applications configuration settings are as follows:
</system.net> ... <connectionManagement> <add address="*" maxconnection="2" /> </connectionManagement> .... </system.net> <system.web> ... <httpRuntime minFreeThreads="8" minLocalRequestFreeThreads="4" ... /> .... <processModel maxWorkerThreads="20" maxIoThreads="20" ... /> .... </.system.web>
Solution Example
A high traffic ASP.NET application is deployed on a 2 proc machine. The thread pool settings are configured using the formula for reducing contention. This improves the CPU utilization and lowers request queuing, giving better throughput.
The application configuration settings are as follows:
</system.net> ... <connectionManagement> <add address="*" maxconnection="24" /> </connectionManagement> .... </system.net> <system.web> ... <httpRuntime minFreeThreads="176" minLocalRequestFreeThreads="152" ... /> .... <processModel maxWorkerThreads="100" maxIoThreads="100" ... /> .... </.system.web>
References
- For more information, see Knowledge Base article 821268, "PRB: Contention, Poor Performance, and Deadlocks When You Make Web Service Requests from ASP.NET Applications," at http://support.microsoft.com/default.aspx?scid=kb;en-us;821268.
- For more information, see "ASP.NET Tuning" in Chapter 17, "Tuning .NET Application Performance." at http://msdn.microsoft.com/library/en-us/dnpag/html/scalenetchapt17.asp
Configure minIoThreads and minWorkerThreads for Burst Load
Use the minIoThreads and minWorkerThreads settings in Machine.config file to configure a minimum number of worker threads and I/O threads for load conditions. Additionally you can use the SetMinThreads method to increase the number of idle Worker threads and the number of I/O completion threads that the ThreadPool class maintains.
Why
If your application experiences burst loads seperated by prolonged periods of inactivity, the thread pool may not have enough time to reach an optimal level of threads. The sudden requirement for a larger number of threads from the ThreadPool class may result in slow computer response time. If the minimum number of Worker threads or I/O threads is configured, the ThreadPool immediately creates new threads up to the minimum number specified during a time of high load. Thus improving the response time and performance of the application in burst loads as well. After inactivity, these threads die.
When
Tune the minimum number of threads only when there is load on the Web server for short periods of time (10 minutes or less) and your application shows decline in response time.
How
There are two methods for setting the minIoThreads and minWorkerThreads for ThreaPool class. The recommended value for both the attributes is 50, but it may not work for all applications. You will have to tune the setting which is best for your application.
Using configuration In the <processModel> configuration section in the Machine.config, set the following attributes. Note these attributes are not in the configuration file by default. You must add it. minWorkerThreads: Configures the minimum number of worker threads to be used for the process on a per-CPU basis. For example, if this value is 10 on a single-processor server, ASP.NET uses the runtime APIs to set the process limit to 10. On a two-processor server, the limit is set to 20. The default is 0.
minIoThreads: Configures the minimum number of asynchronous I/O threads to be used for the process on a per-CPU basis. For example, if this value is 10 on a single-processor server, ASP.NET uses the runtime APIs to set the process limit to 10. On a two-processor server, the limit is set to 20. The default is 0.
<system.web> ... <processModel minWorkerThreads=10 minIoThreads=10 .../> ... </system.web>
Programmatically Additionally you can use following API of the ThreadPool class for setting the minimum number of worker threads and the minimum number of asynchronous I/O threads for the ThreadPool programmatically.
public static bool SetMinThreads(int workerThreads, int completionPortThreads);
If you want to know the current setting for minimum number of worker threads and the minimum number of asynchronous I/O threads for the ThreadPool, use the GetMinThreads method of ThreadPool class.
public static void GetMinThreads(out int workerThreads, out int completionPortThreads);
Problem Example
An ASP.NET application declares lottery results every day at 10:00 AM and 4:00 PM. There is a heavy load on the Web server for short periods of time directly after the lottery results are declared. The application has been deployed with default minThreads configuration setting of 0 threads. The problem with this application is that the ThreadPool does not have enough time to reach the optimal level of threads to handle the load and hence users experience slow response.
!!! Solution Example An ASP.NET application which declares lottery results every day at 10:00 AM and 4:00 PM. There is heavy load on the Web server only for short periods after the lottery results are declared. The minimum number of Worker threads or I/O threads is configured to 10 threads.
When the application starts, the ThreadPool immediately creates 10 threads to handle heavy load. If 10 threads is not enough, more threads will be allocated at half-second intervals. Thus user experience better response improving the performance of the application.
<system.web> ... <processModel minWorkerThreads=10 minIoThreads=10 .../> ... </system.web>
References
- For more information, see Knowledge Base article 810259, "FIX: SetMinThreads and GetMinThreads API Added to Common Language Runtime ThreadPool Class," at http://support.microsoft.com/default.aspx?scid=kb;en-us;810259
- For more information, see Knowledge Base article 827419, "PRB: Sudden Requirement for a Larger Number of Threads from the ThreadPool Class May Result in Slow Computer Response Time," at http://support.microsoft.com/default.aspx?scid=kb;en-us;827419
- For more information on performance in ASP.NET see "Chapter 6 - Improving ASP.NET Performance" at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt06.asp
- For more information, see "ASP.NET Tuning" in Chapter 17, "Tuning .NET Application Performance." at http://msdn.microsoft.com/library/en-us/dnpag/html/scalenetchapt17.asp
Do Not Create Threads on a Per-Request Basis
Avoid manually creating threads on each client request. But if you have work that is not CPU bound that can run in parallel, use the infrastructure provided by the .NET Framework or CLR thread pool for asynchronous processing.
Why
Creating threads is an expensive operation that requires initialization of both managed and unmanaged resources.
When
Consider using asynchronous calls if you have work that is not CPU bound that can run in parallel with the call. For example, this might include disk I/O bound or network I/O bound operations such as reading or writing files, or making calls to another Web method.
How
Use following options for asynchronous processing.
Use the infrastructure provided by the .NET Framework You can use the infrastructure provided by the .NET Framework to perform asynchronous operations by calling the Beginsynchronous and Endsynchronous methods (where synchronous represents the synchronous method name)
Use CLR thread pool If the above asynchronous calling pattern is not an option, then consider using threads from the CLR thread pool. The following code fragment shows how you queue a method to run on a separate thread from the thread pool.
WaitCallback methodTarget = new WaitCallback(myClass.UpdateCache); bool isQueued = ThreadPool.QueueUserWorkItem(methodTarget);
Problem Example
An ASP.NET application performs long running processing on a separate thread. Unfortunately it creates a thread per request thus making the application less performant.
..... ThreadStart methodTarget = new ThreadStart(myClass.DoLongRunningWork); Thread myThread = new Thread(methodTarget); myThread.Start(); .....
!!! Solution Example An ASP.NET application performs long running processing on a separate thread. This code sample uses threads from the CLR thread pool for executing the long running process asynchronously. This approach does not have the overhead associated of manually creating new threads thus improving performance.
... WaitCallback methodTarget = new WaitCallback(myClass.DoLongRunningWork); bool isQueued = ThreadPool.QueueUserWorkItem(methodTarget); ...
References
- For more information on performance in ASP.NET see "Chapter 6 - Improving ASP.NET Performance" at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt06.asp
- For more information, see "ASP.NET Tuning" in Chapter 17, "Tuning .NET Application Performance." at http://msdn.microsoft.com/library/en-us/dnpag/html/scalenetchapt17.asp