Redgate logo for print use

ANTS Performance ProfilerANTS Performance Profiler

Back to all resources

Article

How to improve .NET application performance – 11 tips

11 .NET performance improvement tips


Concurrent asynchronous I/O on server applications

Tugberk Ugurlu, Redgate | @tourismgeek | www.tugberkugurlu.com

Concurrent I/O operations on server applications are tricky and hard to handle correctly. This is especially the case when applications are under high load. I would like to highlight two core principals to keep in mind.

Firstly, never perform concurrent I/O operations in a blocking fashion on your .NET server applications. Instead, perform then asynchronously, like the following code snippet:

[HttpGet]
public async Task>
AllCarsInParallelNonBlockingAsync() {
	IEnumerable>> allTasks = PayloadSources.Select(uri => GetCarsAsync(uri));
	IEnumerable[] allResults = await Task.WhenAll(allTasks);
		return allResults.SelectMany(cars => cars);
}
							

When concurrent requests are made, the asynchronous method typically performs six times faster.

Secondly, remember to also perform load tests against your server applications based on your estimated consumption rates if you have any sort of multiple I/O operations.

For more information, see http://www.tugberkugurlu.com/archive/how-and-where-concurrent-asynchronous-io-with-aspnet-web-api

Consider String.Replace() for parsing, trimming, and replacing

Rob Karatzas | @ebiztutor

String.Replace() can be a good choice, particularly for individual case-sensitive replacements where it outperforms other algorithms. String Split/Join and String Split/Concat perform equally, while LINQ and RegEx have poor performance for parsing, trimming, and replacing.

For more information, see: http://blogs.msdn.com/b/debuggingtoolbox/archive/2008/04/02/comparing-regex-replace-string-replace-and-stringbuilder-replace-which-has-better-performance.aspx

Don't use interop (WinFormsHost) with WPF when display performance is critical

Srinivasu Achalla | www.practicaldevblog.wordpress.com

When creating a Win32 window in particular, the display performance will fall by a few hundred milliseconds if there are several interop controls involved.

Eagerly create common object types to prevent constant evaluation

Paul Glavich | @glav | http://weblogs.asp.net/pglavich

Many of us use code frequently in applications. For example:

if (myobject.GetType() == typeof(bool)) { …. /* do something */ }
-- or --
if (myobject.GetType() == typeof()(MyCustomObject)) { …. /* do something */ }

							

Instead, consider eagerly creating these type of instances:

public static class CommonTypes
{
	public static readonly Type TypeOfBoolean = typeof(bool);
	public static readonly Type TypeOfString = typeof(string);
}

							

You can then do the following:

if (arg.GetType() == CommonTypes.TypeOfBoolean)
{
	// Do something
}
							

Or:

if (arg.GetType() == CommonTypes.TypeOfString)
{
	// Do something
}
							

And save the cost of performing the typeof(bool) or typeof(string) repeatedly in your code.

Make use of the Async await pattern

Gavin Lanata

Starting to introduce use of the Async await pattern on an established codebase can pose challenges which don't exist when using Async on a new project. If the code already makes use of background threads which do Thread.Sleep(), when you change to using Async methods, make sure you switch from Thread.Sleep() to await Task.Delay() instead. This achieves the same result of suspending that bit of work, but does it in a non-blocking way which can reduce the total resources needed by the application.

NGen your EntityFramework.dll to improve startup performance

Raghavendra Rentala | @vamosraghava

From Entity Framework version 6, core assemblies of Entity Framework are not part of the .NET framework and so a native image is not generated automatically. This despite the fact that a native image can improve memory when the assembly is shared among multiple processes.

Generating a native image of EntityFramework.dll and EntityFramework.SqlServer.dll will not only avoid multiple JIT compilations, but also improve the startup process significantly.

Remember that different ways of reading large files have different performance characteristics

Srinivasu Achalla | www.practicaldevblog.wordpress.com

Using File.ReadAllBytes() can sometimes be faster than reading a stream obtained by opening a large file such as XML. This can be influenced by factors like whether you need to access all of a file or just a small portion of it. If in doubt, use a tool like ANTS Performance Profiler to measure the impact of different methods of file access.

Remember that type casting and conversions incur performance penalties

Rob Karatzas | @ebiztutor

If you must perform type casting and conversions, try doing as little of them as possible. For example, rather than:

if (obj is SomeType) then { ((SomeType)obj) …. };
							

Which causes two casts (one when performing the 'if' evaluation and one in the body of the If statement), use this:

var myObj = obj as SomeType;
if (myObj != null) { myObj … };
							

That way, the cast is only done once with the 'as' parameter. If it fails, the value is NULL. If not, you can go ahead and use it, resulting in only one cast.

For more information, see: https://msdn.microsoft.com/en-us/library/ms173105.aspx

Spot potential issues in your code with Concurrency Visualizer

Michael Sorens | www.linkedin.com/in/michaelsorens

From Visual Studio 2013, Concurrency Visualizer became a plugin rather than a standard feature. It is still a tremendously useful performance analysis tool, however, particularly when you use the SDK to instrument your code with flags, messages, alerts, and spans.

Concurrency Visualizer provides several views: utilization, threads, and cores. For me, the threads view is most useful. MSDN describes the threads view, but that page does not do justice to the power of what you can see and find (however, they do offer another page within the docs that gives a bigger glimpse as to the power lurking beneath your fingertips).

The following Concurrency Visualizer timeline is a good example:

The Concurrency Visualizer timeline
The thread in use

This is from a recent project where a multithreaded system has a large number of users making requests via a web interface and expecting prompt responses back. The system allows an administrator to hot swap pieces of the back end supposedly without interfering with the horde of eager users. But there was a delay of a few seconds now and then, and the source was not clear.

After instrumenting the likely sections of code for Concurrency Visualizer, starting a CV data collection, then running thousands of automated user inputs in parallel with dozens of automated admin inputs, a pattern quickly emerged in the graph.

There was a synchronization lock being held in the admin chunk of code (the "Clean" span) that blocked the end-user processing (the "converse" span) significantly. You can see in the illustration that most of the end-user time was waiting for that lock (the red bar).

I had only to visually scan the timeline to find my markers. Concurrency Visualizer revealed what was being blocked; when the lock was released on one thread and – via the vertical black line at the end of the red block – how that tied back to the other thread and allowed it to continue; and provided stack threads that told me exactly where in the code all this was happening.

Take advantage of spans

Michael Sorens | www.linkedin.com/in/michaelsorens

Spans – regions of code to explicitly demarcate on a timeline graph – are a great feature in Concurrency Visualizer. There are two big things to watch out for.

Firstly, when instrumenting spans, the documentation suggests you use:

var span = Markers.EnterSpan(description);
// do stuff here
span.Leave();

							

That pattern is more error prone than it needs to be – you must manually ensure and remember to "close" your span through all code pathways.

Secondly, you can enhance their disposable nature further with the using statement:

using (Markers.EnterSpan(description))
{
	// do stuff here
}
							

Perception is king

Ben Emmett | Redgate

If you can't do everything quickly, sometimes it's enough just to make a user think you're quick.

From a user's perspective, the most important time is often the delay until they can see something useful on a page, rather than every single detail being available. For example, if serving up a Tweet button adds 200ms to a page's load time, consider using ajax to load the button asynchronously after the more important content is available.

This principle applies to desktop apps too. Ensuring that long-running tasks are done off the UI thread avoids the impression that an application has "locked up", even if an action takes the same length of time to complete. Techniques like spinnies and progress indicators also have a role to play here, letting the user feel as though progress is being made.

How to improve .NET application performance – 11 tips