diff --git a/manifest b/manifest index 550ac32afc..b0e3068ebe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Try\sto\sbetter\sdetect\swhen\sthe\slibrary\sis\scompiled\sfor\slarge\sfile\ssupport\s(LFS)\nbut\sthe\ssupport\sis\snot\savailable\sin\sthe\shost\sOS\skernel.\s(CVS\s782) -D 2002-11-09T00:33:16 +C Two\soptimizations\sto\sthe\spager:\s(1)\sWrite\sdirty\spages\sback\sto\sthe\sdatabase\nfile\sin\sorder\sand\s(2)\sKeep\sa\sseparate\slist\sof\sin-memory\spages\sthat\sare\sin\nthe\scheckpoint\sjournal\sin\sorder\sto\sspeed\sa\scheckpoint\scommit.\s(CVS\s783) +D 2002-11-10T23:32:57 F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -32,7 +32,7 @@ F src/main.c f04f93b8928d6d85976e5137fea146a46de1fd6e F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 F src/os.c caf5a34b35a2d99a58457517261c879ac29b0a05 F src/os.h 1caaea972d1c0401cfe6300aba51fb0f6fe435c9 -F src/pager.c 292853d08658df23f1044fba1a793a210475964e +F src/pager.c 27d9b94641e968bafabfd2119d9ba4304ccb69a4 F src/pager.h 6991c9c2dc5e4c7f2df4d4ba47d1c6458f763a32 F src/parse.y 469c9636ff713e63c00234662209f11668671ae9 F src/printf.c 5c50fc1da75c8f5bf432b1ad17d91d6653acd167 @@ -149,7 +149,7 @@ F www/speed.tcl a20a792738475b68756ea7a19321600f23d1d803 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 2008b56fe11e49d52e28f47d14ccd70504e6c094 -R 4ac4711a79ff9ee10d8741795898da69 +P a29d60ecc5ee3f535142a81f56eecbef7875ef22 +R 52d1e18697b72d70d44fdac37bed7445 U drh -Z c8abc0129b7b7c57014c7437e465381e +Z 7623b80b72f443af5ce24d861a13e847 diff --git a/manifest.uuid b/manifest.uuid index 7e4e87e113..cb446ee9a4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a29d60ecc5ee3f535142a81f56eecbef7875ef22 \ No newline at end of file +a6ef6657a4377684dc2fce7be2bbf009fd2d2f37 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index aac9353fcf..04c7f25a37 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.56 2002/11/09 00:33:16 drh Exp $ +** @(#) $Id: pager.c,v 1.57 2002/11/10 23:32:57 drh Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" @@ -74,6 +74,8 @@ struct PgHdr { int nRef; /* Number of users of this page */ PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */ PgHdr *pNextAll, *pPrevAll; /* A list of all pages */ + PgHdr *pNextCkpt, *pPrevCkpt; /* List of pages in the checkpoint journal */ + PgHdr *pSort; /* Next in list of pages to be written */ u8 inJournal; /* TRUE if has been written to journal */ u8 inCkpt; /* TRUE if written to the checkpoint journal */ u8 dirty; /* TRUE if we need to write back changes */ @@ -130,6 +132,7 @@ struct Pager { u8 *aInCkpt; /* One bit for each page in the database */ PgHdr *pFirst, *pLast; /* List of free pages */ PgHdr *pAll; /* List of all pages */ + PgHdr *pCkpt; /* List of pages in the checkpoint journal */ PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */ }; @@ -253,6 +256,45 @@ static int pager_errcode(Pager *pPager){ return rc; } +/* +** Add or remove a page from the list of all pages that are in the +** checkpoint journal. +** +** The Pager keeps a separate list of pages that are currently in +** the checkpoint journal. This helps the sqlitepager_ckpt_commit() +** routine run MUCH faster for the common case where there are many +** pages in memory but only a few are in the checkpoint journal. +*/ +static void page_add_to_ckpt_list(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + if( pPg->inCkpt ) return; + assert( pPg->pPrevCkpt==0 && pPg->pNextCkpt==0 ); + pPg->pPrevCkpt = 0; + if( pPager->pCkpt ){ + pPager->pCkpt->pPrevCkpt = pPg; + } + pPg->pNextCkpt = pPager->pCkpt; + pPager->pCkpt = pPg; + pPg->inCkpt = 1; +} +static void page_remove_from_ckpt_list(PgHdr *pPg){ + if( !pPg->inCkpt ) return; + if( pPg->pPrevCkpt ){ + assert( pPg->pPrevCkpt->pNextCkpt==pPg ); + pPg->pPrevCkpt->pNextCkpt = pPg->pNextCkpt; + }else{ + assert( pPg->pPager->pCkpt==pPg ); + pPg->pPager->pCkpt = pPg->pNextCkpt; + } + if( pPg->pNextCkpt ){ + assert( pPg->pNextCkpt->pPrevCkpt==pPg ); + pPg->pNextCkpt->pPrevCkpt = pPg->pPrevCkpt; + } + pPg->pNextCkpt = 0; + pPg->pPrevCkpt = 0; + pPg->inCkpt = 0; +} + /* ** Find a page in the hash table given its page number. Return ** a pointer to the page or NULL if not found. @@ -752,6 +794,41 @@ int sqlitepager_ref(void *pData){ return SQLITE_OK; } +/* +** The parameters are pointers to the head of two sorted lists +** of page headers. Merge these two lists together and return +** a single sorted list. This routine forms the core of the +** merge-sort algorithm that sorts dirty pages into accending +** order prior to writing them back to the disk. +** +** In the case of a tie, left sorts in front of right. +** +** Headers are sorted in order of ascending page number. +*/ +static PgHdr *page_merge(PgHdr *pLeft, PgHdr *pRight){ + PgHdr sHead; + PgHdr *pTail; + pTail = &sHead; + pTail->pSort = 0; + while( pLeft && pRight ){ + if( pLeft->pgno<=pRight->pgno ){ + pTail->pSort = pLeft; + pLeft = pLeft->pSort; + }else{ + pTail->pSort = pRight; + pRight = pRight->pSort; + } + pTail = pTail->pSort; + } + if( pLeft ){ + pTail->pSort = pLeft; + }else if( pRight ){ + pTail->pSort = pRight; + } + return sHead.pSort; +} + + /* ** Sync the journal and then write all free dirty pages to the database ** file. @@ -768,10 +845,24 @@ int sqlitepager_ref(void *pData){ ** If we are writing to temporary database, there is no need to preserve ** the integrity of the journal file, so we can save time and skip the ** fsync(). +** +** This routine goes to the extra trouble of sorting all the dirty +** pages by their page number prior to writing them. Tests show that +** writing pages in order by page number gives a modest speed improvement +** under Linux. */ static int syncAllPages(Pager *pPager){ PgHdr *pPg; + PgHdr *pToWrite; +# define NSORT 28 + Pgno lastPgno; + int i; + PgHdr *apSorter[NSORT]; int rc = SQLITE_OK; + + /* Sync the journal before modifying the main database + ** (assuming there is a journal and it needs to be synced.) + */ if( pPager->needSync ){ if( !pPager->tempFile ){ rc = sqliteOsSync(&pPager->jfd); @@ -779,14 +870,58 @@ static int syncAllPages(Pager *pPager){ } pPager->needSync = 0; } + + /* Create a list of all dirty pages + */ + pToWrite = 0; for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){ if( pPg->dirty ){ - sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE); - rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); - if( rc!=SQLITE_OK ) break; - pPg->dirty = 0; + pPg->pSort = pToWrite; + pToWrite = pPg; } } + + /* Sort the list of dirty pages into accending order by + ** page number + */ + for(i=0; ipSort; + pPg->pSort = 0; + for(i=0; i=NSORT-1 ){ + apSorter[NSORT-1] = page_merge(apSorter[NSORT-1],pPg); + } + } + pToWrite = 0; + for(i=0; ipSort){ + if( lastPgno==0 || pPg->pgno!=lastPgno-1 ){ + sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE); + } + rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); + if( rc!=SQLITE_OK ) break; + pPg->dirty = 0; + lastPgno = pPg->pgno; + } return rc; } @@ -984,10 +1119,11 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ }else{ pPg->inJournal = 0; } - if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize ){ - pPg->inCkpt = (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0; + if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize + && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){ + page_add_to_ckpt_list(pPg); }else{ - pPg->inCkpt = 0; + page_remove_from_ckpt_list(pPg); } pPg->dirty = 0; pPg->nRef = 1; @@ -1248,7 +1384,7 @@ int sqlitepager_write(void *pData){ pPg->inJournal = 1; if( pPager->ckptInUse ){ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->inCkpt = 1; + page_add_to_ckpt_list(pPg); } } @@ -1268,7 +1404,7 @@ int sqlitepager_write(void *pData){ } assert( pPager->aInCkpt!=0 ); pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->inCkpt = 1; + page_add_to_ckpt_list(pPg); } /* Update the database size and return. @@ -1352,14 +1488,14 @@ void sqlitepager_dont_rollback(void *pData){ pPg->inJournal = 1; if( pPager->ckptInUse ){ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->inCkpt = 1; + page_add_to_ckpt_list(pPg); } } if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); assert( pPager->aInCkpt!=0 ); pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->inCkpt = 1; + page_add_to_ckpt_list(pPg); } } @@ -1521,15 +1657,19 @@ ckpt_begin_failed: */ int sqlitepager_ckpt_commit(Pager *pPager){ if( pPager->ckptInUse ){ - PgHdr *pPg; + PgHdr *pPg, *pNext; sqliteOsSeek(&pPager->cpfd, 0); sqliteOsTruncate(&pPager->cpfd, 0); pPager->ckptInUse = 0; sqliteFree( pPager->aInCkpt ); pPager->aInCkpt = 0; - for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + for(pPg=pPager->pCkpt; pPg; pPg=pNext){ + pNext = pPg->pNextCkpt; + assert( pPg->inCkpt ); pPg->inCkpt = 0; + pPg->pPrevCkpt = pPg->pNextCkpt = 0; } + pPager->pCkpt = 0; } return SQLITE_OK; }