Designing High-Performance ISAPI Applications
ISAPI is the highest-performance interface for Web applications. If you create an ISAPI extension or filter, chances are that it can outperform scripts in ASP pages or even components performing similar tasks. However, the inherent speed of the ISAPI interface does not mean that you can ignore performance and scalability considerations. ISAPI cannot utilize much of the application support services provided by ASP and COM. If you want your ISAPI application to maintain session state, for instance, you need to implement much of that session-state functionality.
The following are suggestions to improve the scalability and performance of your ISAPI extensions:
- Avoid ISAPI filters-Avoid ISAPI filters unless adding an ISAPI filter is absolutely necessary to your application architecture. You should especially avoid filters that perform processing on raw incoming or outgoing data. If you determine that a filter is absolutely necessary, be sure to carefully optimize the main code paths through the filter event notification code.
- Create your own worker thread pool-You should create your own worker thread pool so that the main I/O threads are free to accomplish other tasks. This option is only available for ISAPI extensions.
- Consider using asynchronous operations and I/O completion ports-IIS supports asynchronous reading and writing by using the I/O completion ports, available in Windows NT 4.0 and Windows 2000 or later. Depending on the type of I/O operations being performed, asynchronous operations can make better use of the CPU time available. Asynchronous operations also work particularly well when implemented using a worker thread pool.
- Use the Win32 TransmitFile function-When sending an HTML or image file, ISAPI extensions should use the Win32 TransmitFile function, which is exposed by the HSE_REQ_TRANSMIT_FILE ServerSupportFunction.
- Use Connection: Keep-Alive headers-In most cases keeping persistent HTTP connections provides better performance than using non-persistent connections.
- Minimize need for thread synchronization-By maintaining session state information with the request context, you can minimize the need for thread synchronization. If thread synchronization is required, make sure that critical sections are kept short.
- Consider other heap alternatives-If your ISAPI application uses the heap intensively, you should consider other heap alternatives. Intensive use of the Windows heap can cause resource contention. Several memory allocation alternatives are worth exploring, including:
- Heap Partitioning-Create multiple custom heaps, one for each thread, in addition to the default process heap. A separate, non-global lock controls each custom heap and lock contention is reduced.
- Cached Allocation-Use custom allocation operations that operate at a middle layer between the object users and the heap. Calls to the Win32 heap are made infrequently and only for large memory blocks. These blocks are then subdivided and managed by the custom allocator.
- Stack Allocation-Use the C run-time function _alloca to allocate memory for your objects on the stack instead of the heap. This method is feasible only for relatively small objects because the space available on the stack is limited. In addition, your newly allocated object is only available within the current functions, or functions called by that function. Once the current function returns to the main calling program, the storage allocated on the stack is lost.
- Object Encapsulation-Include a buffer as a member data structure of a class. Use this buffer for tasks that require accesses to the Win32 heap.
- Avoid using global locks within your ISAPI-Global locks always adversely affect scalability.