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 yourself.

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, or do the main processing on requests. In other words, filters that intercept requests in order to build complex responses should be avoided. In general, the only response that a filter should ever send are error responses. For example, an authentication filter might send a 401 response back to the client. 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 - IIS supports asynchronous reading and writing. 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 server support function.

  • Use the VectorSend function - IIS 6.0 Supports VectorSend, which allows an ISAPI extension to specify a vector of buffers and file handles for sending responses to requests for data stored in more than one file or buffer. VectorSend is exposed by the HSE_REQ_VECTOR_SEND server support function.

  • 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.