diff --git a/manifest b/manifest index 1a7976cec9..bca29d167e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Disentangle\svariable\suse\sin\slast\scheckin -D 2022-03-07T00:14:52.499 +C Optimizations\sto\ssqlite3BtreeIndexMoveto()\savoid\sunnecessary\scomparisons\sif\nthe\scursor\sis\salready\snear\sthe\send\sof\sthe\stable\sand\sis\snot\smoving\sfar.\s\sThis\ncase\sis\smore\scommon\sthat\syou\swould\sexpect.\s\sThe\soptimization\ssaves\salmost\n4\smillion\sCPU\scycles. +D 2022-03-07T01:29:36.578 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -492,7 +492,7 @@ F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7 F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 -F src/btree.c 3a925b8542eb7f2a00c1919d6dbcd252b1d2781fbc43228b33dcc03fe6cd772a +F src/btree.c dff405cefec5a5573ca9254a6fdefcadf64fa884b575cc15cd4bb23c13b35516 F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22 F src/btreeInt.h 1ca477727c5f420a8321208dc5b14d93cb46cec8f941bc49318feb0e00bc961f F src/build.c 9891c2160886cf7e344d7e8f1f7177f9612916c7c67ffeacd64cb34a92d387a8 @@ -1944,8 +1944,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bf9d1278846dce9255f9a11ddfc5dfac1acea2eadcb20816a19d59f7bccaec0f -R 496fa8a876df6daf353669fc02e2fc4c -U larrybr -Z 013bf70ae42a0fa3c911efc40e5d2bba +P 4c3a02600f10926da1f88ddbd457bb1486e6e02dee366b5cfc89e498a10daa6f +R d2948dadaedd31df84454fee7cf7f673 +U drh +Z 0ebffd417ef0adafd19b87cdd03ffdc7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 209490bd9e..5424375871 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4c3a02600f10926da1f88ddbd457bb1486e6e02dee366b5cfc89e498a10daa6f \ No newline at end of file +0057bbb508e7662b0da19e981c07ef10236cb616bda952745de3aa2d1c286289 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index ea31738c7d..0acfad38c0 100644 --- a/src/btree.c +++ b/src/btree.c @@ -5698,6 +5698,69 @@ moveto_table_finish: return rc; } +/* +** Compare the "idx"-th cell on the page the cursor pCur is currently +** pointing to to pIdxKey using xRecordCompare. Return negative or +** zero if the cell is less than or equal pIdxKey. Return positive +** if unknown. +** +** Return value negative: Cell at pCur[idx] less than pIdxKey +** +** Return value is zero: Cell at pCur[idx] equals pIdxKey +** +** Return value positive: Nothing is known about the relationship +** of the cell at pCur[idx] and pIdxKey. +** +** This routine is part of an optimization. It is always safe to return +** a positive value as that will cause the optimization to be skipped. +*/ +static int indexCellCompare( + BtCursor *pCur, + int idx, + UnpackedRecord *pIdxKey, + RecordCompare xRecordCompare +){ + MemPage *pPage = pCur->pPage; + int c; + int nCell; /* Size of the pCell cell in bytes */ + u8 *pCell = findCellPastPtr(pPage, idx); + + nCell = pCell[0]; + if( nCell<=pPage->max1bytePayload ){ + /* This branch runs if the record-size field of the cell is a + ** single byte varint and the record fits entirely on the main + ** b-tree page. */ + testcase( pCell+nCell+1==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + }else if( !(pCell[1] & 0x80) + && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + ){ + /* The record-size field is a 2 byte varint and the record + ** fits entirely on the main b-tree page. */ + testcase( pCell+nCell+2==pPage->aDataEnd ); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + }else{ + /* If the record extends into overflow pages, do not attempt + ** the optimization. */ + c = 99; + } + return c; +} + +/* +** Return true (non-zero) if pCur is current pointing to the last +** page of a table. +*/ +static int cursorOnLastPage(BtCursor *pCur){ + int i; + assert( pCur->eState==CURSOR_VALID ); + for(i=0; iiPage; i++){ + MemPage *pPage = pCur->apPage[i]; + if( pCur->aiIdx[i]nCell ) return 0; + } + return 1; +} + /* Move the cursor so that it points to an entry in an index table ** near the key pIdxKey. Return a success code. ** @@ -5748,6 +5811,36 @@ int sqlite3BtreeIndexMoveto( || pIdxKey->default_rc==-1 ); + + /* Check to see if we can skip a lot of work. Two cases: + ** + ** (1) If the cursor is already pointing to the very last cell + ** in the table and the pIdxKey search key is greater than or + ** equal to that last cell, then no movement is required. + ** + ** (2) If the cursor is on the last page of the table and the first + ** cell on that last page is less than or equal to the pIdxKey + ** search key, then we can start the search on the current page + ** without needing to go back to root. + */ + if( pCur->eState==CURSOR_VALID + && pCur->pPage->leaf + && cursorOnLastPage(pCur) + ){ + int c; + if( pCur->ix==pCur->pPage->nCell-1 + && (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0 + ){ + *pRes = c; + return SQLITE_OK; /* Cursor already pointing at the correct spot */ + } + if( pCur->iPage>0 + && (c = indexCellCompare(pCur, 0, pIdxKey, xRecordCompare))<=0 + ){ + goto bypass_moveto_root; /* Start search on the current page */ + } + } + rc = moveToRoot(pCur); if( rc ){ if( rc==SQLITE_EMPTY ){ @@ -5757,12 +5850,14 @@ int sqlite3BtreeIndexMoveto( } return rc; } + +bypass_moveto_root: assert( pCur->pPage ); assert( pCur->pPage->isInit ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->pPage->nCell > 0 ); - assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey ); - assert( pCur->curIntKey || pIdxKey ); + assert( pCur->curIntKey==0 ); + assert( pIdxKey!=0 ); for(;;){ int lwr, upr, idx, c; Pgno chldPg; @@ -5776,7 +5871,7 @@ int sqlite3BtreeIndexMoveto( ** be the right kind (index or table) of b-tree page. Otherwise ** a moveToChild() or moveToRoot() call would have detected corruption. */ assert( pPage->nCell>0 ); - assert( pPage->intKey==(pIdxKey==0) ); + assert( pPage->intKey==0 ); lwr = 0; upr = pPage->nCell-1; idx = upr>>1; /* idx = (lwr+upr)/2; */