]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Avoid redundant string comparisons while merging fts5 segment b-trees.
authordan <dan@noemail.net>
Tue, 10 Mar 2015 19:24:30 +0000 (19:24 +0000)
committerdan <dan@noemail.net>
Tue, 10 Mar 2015 19:24:30 +0000 (19:24 +0000)
FossilOrigin-Name: 5c46820d9b4aae791a8704b69145bd81f1e6780d

ext/fts5/fts5_index.c
manifest
manifest.uuid

index 15b345da4fdf523291ceb028a87988d186ecb514..9fc9d2b7993c742f2904f33d9b7de9af071ef1d9 100644 (file)
@@ -410,12 +410,19 @@ struct Fts5SegWriter {
 ** aFirst[1] contains the index in aSeg[] of the iterator that points to
 ** the smallest key overall. aFirst[0] is unused. 
 */
+
+typedef struct Fts5CResult Fts5CResult;
+struct Fts5CResult {
+  u16 iFirst;                     /* aSeg[] index of firstest iterator */
+  u8 bTermEq;                     /* True if the terms are equal */
+};
+
 struct Fts5MultiSegIter {
   int nSeg;                       /* Size of aSeg[] array */
   int bRev;                       /* True to iterate in reverse order */
   int bSkipEmpty;                 /* True to skip deleted entries */
   Fts5SegIter *aSeg;              /* Array of segment iterators */
-  u16 *aFirst;                    /* Current merge state (see above) */
+  Fts5CResult *aFirst;            /* Current merge state (see above) */
 };
 
 /*
@@ -1744,8 +1751,10 @@ static int fts5SegIterIsDelete(
 */
 static void fts5SegIterNext(
   Fts5Index *p,                   /* FTS5 backend object */
-  Fts5SegIter *pIter              /* Iterator to advance */
+  Fts5SegIter *pIter,             /* Iterator to advance */
+  int *pbNewTerm                  /* OUT: Set for new term */
 ){
+  assert( pbNewTerm==0 || *pbNewTerm==0 );
   if( p->rc==SQLITE_OK ){
     if( pIter->flags & FTS5_SEGITER_REVERSE ){
       if( pIter->iRowidOffset>0 ){
@@ -1841,6 +1850,7 @@ static void fts5SegIterNext(
           pIter->pLeaf = 0;
         }else{
           fts5SegIterLoadTerm(p, pIter, nKeep);
+          if( pbNewTerm ) *pbNewTerm = 1;
         }
       }
     }
@@ -2033,7 +2043,7 @@ static void fts5SegIterSeekInit(
     do {
       res = fts5BufferCompareBlob(&pIter->term, pTerm, nTerm);
       if( res>=0 ) break;
-      fts5SegIterNext(p, pIter);
+      fts5SegIterNext(p, pIter, 0);
     }while( pIter->pLeaf && p->rc==SQLITE_OK );
 
     if( bGe==0 && res ){
@@ -2123,6 +2133,79 @@ static void fts5SegIterClear(Fts5SegIter *pIter){
   memset(pIter, 0, sizeof(Fts5SegIter));
 }
 
+#ifdef SQLITE_DEBUG
+
+/*
+** This function is used as part of the big assert() procedure implemented by
+** fts5AssertMultiIterSetup(). It ensures that the result currently stored
+** in *pRes is the correct result of comparing the current positions of the
+** two iterators.
+*/
+static void fts5AssertComparisonResult(
+  Fts5MultiSegIter *pIter, 
+  Fts5SegIter *p1,
+  Fts5SegIter *p2,
+  Fts5CResult *pRes
+){
+  int i1 = p1 - pIter->aSeg;
+  int i2 = p2 - pIter->aSeg;
+
+  if( p1->pLeaf || p2->pLeaf ){
+    if( p1->pLeaf==0 ){
+      assert( pRes->iFirst==i2 );
+    }else if( p2->pLeaf==0 ){
+      assert( pRes->iFirst==i1 );
+    }else{
+      int nMin = MIN(p1->term.n, p2->term.n);
+      int res = memcmp(p1->term.p, p2->term.p, nMin);
+      if( res==0 ) res = p1->term.n - p2->term.n;
+
+      if( res==0 ){
+        assert( pRes->bTermEq==1 );
+        assert( p1->iRowid!=p2->iRowid );
+        res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : 1;
+      }else{
+        assert( pRes->bTermEq==0 );
+      }
+
+      if( res<0 ){
+        assert( pRes->iFirst==i1 );
+      }else{
+        assert( pRes->iFirst==i2 );
+      }
+    }
+  }
+}
+
+/*
+** This function is a no-op unless SQLITE_DEBUG is defined when this module
+** is compiled. In that case, this function is essentially an assert() 
+** statement used to verify that the contents of the pIter->aFirst[] array
+** are correct.
+*/
+static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5MultiSegIter *pIter){
+  if( p->rc==SQLITE_OK ){
+    int i;
+    for(i=0; i<pIter->nSeg; i+=2){
+      Fts5SegIter *p1 = &pIter->aSeg[i];
+      Fts5SegIter *p2 = &pIter->aSeg[i+1];
+      Fts5CResult *pRes = &pIter->aFirst[(pIter->nSeg + i) / 2];
+      fts5AssertComparisonResult(pIter, p1, p2, pRes);
+    }
+
+    for(i=1; i<(pIter->nSeg / 2); i+=2){
+      Fts5CResult *pRes = &pIter->aFirst[i];
+      Fts5SegIter *p1 = &pIter->aSeg[ pIter->aFirst[i*2].iFirst ];
+      Fts5SegIter *p2 = &pIter->aSeg[ pIter->aFirst[i*2+1].iFirst ];
+
+      fts5AssertComparisonResult(pIter, p1, p2, pRes);
+    }
+  }
+}
+#else
+# define fts5AssertMultiIterSetup(x,y)
+#endif
+
 /*
 ** Do the comparison necessary to populate pIter->aFirst[iOut].
 **
@@ -2137,6 +2220,7 @@ static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){
   int iRes;
   Fts5SegIter *p1;                /* Left-hand Fts5SegIter */
   Fts5SegIter *p2;                /* Right-hand Fts5SegIter */
+  Fts5CResult *pRes = &pIter->aFirst[iOut];
 
   assert( iOut<pIter->nSeg && iOut>0 );
   assert( pIter->bRev==0 || pIter->bRev==1 );
@@ -2145,12 +2229,13 @@ static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){
     i1 = (iOut - pIter->nSeg/2) * 2;
     i2 = i1 + 1;
   }else{
-    i1 = pIter->aFirst[iOut*2];
-    i2 = pIter->aFirst[iOut*2+1];
+    i1 = pIter->aFirst[iOut*2].iFirst;
+    i2 = pIter->aFirst[iOut*2+1].iFirst;
   }
   p1 = &pIter->aSeg[i1];
   p2 = &pIter->aSeg[i2];
 
+  pRes->bTermEq = 0;
   if( p1->pLeaf==0 ){           /* If p1 is at EOF */
     iRes = i2;
   }else if( p2->pLeaf==0 ){     /* If p2 is at EOF */
@@ -2160,6 +2245,7 @@ static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){
     if( res==0 ){
       assert( i2>i1 );
       assert( i2!=0 );
+      pRes->bTermEq = 1;
       if( p1->iRowid==p2->iRowid ) return i2;
       res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
     }
@@ -2171,7 +2257,7 @@ static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){
     }
   }
 
-  pIter->aFirst[iOut] = iRes;
+  pRes->iFirst = iRes;
   return 0;
 }
 
@@ -2252,7 +2338,7 @@ static void fts5SegIterNextFrom(
   }
 
   while( 1 ){
-    if( bMove ) fts5SegIterNext(p, pIter);
+    if( bMove ) fts5SegIterNext(p, pIter, 0);
     if( pIter->pLeaf==0 ) break;
     if( bRev==0 && pIter->iRowid>=iMatch ) break;
     if( bRev!=0 && pIter->iRowid<=iMatch ) break;
@@ -2284,12 +2370,43 @@ static void fts5MultiIterAdvanced(
   for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){
     int iEq;
     if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){
-      fts5SegIterNext(p, &pIter->aSeg[iEq]);
+      fts5SegIterNext(p, &pIter->aSeg[iEq], 0);
       i = pIter->nSeg + iEq;
     }
   }
 }
 
+static int fts5MultiIterAdvanceRowid(
+  Fts5Index *p,                   /* FTS5 backend to iterate within */
+  Fts5MultiSegIter *pIter,        /* Iterator to update aFirst[] array for */
+  int iChanged                    /* Index of sub-iterator just advanced */
+){
+  int i;
+  Fts5SegIter *pNew = &pIter->aSeg[iChanged];
+  Fts5SegIter *pOther = &pIter->aSeg[iChanged ^ 0x0001];
+
+  for(i=(pIter->nSeg+iChanged)/2; p->rc==SQLITE_OK; i=i/2){
+    Fts5CResult *pRes = &pIter->aFirst[i];
+
+    assert( pNew->pLeaf );
+    assert( pRes->bTermEq==0 || pOther->pLeaf );
+    
+    if( pRes->bTermEq ){
+      if( pNew->iRowid==pOther->iRowid ){
+        return 1;
+      }else if( (pOther->iRowid>pNew->iRowid)==pIter->bRev ){
+        pNew = pOther;
+      }
+    }
+    pRes->iFirst = (pNew - pIter->aSeg);
+    if( i==1 ) break;
+
+    pOther = &pIter->aSeg[ pIter->aFirst[i ^ 0x0001].iFirst ];
+  }
+
+  return 0;
+}
+
 /*
 ** Move the iterator to the next entry. 
 **
@@ -2306,17 +2423,25 @@ static void fts5MultiIterNext(
   if( p->rc==SQLITE_OK ){
     int bUseFrom = bFrom;
     do {
-      int iFirst = pIter->aFirst[1];
+      int iFirst = pIter->aFirst[1].iFirst;
+      int bNewTerm = 0;
       Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
       if( bUseFrom && pSeg->pDlidx ){
         fts5SegIterNextFrom(p, pSeg, iFrom);
       }else{
-        fts5SegIterNext(p, pSeg);
+        fts5SegIterNext(p, pSeg, &bNewTerm);
+      }
+
+      if( pSeg->pLeaf==0 || bNewTerm 
+       || fts5MultiIterAdvanceRowid(p, pIter, iFirst)
+      ){
+        fts5MultiIterAdvanced(p, pIter, iFirst, 1);
       }
-      fts5MultiIterAdvanced(p, pIter, iFirst, 1);
+      fts5AssertMultiIterSetup(p, pIter);
+
       bUseFrom = 0;
     }while( pIter->bSkipEmpty 
-         && fts5SegIterIsDelete(p, &pIter->aSeg[pIter->aFirst[1]])
+         && fts5SegIterIsDelete(p, &pIter->aSeg[pIter->aFirst[1].iFirst])
     );
   }
 }
@@ -2336,7 +2461,7 @@ static void fts5MultiIterNew(
   Fts5Index *p,                   /* FTS5 backend to iterate within */
   Fts5Structure *pStruct,         /* Structure of specific index */
   int iIdx,                       /* Config.aHash[] index of FTS index */
-  int bSkipEmpty,
+  int bSkipEmpty,                 /* True to ignore delete-keys */
   int flags,                      /* True for >= */
   const u8 *pTerm, int nTerm,     /* Term to seek to (or NULL/0) */
   int iLevel,                     /* Level to iterate (-1 for all) */
@@ -2363,12 +2488,12 @@ static void fts5MultiIterNew(
   *ppOut = pNew = fts5IdxMalloc(p, 
       sizeof(Fts5MultiSegIter) +          /* pNew */
       sizeof(Fts5SegIter) * nSlot +       /* pNew->aSeg[] */
-      sizeof(u16) * nSlot                 /* pNew->aFirst[] */
+      sizeof(Fts5CResult) * nSlot         /* pNew->aFirst[] */
   );
   if( pNew==0 ) return;
   pNew->nSeg = nSlot;
   pNew->aSeg = (Fts5SegIter*)&pNew[1];
-  pNew->aFirst = (u16*)&pNew->aSeg[nSlot];
+  pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
   pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
   pNew->bSkipEmpty = bSkipEmpty;
 
@@ -2407,13 +2532,14 @@ static void fts5MultiIterNew(
     for(iIter=nSlot-1; iIter>0; iIter--){
       int iEq;
       if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
-        fts5SegIterNext(p, &pNew->aSeg[iEq]);
+        fts5SegIterNext(p, &pNew->aSeg[iEq], 0);
         fts5MultiIterAdvanced(p, pNew, iEq, iIter);
       }
     }
+    fts5AssertMultiIterSetup(p, pNew);
 
     if( pNew->bSkipEmpty 
-     && fts5SegIterIsDelete(p, &pNew->aSeg[pNew->aFirst[1]]) 
+     && fts5SegIterIsDelete(p, &pNew->aSeg[pNew->aFirst[1].iFirst]) 
     ){
       fts5MultiIterNext(p, pNew, 0, 0);
     }
@@ -2428,7 +2554,7 @@ static void fts5MultiIterNew(
 ** False otherwise.
 */
 static int fts5MultiIterEof(Fts5Index *p, Fts5MultiSegIter *pIter){
-  return (p->rc || pIter->aSeg[ pIter->aFirst[1] ].pLeaf==0);
+  return (p->rc || pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0);
 }
 
 /*
@@ -2437,8 +2563,8 @@ static int fts5MultiIterEof(Fts5Index *p, Fts5MultiSegIter *pIter){
 ** results are undefined.
 */
 static i64 fts5MultiIterRowid(Fts5MultiSegIter *pIter){
-  assert( pIter->aSeg[ pIter->aFirst[1] ].pLeaf );
-  return pIter->aSeg[ pIter->aFirst[1] ].iRowid;
+  assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf );
+  return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid;
 }
 
 /*
@@ -2464,7 +2590,7 @@ static void fts5MultiIterNextFrom(
 ** entry that the iterator currently points to.
 */
 static const u8 *fts5MultiIterTerm(Fts5MultiSegIter *pIter, int *pn){
-  Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1] ];
+  Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
   *pn = p->term.n;
   return p->term.p;
 }
@@ -2593,7 +2719,7 @@ static void fts5PosIterInit(
   Fts5PosIter *pIter              /* Initialize this object */
 ){
   if( p->rc==SQLITE_OK ){
-    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
+    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ];
     memset(pIter, 0, sizeof(*pIter));
     fts5ChunkIterInit(p, pSeg, &pIter->chunk);
     if( fts5ChunkIterEof(p, &pIter->chunk)==0 ){
@@ -3214,7 +3340,7 @@ fflush(stdout);
       fts5MultiIterEof(p, pIter)==0;
       fts5MultiIterNext(p, pIter, 0, 0)
   ){
-    Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1] ];
+    Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
     Fts5ChunkIter sPos;           /* Used to iterate through position list */
 
     /* If the segment being written is the oldest in the entire index and
@@ -3940,7 +4066,7 @@ static void fts5MultiIterPoslist(
 ){
   if( p->rc==SQLITE_OK ){
     Fts5ChunkIter iter;
-    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
+    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ];
     assert( fts5MultiIterEof(p, pMulti)==0 );
     fts5ChunkIterInit(p, pSeg, &iter);
     if( fts5ChunkIterEof(p, &iter)==0 ){
index dbf36921fdfe0973de16d230271e91e75a1f37ff..3b3d160cd1458509f8eaca42019e4896f962d93f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\ssome\scompiler\swarnings\scaused\sby\ssigned/unsigned\spointer\sconversions.
-D 2015-03-07T15:46:41.341
+C Avoid\sredundant\sstring\scomparisons\swhile\smerging\sfts5\ssegment\sb-trees.
+D 2015-03-10T19:24:30.225
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -112,7 +112,7 @@ F ext/fts5/fts5_buffer.c 29f79841bf6eef5220eef41b122419b1bcb07b06
 F ext/fts5/fts5_config.c 0847facc8914f57ea4452c43ce109200dc65e894
 F ext/fts5/fts5_expr.c 5215137efab527577d36bdf9e44bfc2ec3e1be98
 F ext/fts5/fts5_hash.c 9959b5408f649487d4b0ee081416f37dc3cd8cdd
-F ext/fts5/fts5_index.c 3eb8db82d08386d6777faeb4ff45ee998b3d9a81
+F ext/fts5/fts5_index.c b00f7147f9660e66d9d1a8149d4faea3a06cd48e
 F ext/fts5/fts5_storage.c ac0f0937059c8d4f38a1f13aa5f2c2cd7edf3e0d
 F ext/fts5/fts5_tcl.c 617b6bb96545be8d9045de6967c688cd9cd15541
 F ext/fts5/fts5_tokenize.c c3fe30914f7722941ea9e0092c07ab5ae87112e4
@@ -1284,7 +1284,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 3ee7b5a9f987c269251620ae7cc0fc7876b58ee5
-R 2a14dede3733f8ccd7d343d89b036f09
+P cccee7b5b1e84523f1c549d3052fd170e32bde80
+R 3da0db5a8a88c2deea54e98a4c62e146
 U dan
-Z b868027a203766c0d15e29e2b4da678d
+Z f8fc1108495d7cd86a621708029f2995
index 51cb90e53a0aa5d324751cdaed285c7e8f7dbe5e..6ab7ead8ed128293e23e9b5cfaac4646f13d2452 100644 (file)
@@ -1 +1 @@
-cccee7b5b1e84523f1c549d3052fd170e32bde80
\ No newline at end of file
+5c46820d9b4aae791a8704b69145bd81f1e6780d
\ No newline at end of file