mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
Refactor the sqlite3PcacheFetch() routine into three separate routines,
which are significantly faster overall and about 100 bytes smaller in size as well. FossilOrigin-Name: bdb6e4978d1a26d5f795262172605184264ede9c
This commit is contained in:
191
src/pcache.c
191
src/pcache.c
@@ -231,15 +231,33 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
||||
|
||||
/*
|
||||
** Try to obtain a page from the cache.
|
||||
**
|
||||
** This routine returns a pointer to an sqlite3_pcache_page object if
|
||||
** such an object is already in cache, or if a new one is created.
|
||||
** This routine returns a NULL pointer if the object was not in cache
|
||||
** and could not be created.
|
||||
**
|
||||
** The createFlags should be 0 to check for existing pages and should
|
||||
** be 3 (not 1, but 3) to try to create a new page.
|
||||
**
|
||||
** If the createFlag is 0, then NULL is always returned if the page
|
||||
** is not already in the cache. If createFlag is 1, then a new page
|
||||
** is created only if that can be done without spilling dirty pages
|
||||
** and without exceeding the cache size limit.
|
||||
**
|
||||
** The caller needs to invoke sqlite3PcacheFetchFinish() to properly
|
||||
** initialize the sqlite3_pcache_page object and convert it into a
|
||||
** PgHdr object. The sqlite3PcacheFetch() and sqlite3PcacheFetchFinish()
|
||||
** routines are split this way for performance reasons. When separated
|
||||
** they can both (usually) operate without having to push values to
|
||||
** the stack on entry and pop them back off on exit, which saves a
|
||||
** lot of pushing and popping.
|
||||
*/
|
||||
int sqlite3PcacheFetch(
|
||||
sqlite3_pcache_page *sqlite3PcacheFetch(
|
||||
PCache *pCache, /* Obtain the page from this cache */
|
||||
Pgno pgno, /* Page number to obtain */
|
||||
int createFlag, /* If true, create page if it does not exist already */
|
||||
PgHdr **ppPage /* Write the page here */
|
||||
int createFlag /* If true, create page if it does not exist already */
|
||||
){
|
||||
sqlite3_pcache_page *pPage;
|
||||
PgHdr *pPgHdr = 0;
|
||||
int eCreate;
|
||||
|
||||
assert( pCache!=0 );
|
||||
@@ -258,69 +276,116 @@ int sqlite3PcacheFetch(
|
||||
assert( eCreate==0 || eCreate==1 || eCreate==2 );
|
||||
assert( createFlag==0 || pCache->eCreate==eCreate );
|
||||
assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
if( !pPage && eCreate==1 ){
|
||||
PgHdr *pPg;
|
||||
return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
}
|
||||
|
||||
/* Find a dirty page to write-out and recycle. First try to find a
|
||||
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
||||
** cleared), but if that is not possible settle for any other
|
||||
** unreferenced dirty page.
|
||||
*/
|
||||
expensive_assert( pcacheCheckSynced(pCache) );
|
||||
for(pPg=pCache->pSynced;
|
||||
pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
|
||||
pPg=pPg->pDirtyPrev
|
||||
);
|
||||
pCache->pSynced = pPg;
|
||||
if( !pPg ){
|
||||
for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
|
||||
}
|
||||
if( pPg ){
|
||||
int rc;
|
||||
/*
|
||||
** If the sqlite3PcacheFetch() routine is unable to allocate a new
|
||||
** page because new clean pages are available for reuse and the cache
|
||||
** size limit has been reached, then this routine can be invoked to
|
||||
** try harder to allocate a page. This routine might invoke the stress
|
||||
** callback to spill dirty pages to the journal. It will then try to
|
||||
** allocate the new page and will only fail to allocate a new page on
|
||||
** an OOM error.
|
||||
**
|
||||
** This routine should be invoked only after sqlite3PcacheFetch() fails.
|
||||
*/
|
||||
int sqlite3PcacheFetchStress(
|
||||
PCache *pCache, /* Obtain the page from this cache */
|
||||
Pgno pgno, /* Page number to obtain */
|
||||
sqlite3_pcache_page **ppPage /* Write result here */
|
||||
){
|
||||
PgHdr *pPg;
|
||||
if( pCache->eCreate==2 ) return 0;
|
||||
|
||||
|
||||
/* Find a dirty page to write-out and recycle. First try to find a
|
||||
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
||||
** cleared), but if that is not possible settle for any other
|
||||
** unreferenced dirty page.
|
||||
*/
|
||||
expensive_assert( pcacheCheckSynced(pCache) );
|
||||
for(pPg=pCache->pSynced;
|
||||
pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
|
||||
pPg=pPg->pDirtyPrev
|
||||
);
|
||||
pCache->pSynced = pPg;
|
||||
if( !pPg ){
|
||||
for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
|
||||
}
|
||||
if( pPg ){
|
||||
int rc;
|
||||
#ifdef SQLITE_LOG_CACHE_SPILL
|
||||
sqlite3_log(SQLITE_FULL,
|
||||
"spill page %d making room for %d - cache used: %d/%d",
|
||||
pPg->pgno, pgno,
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
numberOfCachePages(pCache));
|
||||
sqlite3_log(SQLITE_FULL,
|
||||
"spill page %d making room for %d - cache used: %d/%d",
|
||||
pPg->pgno, pgno,
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
numberOfCachePages(pCache));
|
||||
#endif
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
|
||||
}
|
||||
|
||||
if( pPage ){
|
||||
pPgHdr = (PgHdr *)pPage->pExtra;
|
||||
|
||||
if( !pPgHdr->pPage ){
|
||||
memset(pPgHdr, 0, sizeof(PgHdr));
|
||||
pPgHdr->pPage = pPage;
|
||||
pPgHdr->pData = pPage->pBuf;
|
||||
pPgHdr->pExtra = (void *)&pPgHdr[1];
|
||||
memset(pPgHdr->pExtra, 0, pCache->szExtra);
|
||||
pPgHdr->pCache = pCache;
|
||||
pPgHdr->pgno = pgno;
|
||||
}
|
||||
assert( pPgHdr->pCache==pCache );
|
||||
assert( pPgHdr->pgno==pgno );
|
||||
assert( pPgHdr->pData==pPage->pBuf );
|
||||
assert( pPgHdr->pExtra==(void *)&pPgHdr[1] );
|
||||
|
||||
if( 0==pPgHdr->nRef ){
|
||||
pCache->nRef++;
|
||||
}
|
||||
pPgHdr->nRef++;
|
||||
if( pgno==1 ){
|
||||
pCache->pPage1 = pPgHdr;
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
*ppPage = pPgHdr;
|
||||
return (pPgHdr==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
|
||||
*ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
|
||||
return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a helper routine for sqlite3PcacheFetchFinish()
|
||||
**
|
||||
** In the uncommon case where the page being fetched has not been
|
||||
** initialized, this routine is invoked to do the initialization.
|
||||
** This routine is broken out into a separate function since it
|
||||
** requires extra stack manipulation that can be avoided in the common
|
||||
** case.
|
||||
*/
|
||||
static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
|
||||
PCache *pCache, /* Obtain the page from this cache */
|
||||
Pgno pgno, /* Page number obtained */
|
||||
sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
|
||||
){
|
||||
PgHdr *pPgHdr;
|
||||
assert( pPage!=0 );
|
||||
pPgHdr = (PgHdr*)pPage->pExtra;
|
||||
assert( pPgHdr->pPage==0 );
|
||||
memset(pPgHdr, 0, sizeof(PgHdr));
|
||||
pPgHdr->pPage = pPage;
|
||||
pPgHdr->pData = pPage->pBuf;
|
||||
pPgHdr->pExtra = (void *)&pPgHdr[1];
|
||||
memset(pPgHdr->pExtra, 0, pCache->szExtra);
|
||||
pPgHdr->pCache = pCache;
|
||||
pPgHdr->pgno = pgno;
|
||||
return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine converts the sqlite3_pcache_page object returned by
|
||||
** sqlite3PcacheFetch() into an initialized PgHdr object. This routine
|
||||
** must be called after sqlite3PcacheFetch() in order to get a usable
|
||||
** result.
|
||||
*/
|
||||
PgHdr *sqlite3PcacheFetchFinish(
|
||||
PCache *pCache, /* Obtain the page from this cache */
|
||||
Pgno pgno, /* Page number obtained */
|
||||
sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
|
||||
){
|
||||
PgHdr *pPgHdr;
|
||||
|
||||
if( pPage==0 ) return 0;
|
||||
pPgHdr = (PgHdr *)pPage->pExtra;
|
||||
|
||||
if( !pPgHdr->pPage ){
|
||||
return pcacheFetchFinishWithInit(pCache, pgno, pPage);
|
||||
}
|
||||
if( 0==pPgHdr->nRef ){
|
||||
pCache->nRef++;
|
||||
}
|
||||
pPgHdr->nRef++;
|
||||
if( pgno==1 ){
|
||||
pCache->pPage1 = pPgHdr;
|
||||
}
|
||||
return pPgHdr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user