]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Optimizations to sqlite3BtreeIndexMoveto() avoid unnecessary comparisons if
authordrh <>
Mon, 7 Mar 2022 01:29:36 +0000 (01:29 +0000)
committerdrh <>
Mon, 7 Mar 2022 01:29:36 +0000 (01:29 +0000)
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

manifest
manifest.uuid
src/btree.c

index 1a7976cec9eac762e3a900463bea9cee2559a3aa..bca29d167ee737dbee67b60c980b6f10704b9b9c 100644 (file)
--- 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.
index 209490bd9eef88225de87c690d65892b0635942e..5424375871c44bdb8079ba64c9e59a78e48b5b6d 100644 (file)
@@ -1 +1 @@
-4c3a02600f10926da1f88ddbd457bb1486e6e02dee366b5cfc89e498a10daa6f
\ No newline at end of file
+0057bbb508e7662b0da19e981c07ef10236cb616bda952745de3aa2d1c286289
\ No newline at end of file
index ea31738c7d4facd9f82d53b82f3d912ba59d1fbc..0acfad38c054f79542d9e453a1808c958b0d3fd2 100644 (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; */