- Table of Contents
- .NET Book Recommendations
- What Is .NET?
- The Microsoft .NET Framework
- The Common Language Runtime (CLR), the Common Type System (CTS), and the Common Language Specification (CLS)
- .NET Framework Class Library
- Visual Studio .NET
- .NET Enterprise Servers and .NET My Services
- .NET Compliant Languages
- C#
- Visual Basic .NET (VB .NET)
- ASP.NET
- XML Web Services
- ADO.NET
- XML.NET
- Windows Forms
- Why .NET?
- Displaying Errors with the Error Provider
- COM Interoperability
- Comparing Java and .NET
- Calling Unmanaged Code
- .NET Application Security
- Code Access Security
- .NET Standards Support
- Numeric Types in the .NET Framework
- Working with Strings
- Formatting Strings
- Trimming Character Strings
- Comparing Strings in .NET 2.0
- Arrays and Collections
- Arrays as Class Members
- Sorting a Multi-Dimensional Array
- Sorting a Multi-Dimensional Array with LINQ
- File I/O (System.IO)
- Working with File Names
- Using the File System
- Working with Files and Directories
- Monitoring the File System
- Working with Streams
- Working with Text Encodings
- Working with Date and Time
- Extending the DateTime Class
- Fun with Dates
- Exceptions
- Delegates
- Events
- Asynchronous Programming
- Asynchronous File I/O
- Timers
- Random Numbers
- Cryptographically Secure Random Numbers
- Serialization
- MultiThreading (System.Threading)
- Multi-Threading Overview
- The Managed Thread Pool
- Managed Threading
- Thread Synchronization
- Synchronizing Data Access
- Trace Debugging
- Tracing in .NET 2.0
- ASP.NET Trace
- Validating User Input in ASP.NET Web Pages
- Event Logging
- Monitoring Application Performance
- Accessing the Registry
- Accessing Environment Information
- Environment Variables in .NET 2.0
- Managing Windows Forms Applications
- Working with Email
- Working with Graphics
- Animating a Background
- Working with Images
- Drawing Cycloid Curves
- Simulating the Spirograph
- Building International Web Applications
- .NET Compact Framework
- Mobile Web Development with ASP.NET
- Speech Technologies
- Microsoft MapPoint Web Service
- Working with Typed DataSets
- Using Relationships in DataSets
- DataColumn Expressions
- Playing Simple Sounds
- Playing Sounds with .NET 2.0
- Returning an Image in a Web Page
- RSS
- Best Practices Project Structure
- Best Practices Application Blocks
- The Data Access Application Block
- The Exception Management Application Block
- Best Practices — Performance
- Best Practices — Performance and Scalability
- Best Practices - Testing
- Reading the Tea Leaves, 2005
- Predictions: A Look Back at 2005, and a Look Ahead to 2006
- .NET Downloads
- Application Deployment Overview
- Application Deployment — Versioning
- Application Deployment — Version Policy
- Application Deployment — Packaging and Distribution
- .NET Remoting Overview
- A Remoting Demonstration
- Remoting Configuration
- Remoting: Lifetimes and Leases
- Remoting: Other Issues
- Attributes
- Writing Custom Attributes
- Accessing Attributes in Code
- Reflection
- Class Design: Inheritance, Interface, or Composition?
- The TriTryst Game
- Console Applications in .NET 2.0
- New File I/O Methods in .NET 2.0
- Building Projects with MSBuild
- Unmanaged Callbacks in .NET 2.0
- Timer Troubles
- Non-Rectangular Windows Forms
- Windows Forms Transparency
- 10 Things I Hate About Visual Basic
- 10 Things I Hate About C#
- Background Processing with Idle Time
- Scaling Windows Forms
- Reading and Writing Binary Data
- New Memory Management Functions in .NET 2.0
- Compatibility Between .NET 1.1 and .NET 2.0
- Managed Debugging Assistants in .NET 2.0
- XDir: A Program for Viewing Directory Sizes
- The Microsoft.VisualBasic Namespace
- Operator Overloading
- Working with GPS Data
- Hidden Visual Studio Tools
- .NET 3.0
- The .NET 2.0 Stopwatch Class
- Nullable Types
- Drawing Rotated Text
- Unsafe Code
- Other .NET Languages
- Compiler Directives
- Safe Handles
- Predictions, 2007 Edition
- New Features in C# 3.0
- Generics
- Network Client Programming
- On the Misuse of Exceptions
- Maximum Object Size in .NET
- More on Maximum Object Sizes
- Keyed Collection Memory Limitations
- Matching String Endings
- Allocating Small Data Structures
- Grumbling About Limitations
- Some Thoughts on the Nature of What We Do
- Working with Predicates in Collections
- Working with DataReaders
- Outputting XML with XmlWriter
- Writing XML Data
- Working with Compression
- Another Look at Compressed Streams
- Compressing a Very Large File
- Canonical URIs
- Constructing URIs
- Using OneWayAttribute for Remote Calls
- Selecting a Garbage Collector
- Linked List
- Linked List Application - The MRU List
- Auto-implemented Properties in C#
- The HashSet Collection
- Looking Ahead: 2018
- An Experiment in Optimization
- A Larger Integer
- Extension Methods
- Language Integrated Query (LINQ)
- Variable Length Parameter Lists
- The ReaderWriterLockSlim Synchronization Primitive
- Sorting a Text File
- Sorting a Large Text File
- Using ListView with Large Data Sets
- LINQ One-Liners
- Regular Expression Optimization
- Random File I/O
- Computing the Size of a Structure
- More on Computing Structure Sizes
- UnmanagedMemoryStream
- Dynamically Loading Code
- Building a String Table
- Delegates Versus Function Pointers
- Visual Studio Editor Features
- A Simple Profile Timer
- New Features in C# 4.0
- IEnumerator or IList?
- New Features in .NET 4.0
- Set Operations with IEnumerable and HashSet
- Using File Locks
- Extending Object Functionality
- Clearing a HashSet
- When Hash Codes Matter
- Parsing Command Line Options
- Creating a Single-Instance Program
- Asynchronous Windows Forms Events
- The BackgroundWorker Component
- Fixing a Dumb Mistake
- Thinking About Multi-Threaded Programs
- JavaScript Object Notation
- Useful .NET-related Sites
- Markov Models
- Building an Order 0 Markov Model
- Higher Order Markov Models
- Webmaster's Guide to robots.txt
- An Overview of the Parallel Extensions to .NET
- Parallel Extensions Synchronization Objects
- Thread Safe Collections
- A Bug and a Conundrum
- Another Bug and an Answer
- Task Parallel Library
- Good and Bad Ideas in C#
- Parallel LINQ
- Copying Large Files
- Replacing File.Copy
- Learning from Our Mistakes
- Symbolic Links
- There Is No Easy Fix
- Tracking Hurricanes
- Examining Hurricane Data
- Searching for Multiple Strings
- Simple JSON Processing
- Aho-Corasick String Searching
- Writing a Web Crawler
- Web Crawler Politeness
- Source Control Management
- Subversion
- Communicating with Datagrams
- Fun with Actions and Funcs New
- The Future of Media
- The Importance of Metadata
- Of Comparison and IComparer
- IComparer, Comparer, IComparable, Oh My!
- Comparing Generic Types New
- A Simple HTTP Server New
- Informit Reference Library
Best Practices — Performance
Last updated Nov 11, 2004.
Many programmers are obsessed with performance issues, wanting to write "the fastest code" in all situations, even when doing so doesn't provide any real benefit. And all too often, programmers concentrate their optimization efforts in the wrong place: on areas of the program that aren't executed often enough to be worth the effort.
For line of business applications, performance is primarily a design and infrastructure consideration. When combined with good algorithm selection, a good design running on the right hardware will perform quite well with no particular emphasis having been placed on performance by the application developer. That said, there are some places in which a simple coding decision can result in a huge gain or loss in performance. There are many such places and I can't cover them all here, but I will touch on those that have been found to provide a good performance boost with relatively little effort.
Garbage Collection
Contrary to the myth, automatic memory management is not significantly slower than doing it yourself. The .NET memory manager provides very fast--almost instantaneous--allocations, and the process of cleaning up (garbage collection) is optimized so as to be as unobtrusive as possible. In general, garbage collection "just works", and you don't have to mess with it.
The System.GC class controls the garbage collector, and has a method called Collect that you can call to force a garbage collection. Don't do this! The garbage collector keeps track of the state of memory and "knows" when to collect for best performance. Applications that try to help the garbage collector by calling GC.Collect periodically usually end up running slower than those that just let the garbage collector do its thing.
An issue related to automatic memory management is deallocation of unmanaged resources. Because the garbage collector doesn't run at any set time, there is no deterministic finalization of objects. Objects are finalized when the garbage collector gets around to doing it. This can cause trouble if you are using scarce unmanaged resources. The .NET Framework includes a Dispose design pattern that was designed precisely to address this issue. Used correctly, the Dispose pattern will allow you to free unmanaged resources deterministically while still working within the recommendations of the garbage collector. See the articles linked below, and study the Automatic Memory Management topic in the .NET SDK documentation for information on how to implement the Dispose pattern and IDisposable interface.
String Concatenation
By definition in the Common Language Specification (CLS) any object of type String is immutable. This means that once the object is created, its contents cannot change. When a string is modified, the runtime creates a new string and returns it, leaving the original to be garbage collected. This is fast and simple for infrequent string concatenations, but when you're repeatedly modifying a string, the overhead of collecting those scraps can seriously degrade performance. If you must repeatedly modify a string, use a StringBuilder object with an appropriate initial buffer size rather than direct string manipulation. Just as a rough indicator, string concatenation is anywhere from 10% to 500% slower than using a StringBuilder.
Reference Types versus Value Types
You should use simple structs rather than classes when you can get away with it. Allocating and initializing a value type (struct) is an order of magnitude faster than creating an instance of a class. Member access is about the same. Value types are perfect for small structures like points, sizes, vectors, and other simple types, and using them can noticeably increase performance. However, they are a lot less flexible than reference types and they can actually hurt performance if you're continually treating them like reference types. The reason is that in order to treat a value type like a reference type, the runtime must perform "boxing" and "unboxing" operations. The overhead of these operations causes the use of value types to be more expensive than corresponding reference types.
Boxing and unboxing occur any time you treat a value type like a reference type. This includes passing value types to methods that expect object parameters, and when you add a value type to an array, collection, or hash table.
Caching
Output caching can provide a huge performance boost for your ASP.NET applications, at very little cost. On server versions of the operating system, you have a lot of options for tweaking the use of caches. Make sure that you keep caching in mind when designing your ASP.NET applications. Using output caching, one of the techniques available to web applications, can mean a difference of 10 times the number of requests per second that your application can serve.
Throw Fewer Exceptions
Throwing exceptions can be very expensive, so make sure that you don't throw a lot of them. Use the performance monitor to see how many exceptions your application is throwing. You might be surprised to find that certain parts of your application throw a lot more exceptions than you expected. You can realize a good performance gain by finding and designing away exception-heavy code.
Note that I'm not talking about try/catch blocks here, which are very performant in the normal case. The only time you incur overhead with try/catch is when an exception is thrownsomething that should be relatively rare. And then, the benefit of the catch far outweighs the performance cost.
You should avoid using exceptions for control flow and for not-so-exceptional things like end of file. Save exceptions for the truly exceptional--operations that cannot complete, or unexpected errors that crop up in normal operation.
The runtime can throw exceptions even if your code doesn't. In addition, COM HRESULT values are returned as exceptions during interop calls. You should use the performance monitor to see how many exceptions you are throwing, and use the debugger to check the source of those exceptions if you find that they are excessive.
Further Reading
Writing high performance .NET applications is a much deeper topic than I can cover in this small article. The items above are among the most important, but certainly not exhaustive. You can gain a better understanding of this topic by studying these two articles from MSDN:
Performance Tips and Tricks in .NET Applications
Performance Considerations for Run-Time Technologies in the .NET Framework



Account Sign In
View your cart