]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add an optimization to OP_Column to speed up sequential OP_Column sort-column-opcodes
authordan <dan@noemail.net>
Sat, 18 Feb 2017 20:05:05 +0000 (20:05 +0000)
committerdan <dan@noemail.net>
Sat, 18 Feb 2017 20:05:05 +0000 (20:05 +0000)
instructions that read earlier fields from the same cursor. Attempt to reorder
OP_Column opcodes so as to take advantage of this.

FossilOrigin-Name: c1adf95958d5210bf9f89fc86d0f6fc6ce32c480

manifest
manifest.uuid
src/expr.c
src/select.c
src/sqliteInt.h
src/update.c
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeaux.c
src/wherecode.c

index 44763338e48963dc508b3c321d487ef4e5927a04..0a428956a4cf4130d39fd102a03de12c365644f9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\sSQLITE_BUG_COMPATIBLE_20160819\scompile-time\soption\sto\somit\sthe\serror\nmessage\swhen\san\sunrecognized\sargument\sis\sprovided\sto\sthe\sVACUUM\scommand.
-D 2017-02-18T13:47:11.181
+C Add\san\soptimization\sto\sOP_Column\sto\sspeed\sup\ssequential\sOP_Column\ninstructions\sthat\sread\searlier\sfields\sfrom\sthe\ssame\scursor.\sAttempt\sto\sreorder\nOP_Column\sopcodes\sso\sas\sto\stake\sadvantage\sof\sthis.
+D 2017-02-18T20:05:05.184
 F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc a89ea37ab5928026001569f056973b9059492fe2
@@ -347,7 +347,7 @@ F src/ctime.c a9984df73898c042a5cfc8f9d8e7723d02bc35c9
 F src/date.c dc3f1391d9297f8c748132813aaffcb117090d6e
 F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
 F src/delete.c 0d9d5549d42e79ce4d82ff1db1e6c81e36d2f67c
-F src/expr.c 8a29e9b72d4b642189999c41782cd6c5bc43512f
+F src/expr.c 980e4627f5d5efe8366ca5123e9d393e495a404c
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 2e9aabe1aee76273aff8a84ee92c464e095400ae
 F src/func.c c67273e1ec08abbdcc14c189892a3ff6eeece86b
@@ -394,12 +394,12 @@ F src/printf.c 67427bbee66d891fc6f6f5aada857e9cdb368c1c
 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c f9bc0de45a30a450da47b3766de00be89bf9be79
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c d12f3539f80db38b09015561b569e0eb1c4b6c5f
+F src/select.c e32cc5c546d77c217d1b61b17eb953471c8bbd19
 F src/shell.c bf976d5301be9d8a4c52852c97909cc9a41ee20d
 F src/sqlite.h.in 751ff125eb159c8f92c182b8df980a5e4f50e966
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
-F src/sqliteInt.h 46fe8e5aee3825d77fa771216ef263dc947030e7
+F src/sqliteInt.h e30c066962672158359575ddd550db8200107275
 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -457,15 +457,15 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
 F src/tokenize.c 25ccc63ae2c4163933221f3181c9982b47cd08d2
 F src/treeview.c 4e44ade3bfe59d82005039f72e09333ce2b4162c
 F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182
-F src/update.c 456d4a4656f8a03c2abc88a51b19172197400e58
+F src/update.c bcfda1d123d19e86479a619e37a2f3b5aaa978e5
 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
 F src/util.c ca8440ede81e155d15cff7c101654f60b55a9ae6
 F src/vacuum.c 1fe4555cd8c9b263afb85b5b4ee3a4a4181ad569
-F src/vdbe.c 16f378640570c24442fd7191b136b5d6380f5c7b
-F src/vdbe.h 59998ffd71d7caa8886bc78dafaf8caeccd4c13c
-F src/vdbeInt.h 4e4b15b2e1330e1636e4e01974eab2b0b985092f
+F src/vdbe.c 337429082b858125a6dee0803a2140039a94ab07
+F src/vdbe.h 0a52f346f6c89ab144adc11d1c04e94f74425eaa
+F src/vdbeInt.h 6186ba2bd4d4299ada9c17a61a23761c4cb5757b
 F src/vdbeapi.c 3e4a8893feeb78620f4aac4ac5b85d92255b97e1
-F src/vdbeaux.c 4122458d33318ab039c4b5da1ca4e7c9221c38e4
+F src/vdbeaux.c fb1d5bfb60ba6b75a964d91fe5d054cb3dd31839
 F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
 F src/vdbemem.c 3b5a9a5b375458d3e12a50ae1aaa41eeec2175fd
 F src/vdbesort.c eda25cb2d1727efca6f7862fea32b8aa33c0face
@@ -477,7 +477,7 @@ F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
 F src/walker.c 91a6df7435827e41cff6bb7df50ea00934ee78b0
 F src/where.c 01baf58b72f3ddb7793cdee2871f751e3e09b35e
 F src/whereInt.h c0b092180f04608d80c258174b0a14a1f9c8d02f
-F src/wherecode.c 677e95413c472c0b413023b6b69a47f40fce1b04
+F src/wherecode.c 373faba5f8df4d22cd3db9f239ec71a1d0c99e8b
 F src/whereexpr.c 130cdd1a43af71b19755270fb1224874cf55158c
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
@@ -1556,7 +1556,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8cc9d74c176a78aeebfbb39198c21b5dd547ff52
-R 57839b570efd132c834489fe34d93e5e
-U drh
-Z 38cfd439ab0cfeae01cbf1d291727475
+P 491814272dce7e937b4734fcbc2ad69e12377b56
+R edee9ca5068aff62e1a1b155721f75af
+T *branch * sort-column-opcodes
+T *sym-sort-column-opcodes *
+T -sym-trunk *
+U dan
+Z b7f122691d1e00c6c9a153ed5456e504
index 557d9a7e8b87af705ae093d6dc7a7605d85ded0f..39a2c60c5a32de6abcbe1d12fd2349adcb925b35 100644 (file)
@@ -1 +1 @@
-491814272dce7e937b4734fcbc2ad69e12377b56
\ No newline at end of file
+c1adf95958d5210bf9f89fc86d0f6fc6ce32c480
\ No newline at end of file
index 2bf76eb0d7cfc9ca02278a0e3fce1ed10e283ddf..2c9f3f4968877a906ac6d8f7c342ce35149079d9 100644 (file)
@@ -4152,6 +4152,104 @@ void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
   exprToRegister(pExpr, iMem);
 }
 
+/*
+** Return the value of (*p1) - (*p2), as defined by the sort order described
+** in the comment above reorderColumnFetch(). 
+*/
+static int compareOpCode(VdbeOp *p1, VdbeOp *p2){
+  int res;
+
+  assert( p1->opcode==OP_Column || p1->opcode==OP_VColumn
+       || p1->opcode==OP_Copy   || p1->opcode==OP_SCopy
+       || p1->opcode==OP_Rowid  || p1->opcode==OP_RealAffinity
+  );
+  assert( p2->opcode==OP_Column || p2->opcode==OP_VColumn
+       || p2->opcode==OP_Copy   || p2->opcode==OP_SCopy
+       || p2->opcode==OP_Rowid  || p2->opcode==OP_RealAffinity
+  );
+
+  assert( OP_VColumn>OP_Column && OP_Rowid>OP_Column );
+  assert( OP_Column>OP_RealAffinity );
+  assert( OP_RealAffinity>OP_Copy && OP_RealAffinity>OP_SCopy );
+
+  res = (int)(p2->opcode) - (int)(p1->opcode);
+  if( res==0 ){
+    res = p1->p1 - p2->p1;
+    if( res==0 ){
+      res = p2->p2 - p1->p2;
+    }
+  }
+  return res;
+}
+
+/*
+** The VM instructions from iFirst to the current address were generated
+** in order to populate an array of registers with the results of a series
+** of TK_COLUMN expressions. This guarantees that the specified range 
+** contains opcodes of the following types only:
+**
+**   Rowid
+**   Column
+**   VColumn
+**   RealAffinity
+**   Copy
+**   SCopy
+**
+** This function sorts the opcodes so all of the OP_Column appear in a
+** contiguous block, sorted by (p1, p2 DESC). The VDBE layer processes
+** OP_Column instructions in this order more efficiently.
+**
+** In practice, the array is sorted so that all instructions of each type 
+** of opcode are arranged into a contiguous group. Given the following,
+** this is a safe re-ordering:
+**
+**   * All Rowid, VColumn and Column instructions appear before all 
+**     RealAffinity instructions (that might operate on the result of
+**     a VColumn or Column), and
+**
+**   * All RealAffinity instructions occur before all Copy and SCopy
+**     instructions (which might read a column-cache entry populated
+**     by a prior Column+RealAffinity).
+*/
+static void reorderColumnFetch(sqlite3 *db, Vdbe *v, int iFirst){
+  int iEnd = sqlite3VdbeCurrentAddr(v);
+  int nOp = iEnd-iFirst;
+  if( nOp>1 ){
+    VdbeOp *aSpace = (VdbeOp*)sqlite3StackAllocRaw(db, sizeof(VdbeOp) * nOp);
+    if( aSpace ){
+      int sz;
+      VdbeOp *aOp = sqlite3VdbeGetOp(v, iFirst);
+      VdbeOp *a1 = aOp;
+      VdbeOp *a2 = aSpace;
+      for(sz=1; sz<nOp; sz=sz*2){
+        int i;
+        for(i=0; i<nOp; i+=(sz*2)){
+          /* Merge the two lists of sz elements each starting at a1[i] and 
+          ** a1[i+sz] into a sz element list at a2[i].  */
+          int e1 = MIN(i+sz, nOp);
+          int e2 = MIN(i+sz*2, nOp);
+          int i2 = i+sz;
+          int i1 = i;
+          int iOut = i;
+
+          while( i1<e1 || i2<e2 ){
+            if( i1>=e1 || (i2<e2 && compareOpCode(&a1[i2], &a1[i1])<0) ){
+              a2[iOut++] = a1[i2++];
+            }else{
+              a2[iOut++] = a1[i1++];
+            }
+          }
+        }
+        SWAP(VdbeOp*, a1, a2);
+      }
+      if( a1!=aOp ){
+        memcpy(aOp, a1, sizeof(VdbeOp)*nOp);
+      }
+      sqlite3StackFree(db, aSpace);
+    }
+  }
+}
+
 /*
 ** Generate code that pushes the value of every element of the given
 ** expression list into a sequence of registers beginning at target.
@@ -4177,6 +4275,7 @@ int sqlite3ExprCodeExprList(
 ){
   struct ExprList_item *pItem;
   int i, j, n;
+  int iFirst = -1;
   u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy;
   Vdbe *v = pParse->pVdbe;
   assert( pList!=0 );
@@ -4186,6 +4285,14 @@ int sqlite3ExprCodeExprList(
   if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
   for(pItem=pList->a, i=0; i<n; i++, pItem++){
     Expr *pExpr = pItem->pExpr;
+    if( iFirst>0 ){
+      if( pExpr->op!=TK_COLUMN ){
+        reorderColumnFetch(pParse->db, v, iFirst);
+        iFirst = -1;
+      }
+    }else{
+      if( pExpr->op==TK_COLUMN ) iFirst = sqlite3VdbeCurrentAddr(v);
+    }
     if( (flags & SQLITE_ECEL_REF)!=0 && (j = pItem->u.x.iOrderByCol)>0 ){
       if( flags & SQLITE_ECEL_OMITREF ){
         i--;
@@ -4211,6 +4318,7 @@ int sqlite3ExprCodeExprList(
       }
     }
   }
+  if( iFirst>0 ) reorderColumnFetch(pParse->db, v, iFirst);
   return n;
 }
 
index d817ebd074f39c616d5287af89cd2397655dc061..d18f002bca238fdda3f82bd6fa8e8c99e922a48f 100644 (file)
@@ -1274,15 +1274,22 @@ static void generateSortTail(
     iSortTab = iTab;
     bSeq = 1;
   }
-  for(i=0, iCol=nKey+bSeq; i<nSortData; i++){
-    int iRead;
+  iCol = nKey+bSeq;
+  for(i=0; i<nSortData; i++){
+    if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++;
+  }
+  for(i=nSortData-1; i>=0; i--){
+    if( aOutEx[i].u.x.iOrderByCol==0 ){
+      sqlite3VdbeAddOp3(v, OP_Column, iSortTab, --iCol, regRow+i);
+      VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName:aOutEx[i].zSpan));
+    }
+  }
+  for(i=nSortData-1; i>=0; i--){
     if( aOutEx[i].u.x.iOrderByCol ){
-      iRead = aOutEx[i].u.x.iOrderByCol-1;
-    }else{
-      iRead = iCol++;
+      int iRead = aOutEx[i].u.x.iOrderByCol-1;
+      sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
+      VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName:aOutEx[i].zSpan));
     }
-    sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
-    VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
   }
   switch( eDest ){
     case SRT_Table:
index 9a53d33626807e37efd0333fd680f805a7968926..a5965ade1a01fc631824a6f7c7d3f3e8eecbea20 100644 (file)
@@ -3081,6 +3081,8 @@ struct AuthContext {
 #define OPFLAG_SAVEPOSITION  0x02    /* OP_Delete/Insert: save cursor pos */
 #define OPFLAG_AUXDELETE     0x04    /* OP_Delete: index in a DELETE op */
 
+#define OPFLAG_CONTINUE      0x01
+
 /*
  * Each trigger present in the database schema is stored as an instance of
  * struct Trigger.
index 8fca7eb9882e2e980b5b886c639e51516d4e1041..d2bd785cac201cdd8bf82efe328433cd73034820 100644 (file)
@@ -547,7 +547,11 @@ void sqlite3Update(
   newmask = sqlite3TriggerColmask(
       pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
   );
+#if 1
+  for(i=pTab->nCol-1; i>=0; i--){
+#else
   for(i=0; i<pTab->nCol; i++){
+#endif
     if( i==pTab->iPKey ){
       sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
     }else{
index 7f286b2c6d3a068511d6f28a863fab7e48547c20..2c9ac2f97d6a6cd54060f75cc8e410c1b494f512 100644 (file)
@@ -553,7 +553,6 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
   }
 }
 
-
 /*
 ** Execute as much of a VDBE program as we can.
 ** This is the core of sqlite3_step().  
@@ -2627,27 +2626,42 @@ case OP_Column: {
   if( pC->szRow>=aOffset[p2+1] ){
     /* This is the common case where the desired content fits on the original
     ** page - where the content is not on an overflow page */
-    zData = pC->aRow + aOffset[p2];
-    if( t<12 ){
-      sqlite3VdbeSerialGet(zData, t, pDest);
-    }else{
-      /* If the column value is a string, we need a persistent value, not
-      ** a MEM_Ephem value.  This branch is a fast short-cut that is equivalent
-      ** to calling sqlite3VdbeSerialGet() and sqlite3VdbeDeephemeralize().
-      */
-      static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term };
-      pDest->n = len = (t-12)/2;
-      pDest->enc = encoding;
-      if( pDest->szMalloc < len+2 ){
-        pDest->flags = MEM_Null;
-        if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem;
+    while( 1 ){
+      zData = pC->aRow + aOffset[p2];
+      if( t<12 ){
+        sqlite3VdbeSerialGet(zData, t, pDest);
       }else{
-        pDest->z = pDest->zMalloc;
+        /* If the column value is a string, we need a persistent value, not
+        ** a MEM_Ephem value.  This branch is a fast short-cut that is
+        ** equivalent to calling sqlite3VdbeSerialGet() and
+        ** sqlite3VdbeDeephemeralize().
+        */
+        static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term };
+        pDest->n = len = (t-12)/2;
+        pDest->enc = encoding;
+        if( pDest->szMalloc < len+2 ){
+          pDest->flags = MEM_Null;
+          if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem;
+        }else{
+          pDest->z = pDest->zMalloc;
+        }
+        memcpy(pDest->z, zData, len);
+        pDest->z[len] = 0;
+        pDest->z[len+1] = 0;
+        pDest->flags = aFlag[t&1];
+      }
+
+      if( (pOp->p5 & OPFLAG_CONTINUE)==0 ) break;
+      UPDATE_MAX_BLOBSIZE(pDest);
+      REGISTER_TRACE(pOp->p3, pDest);
+      pOp++;
+      p2 = pOp->p2;
+      t = pC->aType[p2];
+      pDest = &aMem[pOp->p3];
+      memAboutToChange(p, pDest);
+      if( VdbeMemDynamic(pDest) ){
+        sqlite3VdbeMemSetNull(pDest);
       }
-      memcpy(pDest->z, zData, len);
-      pDest->z[len] = 0;
-      pDest->z[len+1] = 0;
-      pDest->flags = aFlag[t&1];
     }
   }else{
     pDest->enc = encoding;
index a35f3be3443be2f2554a2cdb25384cfa56fecc13..267355f5ed667e48376821e083702e9ae2ae3b20 100644 (file)
@@ -226,6 +226,7 @@ void sqlite3VdbeSwap(Vdbe*,Vdbe*);
 VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
 sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8);
 void sqlite3VdbeSetVarmask(Vdbe*, int);
+void sqlite3VdbeUsesAltMap(Vdbe*);
 #ifndef SQLITE_OMIT_TRACE
   char *sqlite3VdbeExpandSql(Vdbe*, const char*);
 #endif
index 989cdfd34617c9a19ec738c7cc1b7f1ad9030edc..dc380ee673ab3996a437201a338c8b4106ff0545 100644 (file)
@@ -390,6 +390,7 @@ struct Vdbe {
   bft readOnly:1;         /* True for statements that do not write */
   bft bIsReader:1;        /* True for statements that read */
   bft isPrepareV2:1;      /* True if prepared with prepare_v2() */
+  bft usesAltMap:1;       /* True if uses VdbeCursor.aAltMap[] */
   yDbMask btreeMask;      /* Bitmask of db->aDb[] entries referenced */
   yDbMask lockMask;       /* Subset of btreeMask that requires a lock */
   u32 aCounter[5];        /* Counters used by sqlite3_stmt_status() */
index cf062cf01d7cda4b631872e7558d9f62c31c4743..fccd0e9536ddcb7b4b63348955ed8a1dbd041c0b 100644 (file)
@@ -618,6 +618,13 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
     }
     if( pOp==p->aOp ) break;
     pOp--;
+
+    if( p->usesAltMap==0 
+     && pOp[0].opcode==OP_Column && pOp[1].opcode==OP_Column
+     && pOp[0].p1==pOp[1].p1 && pOp[0].p2>=pOp[1].p2
+    ){
+      pOp->p5 |= OPFLAG_CONTINUE;
+    }
   }
   sqlite3DbFree(p->db, pParse->aLabel);
   pParse->aLabel = 0;
@@ -4557,6 +4564,12 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
   }
 }
 
+/*
+** Set the "uses-alt-map" flag.
+*/
+void sqlite3VdbeUsesAltMap(Vdbe *v){
+  v->usesAltMap = 1;
+}
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 /*
 ** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
index 4fd5e16fac9944c4d6903d106a691c47e70c0009..498ef48698fa049aa11e9be5d5cd78e2ca88a64b 100644 (file)
@@ -1003,6 +1003,7 @@ static void codeDeferredSeek(
       }
       sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
     }
+    sqlite3VdbeUsesAltMap(v);
   }
 }