One of my former colleagues once said, "Performance is a journey and not a destination." I think this statement is so true.
Because performance is relative, it is imperative and important to define BOTH a baseline and acceptance metrics for each scenario that we test. This will also provide a common platform to quantify and define success for all the stakeholders. The beauty of establishing a baseline is that you can’t deteriorate from that run. If you do, then you know which configurations will lead you back to your baseline because that was your best run before the test started deteriorating. If your next run is an improvement, then it could be your new baseline.
There are certain things we can do statically, such as code review using FxCop, code analysis in Microsoft Visual Studio, etc. Whereas, issues like resource management can also be discovered when we run it against a given hardware. Therefore, repetitive test runs for a given scenario become useful to uncover such issues.
After several performance tuning iterations, you may still find ways to get that "extra bit" of performance out of the system, but the decision always revolves around time, energy, and resources and whether to scale up or out.
I will discuss some of the best practices in an end-to-end scenario and provide links wherever possible with more detailed information. I do not discuss Microsoft SQL Server, because this topic has been covered well by my colleague in his post here.
Consider the practices that work best for your application.
DESIGN
- Try and make your application stateless: Careful consideration should be given during design to identify any aspect of the application that needs state and how to solve or workaround it so that you can make your application more scalable. As an example, you can configure ASP.NET session state on an out-of-process server.
- In general, most of the applications do more “read operation” compared to “write operation,” so using some form of distributed cache, like AppFabric Caching, can have performance benefits compared to going through the full workflow again.
DEVELOPMENT
- Turning on the debug attribute (it doesn't belong to the production environment) would significantly affect your performance as mentioned here.
- Leverage code analysis in Visual Studio and involve it into your build process: For performance, there is a performance warning section under Code Analysis that can be implemented during the build process.
- Leverage FxCop, especially the performance section for fine-tuning your application.
- Use TAP or TPL if it improves your performance, but use it judiciously as it may not benefit all scenarios\deployments.
- Avoid unnecessary exceptions as they add significant overhead to your application. DO NOT use to control logic flow.
- Avoid long loops where the code inside the loop always has something to work on or the code doesn’t cross boundaries (AppDomain\Process\Machine). String creation, manipulation, and concatenation are a few examples of this. This consumes your CPU and might devoid any CPU cycles for other threads within that process and/or system.
- Reduce Boundary crossing (AppDomain\Process\Machine\unmanaged Code) calls as it incurs marshalling overhead, thread/context switching, etc.
- Consider weak references with Cached Data.
- Avoid Pinning, and if it’s done, then the scope should be minimum; however, you should unpin it as soon as you can. Pinning during IO could be more damaging because the IO might take time. As a workaround, a buffer pool could be used.
- Acquire late and release early to minimize the duration that you hold onto a resource via locking or synchronization primitives. This helps reduce contention for the shared resources.
- Implement Dispose patterns on types containing instances of Disposable types and provide a finalizer on types holding resources that need to be freed explicitly and that do not have finalizers. Consider implementing the Dispose pattern on classes that don’t hold unmanaged resources or disposable objects but that are likely to have subtypes that do.
- Avoid calling GC.Collect as it impacts the self-tuning process of garbage collection.
DEPLOYMENT
- You can use NGen for faster start times and to reduce working set. There are scenarios where it may or may not help to use NGen as explained here and here.
- Build a leaner operating system and host: the Windows operating system is granular in nature, and you should uninstall and disable any unwanted roles, features, or Windows Services. These will unnecessarily consume your CPU and limit CPU cycles to your application. This is explained here, and most of it is true for the latest version of Internet Information Services (IIS).
- Evaluate and deploy the latest versions and service packs whenever possible. There are constant improvements that your application might benefit from, or you can tweak your application to leverage the new improvements. .NET 4.5 Server Side GC is an example of this is. Similarly, there are huge performance benefits out-of-the-box in winjs 2.0 and win 8.1 platform for JavaScript developers.
- Tune your network subsystem for network intensive workloads and monitor the network by using network monitoring tools like Netmon to see if there are any delays on network devices.
- For certain host processes, like IIS, use integrated pipeline instead of classic pipeline whenever possible.
RUNTIME
- If your application generates application log or if the host process (like IIS) has a logging feature, make sure that the log file is configured to be on a different physical disk compared to where the operating system lives and where your application lives. Ideally, each of these should be under different disks so that IOs are parallel.
- Leverage features provided within the product itself. For example, if during the performance run you realize the slowness was on the client side, like Internet Explorer, you can leverage built-in "UI responsiveness," "profiler," and "network capture" features that can provide valuable insights into the performance problem.
There are some essential tools that you can leverage during run time to fine-tune your application:
1. PerfView
2. NP .NET Profiler
![]() | Nelson Dsouza is a Technical Architect at the Microsoft Technology Center (MTC) in Mumbai. He held various roles at Microsoft in the United States, including assisting developers from Fortune 500 companies. Nelson helped these companies write applications that maximize technology investments and solve real-world problems. He recently moved to Mumbai, India. |