Pitfalls and Programming Idioms to Avoid


This section covers various programming pitfalls and idioms you ll want to avoid when you write high-performance Web applications. To a large extent, it s a matter of understanding that what may work fine in client applications can have a very detrimental impact in server applications.

There are some general programming idioms to avoid when you program Web applications. The major performance killers for server-side applications generally fall into a few major categories: excessive memory allocation, locks, and I/O.

Excessive Memory Allocation

Performing memory allocations for every Web request will have a significantly detrimental effect on performance so, whenever possible, you should avoid memory allocation on a per-request basis. Consider using the stack-based buffers instead of allocating from a heap. ATL 7.0 provides some classes to assist in this.

The CFixedStringT class allows you to use the functionality of CString while avoiding memory allocations in most common scenarios. For example, if you re reasonably certain the data you want to put into a character buffer is less than MAX_PATH , you can declare a string of type CFixedStringT < CStringA, MAX_PATH > , which won t do any allocations unless the string exceeds MAX_PATH characters .

The CTempBuffer class provides functionality for generic memory buffers similar to what CFixedStringT provides for CString s. The CTempBuffer class takes template arguments that specify the type of data for the memory buffer, the size of the initial memory buffer, and the type of allocator to use. If the memory required exceeds that specified by the template argument, CTempBuffer will allocate from the heap. For example, if you re reasonably certain that the data you want to put into a BYTE buffer is less than 128 bytes, you can declare an object of type CTempBuffer < BYTE, 128, CCRTAllocator > . In fact, in this situation, the object can be declared just as CTempBuffer < BYTE > because the default template parameters for the size and allocator type are 128 and CCRTAllocator , respectively. The type of allocator to use depends on your code, but in general it s fine to leave the default allocator in place. If there s a particular reason you need to use the GlobalAlloc or LocalAlloc functions, you can use the CGlobalAllocator or CLocalAllocator classes, respectively.

For stack-based allocations, the _alloca function is also an option. However, the _alloca function should be used only with small allocations, never in a loop, and never in a recursive function. If there isn t enough stack space for _alloca to perform the allocation, a stack overflow exception will result. In general, it s preferable to use the CFixedStringT or CTempBuffer class for stack-based buffers.

Locks

Locks represent another performance killer for server-side applications. Any kind of per-request synchronization will negatively affect performance and should be avoided whenever possible. However, there are times when the cost of synchronization is worth the benefit derived from the object being synchronized. For example, synchronizing on cache is worth it if the cost of creating/retrieving the cached data on every request is prohibitively expensive. ATL Server uses several caches internally that, although they take a performance hit on synchronization, improve the overall performance of the framework. Examples include the stencil cache, the DLL cache, and the file cache.

When locks are necessary, such as in the ATL Server cache scenarios, it s important to minimize their overhead. You should keep a few important considerations in mind. First, don t spend too much time in the lock. The longer a thread holds onto a lock, the longer other threads are waiting for that lock. The ATL Server caches, for example, do as much work as possible outside the lock and take the lock only when absolutely necessary. Another way to minimize the cost of synchronization is to use reader/writer locks. Unfortunately, neither the Win32 API nor ATL/MFC provide a built-in reader/writer lock. There are many open -source implementations of reader/writer locks for Win32 available, however.

Even though there are many ways to minimize the cost of locks, it s best to avoid their use altogether. Using per-thread services, for example, eliminates the need for synchronization on those services. For examples of per-thread services, see Chapter 12.

I/O

Another important consideration to keep in mind when you write server-side applications is that the same APIs that work fine on the client won t necessarily behave well on the server. It s important to be aware of what APIs you re calling and their cost. There are some APIs that you should avoid altogether.

When you write code in C++, especially server-side code, it s also important to keep in mind the hidden costs involved in C++. Though these hidden costs rarely have a significant effect on performance, it s still important to keep in mind what the code is doing under the covers. In particular, it s important to pay attention to what functions are being called when you re using overloaded operators. A simple += operation may actually be extremely expensive.

It s also important to be aware of what constructors and cast operators are implicitly invoked. For example, CString will do implicit casts, conversions, and even create temporary CString s when assigning CString s of different types or when assigning a character buffer of a different type to a CString . For example,

 CStringW wstr = "ASCII string"; 

will construct a temporary CString for the ASCII string before it makes the assignment ”a very expensive operation.

Though in general it s important to keep such considerations in mind when you write the code, using the _ATL_CSTRING_EXPLICIT_CONSTRUCTORS preprocessor directive before including any ATL files will ensure no temporaries are constructed for situations like those in the preceding code snippet. In general, however, you should always be aware of what the code you write is doing under the covers to avoid any unforeseen performance problems.

Finally, any use of C++ exceptions should be limited to truly exceptional situations, such as out-of-memory errors and uncommon errors. Any errors that are expected or are likely to occur in any given request should be handled through the normal error-handling procedures because C++ exceptions are too expensive in dealing with common errors.

In general, the most important thing is to be aware of what the code you write is doing and never assume that some function will have good performance simply because it comes from ATL, the C runtime (CRT), or the Win32 API.




ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net