Performance Tips and Tricks in .NET Applications(二)
发表于:2007-06-30来源:作者:点击数:
标签:
Use StringBuilder for Complex String Manipulation When a string is modified, the run time will create a new string and return it, leaving the original to be garbage collected. Most of the time this is a fast and simple way to do it, but wh
Use StringBuilder for Complex String Manipulation
When a string is modified, the run time will create a new string and return it, leaving the original to be garbage collected. Most of the time this is a fast and simple way to do it, but when a string is being modified repeatedly it begins to be a burden on performance: all of those allocations eventually get expensive. Here@#s a simple example of a program that appends to a string 50,000 times, followed by one that uses a
StringBuilder object to modify the string in place. The
StringBuilder code is much faster, and if you run them it becomes immediately obvious.
| namespace ConsoleApplication1.Feedback{ using System; public class Feedback{ public Feedback(){ text = "You have ordered: \n"; } public string text; public static int Main(string[] args) { Feedback test = new Feedback(); String str = test.text; for(int i=0;i<50000;i++){ str = str + "blue_toothbrush"; } System.Console.Out.WriteLine("done"); return 0; } }} | namespace ConsoleApplication1.Feedback{ using System; public class Feedback{ public Feedback(){ text = "You have ordered: \n"; } public string text; public static int Main(string[] args) { Feedback test = new Feedback(); System.Text.StringBuilder SB = new System.Text.StringBuilder(test.text); for(int i=0;i<50000;i++){ SB.Append("blue_toothbrush"); } System.Console.Out.WriteLine("done"); return 0; } }} |
Try looking at Perfmon to see how much time is saved without allocating thousands of strings. Look at the "% time in GC" counter under the .NET CLR Memory list. You can also track the number of allocations you save, as well as collection statistics.
Tradeoffs There is some overhead associated with creating a StringBuilder object, both in time and memory. On a machine with fast memory, a StringBuilder becomes worthwhile if you@#re doing about five operations. As a rule of thumb, I would say 10 or more string operations is a justification for the overhead on any machine, even a slower one.
Precompile Windows Forms Applications
Methods are JITed when they are first used, which means that you pay a larger startup penalty if your application does a lot of method calling during startup. Windows Forms use a lot of shared libraries in the OS, and the overhead in starting them can be much higher than other kinds of applications. While not always the case, precompiling Windows Forms applications usually results in a performance win. In other scenar
ios it@#s usually best to let the JIT take care of it, but if you are a Windows Forms developer you might want to take a look.
Microsoft allows you to precompile an application by calling ngen.exe. You can choose to run ngen.exe during install time or before you distribute you application. It definitely makes the most sense to run ngen.exe during install time, since you can make sure that the application is optimized for the machine on which it is being installed. If you run ngen.exe before you ship the program, you limit the optimizations to the ones available on
your machine. To give you an idea of how much precompiling can help, I@#ve run an informal test on my machine. Below are the cold startup times for ShowFormComplex, a winforms application with roughly a hundred controls.
Code StateTime
Framework JITed
ShowFormComplex JITed | 3.4 sec |
| Framework Precompiled, ShowFormComplex JITed | 2.5 sec |
| Framework Precompiled, ShowFormComplex Precompiled | 2.1sec |
Each test was performed after a reboot. As you can see, Windows Forms applications use a lot of methods up front, making it a substantial performance win to precompile.
Use Jagged Arrays—Version 1
The v1 JIT optimizes jagged arrays (simply @#arrays-of-arrays@#) more efficiently than rectangular arrays, and the difference is quite noticeable. Here is a table demonstrating the performance gain resulting from using jagged arrays in place of rectangular ones in both C# and Visual Basic (higher numbers are better):
C#Visual Basic 7
Assignment (jagged)
Assignment (rectangular) | 14.16
8.37 | 12.24
8.62 |
Neural Net (jagged)
Neural net (rectangular) | 4.48
3.00 | 4.58
3.13 |
Numeric Sort (jagged)
Numeric Sort (rectangular) | 4.88
2.05 | 5.07
2.06 |
The assignment benchmark is a simple assignment algorithm, adapted from the step-by-step guide found in
Quantitative Decision Making for Business (Gordon, Pressman, and Cohn; Prentice-Hall; out of print). The neural net test runs a series of patterns over a small neural network, and the numeric sort is self-explanatory. Taken together, these benchmarks represent a good indication of real-world performance.
As you can see, using jagged arrays can result in fairly dramatic performance increases. The optimizations made to jagged arrays will be added to future versions of the JIT, but for v1 you can save yourself a lot of time by using jagged arrays.
Keep IO Buffer Size Between 4KB and 8KB
For nearly every application, a buffer between 4KB and 8KB will give you the maximum performance. For very specific instances, you may be able to get an improvement from a larger buffer (loading large images of a predictable size, for example), but in 99.99% of cases it will only waste memory. All buffers derived from BufferedStream allow you to set the size to anything you want, but in most cases 4 and 8 will give you the best performance.
Be on the Lookout for Asynchronous IO Opportunities
In rare cases, you may be able to benefit from Asynchronous IO. One example might be downloading and decompressing a series of files: you can read the bits in from one stream, decode them on the CPU and write them out to another. It takes a lot of effort to use Asynchronous IO effectively, and it can result in a performance
loss if it@#s not done right. The advantage is that when applied correctly, Asynchronous IO can give you as much as ten times the performance.
An excellent is available on the MSDN Library.
- One thing to note is that there is a small security overhead for asynchronous calls: Upon invoking an async call, the security state of the caller@#s stack is captured and transferred to the thread that@#ll actually execute the request. This may not be a concern if the callback executes lot of code, or if async calls aren@#t used excessively
Tips for Database Aclearcase/" target="_blank" >ccess
The philosophy of tuning for database access is to use only the functionality that you need, and to design around a @#disconnected@# approach: make several connections in sequence, rather than holding a single connection open for a long time. You should take this change into account and design around it.
Microsoft recommends an
N-Tier strategy for maximum performance, as opposed to a direct client-to-database connection. Consider this as part of your design philosophy, as many of the technologies in place are optimized to take advantage of a multi-tired scenario.
Use the Optimal Managed Provider
Make the correct choice of managed provider, rather than relying on a generic accessor. There are managed providers written specifically for many different databases, such as SQL (System.Data.SqlClient). If you use a more generic interface such as System.Data.Odbc when you could be using a specialized component, you will lose performance dealing with the added level of indirection. Using the optimal provider can also have you speaking a different language: the Managed SQL Client speaks
TDS to a SQL database, providing a dramatic improvement over the generic OleDbprotocol.
Pick Data Reader Over Data Set When You Can
Use a data reader whenever when you don@#t need to keep the data lying around. This allows a fast read of the data, which can be cached if the user desires. A reader is simply a stateless stream that allows you to read data as it arrives, and then drop it without storing it to a dataset for more navigation. The stream approach is faster and has less overhead, since you can start using data immediately. You should evaluate how often you need the same data to decide if the caching for navigation makes sense for you. Here@#s a small table demonstrating the difference between DataReader and DataSet on both ODBC and SQL providers when pulling data from a server (higher numbers are better):
ADOSQL
| DataSet | 801 | 2507 |
| DataReader | 1083 | 4585 |
As you can see, the highest performance is achieved when using the optimal managed provider along with a data reader. When you don@#t need to cache your data, using a data reader can provide you with an enormous performance boost.
Use Mscorsvr.dll for MP Machines
For stand-alone middle-tier and server applications, make sure mscorsvr is being used for multiprocessor machines. Mscorwks is not optimized for scaling or throughput, while the server version has several optimizations that allow it to scale well when more than one processor is available.
Use Stored Procedures Whenever Possible
Stored procedures are highly optimized tools that result in excellent performance when used effectively. Set up stored procedures to handle inserts, updates, and deletes with the data adapter. Stored procedures do not have to be interpreted, compiled or even transmitted from the client, and cut down on both network traffic and server overhead. Be sure to use CommandType.StoredProcedure instead of CommandType.Text
Be Careful About Dynamic Connection Strings
Connection pooling is a useful way to reuse connections for multiple requests, rather than paying the overhead of opening and closing a connection for each request. It@#s done implicitly, but you get
one pool per unique connection string. If you@#re generating connection strings dynamically, make sure the strings are identical each time so pooling occurs. Also be aware that if delegation is occurring, you@#ll get one pool per user. There are a lot of options that you can set for the connection pool, and you can track the performance of the pool by using the Perfmon to keep track of things like response time, transactions/sec, etc.
Turn Off Features You Don@#t Use
Turn off automatic transaction enlistment if it@#s not needed. For the SQL Managed Provider, it@#s done via the connection string: SqlConnection conn = new SqlConnection("Server=mysrv01;Integrated Security=true;Enlist=false");
When filling a dataset with the data adapter, don@#t get primary key information if you don@#t have to (e.g. don@#t set MissingSchemaAction.Add with key):public DataSet SelectSqlSrvRows(DataSet dataset,string connection,string query){ SqlConnection conn = new SqlConnection(connection); SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = new SqlCommand(query, conn); adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; adapter.Fill(dataset); return dataset;}
Avoid Auto-Generated Commands
When using a data adapter, avoid auto-generated commands. These require additional trips to the server to retrieve meta data, and give you a lower level of interaction control. While using auto-generated commands is convenient, it@#s worth the effort to do it yourself in performance-critical applications.
Beware ADO Legacy Design
Be aware that when you execute a command or call fill on the adapter, every record specified by your query is returned.
If server cursors are absolutely required, they can be implemented through a stored procedure in t-
sql. Avoid where possible because server cursor-based implementations don@#t scale very well.
If needed, implement paging in a stateless and connectionless manner. You can add additional records to the dataset by:
- Making sure PK info is present
- Changing the data adapter@#s select command as appropriate, and
- Calling Fill
Keep Your Datasets Lean
Only put the records you need into the dataset. Remember that the dataset stores all of its data in memory, and that the more data you request, the longer it will take to transmit across the wire.
Use Sequential Access as Often as Possible
With a data reader, use CommandBehavior.SequentialAccess. This is essential for dealing with blob data types since it allows data to be read off of the wire in small chunks. While you can only work with one piece of the data at a time, the latency for loading a large data type disappears. If you don@#t need to work the whole object at once, using Sequential Access will give you much better performance.
Performance Tips for ASP.NET Applications
Cache Aggressively
When designing an app using ASP.NET, make sure you design with an eye on caching. On server versions of the OS, you have a lot of options for tweaking the use of caches on the server and client side. There are several features and tools in ASP that you can make use of to gain performance.
Output Caching—Stores the static result of an ASP request. Specified using the <@% OutputCache %> directive:
- Duration—Time item exists in the cache
- VaryByParam—Varies cache entries by Get/Post params
- VaryByHeader—Varies cache entries by Http header
- VaryByCustom—Varies cache entries by browser
- Override to vary by whatever you want:
- Fragment Caching—When it is not possible to store an entire page (privacy, personalization, dynamic content), you can use fragment caching to store parts of it for quicker retrieval later.
a) VaryByControl—Varies the cached items by values of a control - Cache API—Provides extremely fine granularity for caching by keeping a hashtable of cached objects in memory (System.web.UI.caching). It also:
a) Includes Dependencies (key, file, time)
b) Automatically expires unused items
c) Supports Callbacks
Caching intelligently can give you excellent performance, and it@#s important to think about what kind of caching you need. Imagine a complex e-commerce site with several static pages for login, and then a slew of dynamically-generated pages containing images and text. You might want to use Output Caching for those login pages, and then Fragment Caching for the dynamic pages. A toolbar, for example, could be cached as a fragment. For even better performance, you could cache commonly used images and boilerplate text that appear frequently on the site using the Cache API. For detailed information on caching (with sample code), check out the Web site.
Use Session State Only If You Need To
One extremely powerful feature of ASP.NET is its ability to store session state for users, such as a shopping cart on an e-commerce site or a browser history. Since this is on by default, you pay the cost in memory even if you don@#t use it. If you@#re not using Session State, turn it off and save yourself the overhead by adding
<@% EnabledSessionState = false %> to your asp. This comes with several other options, which are explained at the Web site.
For pages that only read session state, you can choose
EnabledSessionState=readonly. This carries less overhead than full read/write session state, and is useful when you need only part of the functionality and don@#t want to pay for the write capabilities.
原文转自:http://www.ltesting.net