1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Optimizations to sqlite3BtreeIndexMoveto() avoid unnecessary comparisons if

the cursor is already near the end of the table and is not moving far.  This
case is more common that you would expect.  The optimization saves almost
4 million CPU cycles.

FossilOrigin-Name: 0057bbb508e7662b0da19e981c07ef10236cb616bda952745de3aa2d1c286289
This commit is contained in:
drh
2022-03-07 01:29:36 +00:00
parent 41f46703b8
commit c5a55db75d
3 changed files with 106 additions and 11 deletions

View File

@@ -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.

View File

@@ -1 +1 @@
4c3a02600f10926da1f88ddbd457bb1486e6e02dee366b5cfc89e498a10daa6f
0057bbb508e7662b0da19e981c07ef10236cb616bda952745de3aa2d1c286289

View File

@@ -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; i<pCur->iPage; i++){
MemPage *pPage = pCur->apPage[i];
if( pCur->aiIdx[i]<pPage->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; */