Index: src/pcache1.c ================================================================== --- src/pcache1.c +++ src/pcache1.c @@ -496,29 +496,59 @@ */ static void pcache1TruncateUnsafe( PCache1 *pCache, /* The cache to truncate */ unsigned int iLimit /* Drop pages with this pgno or larger */ ){ - TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */ - unsigned int h; + TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */ + unsigned int h, iStop; + START_DEBUG_TIMER; + int nFree = 0; assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); - for(h=0; hnHash; h++){ - PgHdr1 **pp = &pCache->apHash[h]; + assert( pCache->iMaxKey >= iLimit ); + assert( pCache->nHash > 0 ); + if( pCache->iMaxKey - iLimit < pCache->nHash/2 ){ + /* If we are just shaving the last few pages off the end of the + ** cache, then there is no point in scanning the entire hash table. + ** Only scan those hash slots that might contain pages that need to + ** be removed. */ + iStop = iLimit % pCache->nHash; + h = pCache->iMaxKey % pCache->nHash; + TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */ + }else{ + /* This is the general case where many pages are being removed. + ** It is necessary to scan the entire hash table */ + iStop = 0; + h = pCache->nHash - 1; + } + for(;;){ + PgHdr1 **pp; PgHdr1 *pPage; + assert( hnHash ); + pp = &pCache->apHash[h]; while( (pPage = *pp)!=0 ){ if( pPage->iKey>=iLimit ){ pCache->nPage--; + nFree++; *pp = pPage->pNext; if( !pPage->isPinned ) pcache1PinPage(pPage); pcache1FreePage(pPage); }else{ pp = &pPage->pNext; - TESTONLY( nPage++; ) + TESTONLY( if( nPage>=0 ) nPage++; ) } } + if( h==iStop ) break; + h = h ? h-1 : pCache->nHash - 1; } - assert( pCache->nPage==nPage ); + assert( nPage<0 || pCache->nPage==(unsigned)nPage ); + END_DEBUG_TIMER( DEBUG_TIMER_BIG_TIMEOUT ){ + sqlite3_log(SQLITE_NOTICE, + "slow pcache1TruncateUnsafe() %lld " + " nFree=%d nHash=%d nPage=%d iLimit=%d iMaxKey=%d", + iDebugTimer, + nFree, pCache->nHash, pCache->nPage, iLimit, pCache->iMaxKey); + } } /******************************************************************************/ /******** sqlite3_pcache Methods **********************************************/ @@ -943,11 +973,11 @@ static void pcache1Destroy(sqlite3_pcache *p){ PCache1 *pCache = (PCache1 *)p; PGroup *pGroup = pCache->pGroup; assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); pcache1EnterMutex(pGroup); - pcache1TruncateUnsafe(pCache, 0); + if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0); assert( pGroup->nMaxPage >= pCache->nMax ); pGroup->nMaxPage -= pCache->nMax; assert( pGroup->nMinPage >= pCache->nMin ); pGroup->nMinPage -= pCache->nMin; pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -197,11 +197,11 @@ ** It determines whether or not the features related to ** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can ** be overridden at runtime using the sqlite3_config() API. */ #if !defined(SQLITE_DEFAULT_MEMSTATUS) -# define SQLITE_DEFAULT_MEMSTATUS 1 +# define SQLITE_DEFAULT_MEMSTATUS 0 #endif /* ** Exactly one of the following macros must be defined in order to ** specify which memory allocation subsystem to use. @@ -3812,7 +3812,28 @@ */ #if SQLITE_MAX_WORKER_THREADS>0 int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*); int sqlite3ThreadJoin(SQLiteThread*, void**); #endif + +#include +#define START_DEBUG_TIMER \ + sqlite3_uint64 iDebugTimerStart, iDebugTimer; \ + struct timeval debug_timer_var; \ + gettimeofday(&debug_timer_var, 0); \ + iDebugTimerStart = 1000000*(sqlite3_uint64)debug_timer_var.tv_sec \ + + debug_timer_var.tv_usec; + +#define END_DEBUG_TIMER(nDebugUsec) \ + gettimeofday(&debug_timer_var, 0); \ + iDebugTimer = 1000000*(sqlite3_uint64)debug_timer_var.tv_sec \ + +debug_timer_var.tv_usec-iDebugTimerStart; \ + if( iDebugTimer>=nDebugUsec ) + +#ifndef DEBUG_TIMER_BIG_TIMEOUT +# define DEBUG_TIMER_BIG_TIMEOUT 10000 +#endif +#ifndef DEBUG_TIMER_SMALL_TIMEOUT +# define DEBUG_TIMER_SMALL_TIMEOUT 1000 +#endif #endif /* _SQLITEINT_H_ */