/*++ Copyright (c) 1997 Microsoft Corporation Module Name : dpte.hxx Abstract: This module determines the size of transfer chunks to be used when calling TransmitFile() Author: Bilal Alam ( t-bilala ) 9-April-1997 Environment: User Mode -- Win32 Project: Internet Services Asynchronous Thread Queue DLL --*/ #ifndef DPTE_HXX_INCLUDED #define DPTE_HXX_INCLUDED #ifndef PAGE_SIZE #define MINIMUM_CHUNK_SIZE 4096 #else #define MINIMUM_CHUNK_SIZE PAGE_SIZE #endif #define DEFAULT_CHUNK_SIZE 65536 #define ROUND_CHUNK_SIZE( cb ) ( (((cb) + 512)/1024) * 1024) /* ChunkAlgorithms The goal is to improve upon the existing behaviour where TransmitFile() calls set the nNumberOfBytesPerSend = 0. This (for NT server) causes 64K to be used for each TransmitFile() and this is problematic for slow links since huge amounts of memory are kept in use. To address this problem, a ChunkAlgorithm is used to dynamically set the chunk size for the TransmitFile() call. The policy for each algorithm determines when to decrease the chunk size and when to increase it again. */ /* Plugging this stuff in - In the registry, algorithm number and parameter are set (default case is DefaultChunkAlgorithm,0 ) - Construct appropriate chunk algorithm (global to ATQ) - In the ATQ timeout call, call Evaluate() method of chunk algorithm - Before doing TransmitFile() call QueryChunkSize() to get chunk size to pass into TransmitFile() - For SPUDTransmitFileAndRecv(), set the AFD_TRANSMIT_FILE_INFO.SendPacketLength member appropriately. - Store the chunk size in the ATQ context so that when the TransmitFile() completes, you can appropriately update the statistics (if necessary) through the DecreaseUsage() method. */ /* ChunkAlgorithm is base class from which all chunksize algorithms are derived. */ class ChunkAlgorithm { public: ChunkAlgorithm( DWORD dwSize = DEFAULT_CHUNK_SIZE ) : _cbChunkSize( dwSize ) { } virtual ~ChunkAlgorithm( VOID ) { } // SetParameter() // // Used to set parameters for use by the chunk algorithms. virtual BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue ) = 0; // Evaluate() // // This is called every ATQ_TIMEOUT to re-evaluate the chunk size and if // necessary, change the size for subsequent requests. virtual VOID Evaluate( VOID ) = 0; // IncreaseUsage() // // When a TransmitFile() call is about to occur, ATQ will call this // method to update the statistic maintaining current PTE usage. For // algorithms that don't keep statistics, the virtual function will be // a NOP. virtual VOID IncreaseUsage( DWORD cbUsage ) = 0; // DecreaseUsage() // // When a TransmitFile() call has completed, ATQ will call this // method to update the statistic maintaining current PTE usage. For // algorithms that don't keep statistics, the virtual function will be // a NOP. virtual VOID DecreaseUsage( DWORD cbUsage ) = 0; // QueryChunkSize() // // Before calling TransmitFile() this function will be called to get // the chunk size to be passed into TransmitFile(). DWORD QueryChunkSize( VOID ) { return _cbChunkSize; } protected: DWORD _cbChunkSize; }; /* DefaultAlgorithm sets the chunksize for TransmitFile() to be a constant. By default, this is the algorithm used with the fixed value being 0. (this default case is the original behaviour of ATQ). The fixed value can be non-zero in which case all TransmitFile() call will use this value as the chunk size. */ class DefaultAlgorithm : public ChunkAlgorithm { public: DefaultAlgorithm( VOID ) : ChunkAlgorithm( 0 ) { } ~DefaultAlgorithm( VOID ) { } BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue ) { ATQ_ASSERT( dwParmNumber == 1 ); if ( dwParmNumber == 1 ) { _cbChunkSize = dwValue; return TRUE; } else { return FALSE; } } VOID IncreaseUsage( DWORD cbUsage ) { } VOID DecreaseUsage( DWORD cbUsage ) { } VOID Evaluate( VOID ) { } }; /* DivideAlgorithm keeps statistics on the current amount of PTE usage and compares this to a given threshold value. The closer the amount gets to the threshold, the lower the chunk size becomes. Beware of this algorithm since it actually takes statistics and thus significantly can slow down the mainline code path for TransmitFile(). */ class DivideAlgorithm : public ChunkAlgorithm { public: DivideAlgorithm( VOID ) { } ~DivideAlgorithm( VOID ) { } BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue ) { ATQ_ASSERT( dwParmNumber == 1 ); if ( dwParmNumber == 1 ) { _cbThreshold = dwValue; return TRUE; } else { return FALSE; } } VOID Evaluate( VOID ) { DWORD cbCurrentUsage = _cbCurrentUsage; DWORD cbFree = _cbThreshold - cbCurrentUsage; if ( _cbThreshold > cbCurrentUsage ) { _cbChunkSize = ROUND_CHUNK_SIZE( DEFAULT_CHUNK_SIZE / ( _cbThreshold / cbFree ) ); if ( _cbChunkSize < MINIMUM_CHUNK_SIZE ) { _cbChunkSize = MINIMUM_CHUNK_SIZE; } } else { _cbChunkSize = MINIMUM_CHUNK_SIZE; } } VOID IncreaseUsage( DWORD cbUsage ) { InterlockedExchangeAdd( (LPLONG) &_cbCurrentUsage, (LONG) cbUsage ); } VOID DecreaseUsage( DWORD cbUsage ) { InterlockedExchangeAdd( (LPLONG) &_cbCurrentUsage, ( (LONG) cbUsage ) * -1 ); } private: DWORD _cbCurrentUsage; DWORD _cbThreshold; }; /* MemoryLoadAlgorithm uses the GlobalMemoryStatus() function to determine what the chunk size should be for TransmitFile(). This API call will return (amongst other things) a load value from 0-100. This value is used to set the chunk size. */ class MemoryLoadAlgorithm : public ChunkAlgorithm { public: MemoryLoadAlgorithm( VOID ) { rChunkSizeTable[ 0 ] = DEFAULT_CHUNK_SIZE; rChunkSizeTable[ 1 ] = DEFAULT_CHUNK_SIZE; rChunkSizeTable[ 2 ] = DEFAULT_CHUNK_SIZE; rChunkSizeTable[ 3 ] = DEFAULT_CHUNK_SIZE; rChunkSizeTable[ 4 ] = DEFAULT_CHUNK_SIZE >> 1; rChunkSizeTable[ 5 ] = DEFAULT_CHUNK_SIZE >> 2; rChunkSizeTable[ 6 ] = DEFAULT_CHUNK_SIZE >> 3; rChunkSizeTable[ 7 ] = DEFAULT_CHUNK_SIZE >> 4; rChunkSizeTable[ 8 ] = MINIMUM_CHUNK_SIZE; rChunkSizeTable[ 9 ] = MINIMUM_CHUNK_SIZE; rChunkSizeTable[ 10] = MINIMUM_CHUNK_SIZE; } ~MemoryLoadAlgorithm( VOID ) { } BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue ) { return TRUE; } VOID Evaluate( VOID ) { MEMORYSTATUS memStatus; GlobalMemoryStatus( &memStatus ); InterlockedExchange( (LPLONG) &_cbChunkSize, rChunkSizeTable[ memStatus.dwMemoryLoad / 10 ] ); } VOID IncreaseUsage( DWORD cbUsage ) { } VOID DecreaseUsage( DWORD cbUsage ) { } private: DWORD rChunkSizeTable[ 11 ]; }; #endif