From 9b47ee3f09c4bad491c4b8b6b6a71d89604e54c5 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 20 Aug 2013 03:13:51 +0000 Subject: [PATCH] Performance optimizations in the VDBE and especially to the OP_Next and related opcodes and in the sqlite3BtreeNext() and sqlite3BtreePrevious() routines. This is a cherrypick of [6f99b54aedeb], [d2efea1682a7], and [d78c5d89de4b]. FossilOrigin-Name: 7f72fc4f47445a2c01910b268335873de9f75059 --- manifest | 23 +++++++++++++---------- manifest.uuid | 2 +- src/btree.c | 48 +++++++++++++++++++++++++++++++++--------------- src/btreeInt.h | 16 +++++++++++----- src/vdbe.c | 34 ++++++++++++++++------------------ src/vdbeInt.h | 9 +++++---- src/vdbeapi.c | 15 ++++++++++++--- 7 files changed, 91 insertions(+), 56 deletions(-) diff --git a/manifest b/manifest index 917de0a385..9ad5c805c7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\simprovement\sin\ssqlite3BtreeNext()\sand\ssqlite3BtreePrevious()\nfor\sthe\scommon\scase\sof\sa\svalid\scursor. -D 2013-08-19T20:04:10.623 +C Performance\soptimizations\sin\sthe\sVDBE\sand\sespecially\sto\sthe\sOP_Next\sand\nrelated\sopcodes\sand\sin\sthe\ssqlite3BtreeNext()\sand\ssqlite3BtreePrevious()\nroutines.\s\sThis\sis\sa\scherrypick\sof\s[6f99b54aedeb],\s[d2efea1682a7],\sand\n[d78c5d89de4b]. +D 2013-08-20T03:13:51.513 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -163,9 +163,9 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 43b348822db3e4cef48b2ae5a445fbeb6c73a165 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c 78287b3012fe65fd5e38927d40c01e01a783e016 +F src/btree.c adea13e65d6c7b969bcd74cea6ae79b2d3c393fa F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf -F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2 +F src/btreeInt.h 51cf220a9b9223354770883e93a859dc377aa27f F src/build.c f99a715ff9290996b579d5e1ec8e94239dc9ae5e F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac @@ -277,10 +277,10 @@ F src/update.c 7f3fe64d8f3b44c44a1eac293f0f85f87c355b7a F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9 F src/vacuum.c d9c5759f4c5a438bb43c2086f72c5d2edabc36c8 -F src/vdbe.c 0fbe7a904a1187dc6c8a2dbe2f594f0ce8d01401 +F src/vdbe.c 938feb53407dee2234849aad0f103ae9b941595e F src/vdbe.h 4f554b5627f26710c4c36d919110a3fc611ca5c4 -F src/vdbeInt.h e9b7c6b165a31a4715c5aa97223d20d265515231 -F src/vdbeapi.c 4d13580bd058b39623e8fcfc233b7df4b8191e8b +F src/vdbeInt.h cbe71b8b36d8b3bba5709cc3f436c7e3b47b7b08 +F src/vdbeapi.c 96b24b946cf21894f63d9393e821baa2f0a80979 F src/vdbeaux.c a6ea36a9dc714e1128a0173249a0532ddcab0489 F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69 F src/vdbemem.c 833005f1cbbf447289f1973dba2a0c2228c7b8ab @@ -1105,7 +1105,10 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P d6c4d48a002a6d7057fccc30064ce0b049678f0c -R 5570190c517979d35e3b41a2553566a9 +P dc65ad8c4c67b21e3b042b8df6580d02b634a90b +Q +6f99b54aedeb91e46d52f65504d02a9cc61c0062 +Q +d2efea1682a7e708000c1f5d36370aaf1199b3be +Q +d78c5d89de4b840351b026c9db1952fc24e689d0 +R 539c71750e964e780f11bfb1b59ee716 U drh -Z 8413b4240dd170b2f74e62b701d0cb1c +Z feae42d544c0a4532e8cd3cb8461eca6 diff --git a/manifest.uuid b/manifest.uuid index 7c9d48de8c..42e6161093 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dc65ad8c4c67b21e3b042b8df6580d02b634a90b \ No newline at end of file +7f72fc4f47445a2c01910b268335873de9f75059 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index bf679cd1f7..27f9f412fb 100644 --- a/src/btree.c +++ b/src/btree.c @@ -724,6 +724,9 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); + if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ + pCur->eState = CURSOR_SKIPNEXT; + } } return rc; } @@ -749,7 +752,7 @@ int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){ *pHasMoved = 1; return rc; } - if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){ + if( pCur->eState!=CURSOR_VALID || NEVER(pCur->skipNext!=0) ){ *pHasMoved = 1; }else{ *pHasMoved = 0; @@ -4797,23 +4800,27 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ rc = restoreCursorPosition(pCur); if( rc!=SQLITE_OK ){ + *pRes = 0; return rc; } if( CURSOR_INVALID==pCur->eState ){ *pRes = 1; return SQLITE_OK; } - } - if( pCur->skipNext ){ - if( pCur->skipNext>0 ){ + if( pCur->skipNext ){ + assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); + pCur->eState = CURSOR_VALID; + if( pCur->skipNext>0 ){ + pCur->skipNext = 0; + *pRes = 0; + return SQLITE_OK; + } pCur->skipNext = 0; - *pRes = 0; - return SQLITE_OK; } - pCur->skipNext = 0; } pPage = pCur->apPage[pCur->iPage]; @@ -4832,7 +4839,10 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ if( idx>=pPage->nCell ){ if( !pPage->leaf ){ rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); - if( rc ) return rc; + if( rc ){ + *pRes = 0; + return rc; + } rc = moveToLeftmost(pCur); *pRes = 0; return rc; @@ -4874,24 +4884,31 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ MemPage *pPage; assert( cursorHoldsMutex(pCur) ); + assert( pRes!=0 ); + assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->atLast = 0; if( pCur->eState!=CURSOR_VALID ){ if( ALWAYS(pCur->eState>=CURSOR_REQUIRESEEK) ){ rc = btreeRestoreCursorPosition(pCur); - if( rc!=SQLITE_OK ) return rc; + if( rc!=SQLITE_OK ){ + *pRes = 0; + return rc; + } } if( CURSOR_INVALID==pCur->eState ){ *pRes = 1; return SQLITE_OK; } - } - if( pCur->skipNext ){ - if( pCur->skipNext<0 ){ + if( pCur->skipNext ){ + assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); + pCur->eState = CURSOR_VALID; + if( pCur->skipNext<0 ){ + pCur->skipNext = 0; + *pRes = 0; + return SQLITE_OK; + } pCur->skipNext = 0; - *pRes = 0; - return SQLITE_OK; } - pCur->skipNext = 0; } pPage = pCur->apPage[pCur->iPage]; @@ -4900,6 +4917,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int idx = pCur->aiIdx[pCur->iPage]; rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); if( rc ){ + *pRes = 0; return rc; } rc = moveToRightmost(pCur); diff --git a/src/btreeInt.h b/src/btreeInt.h index ce3c5493f8..60da24d90c 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -520,14 +520,19 @@ struct BtCursor { /* ** Potential values for BtCursor.eState. ** -** CURSOR_VALID: -** Cursor points to a valid entry. getPayload() etc. may be called. -** ** CURSOR_INVALID: ** Cursor does not point to a valid entry. This can happen (for example) ** because the table is empty or because BtreeCursorFirst() has not been ** called. ** +** CURSOR_VALID: +** Cursor points to a valid entry. getPayload() etc. may be called. +** +** CURSOR_SKIPNEXT: +** Cursor is valid except that the Cursor.skipNext field is non-zero +** indicating that the next sqlite3BtreeNext() or sqlite3BtreePrevious() +** operation should be a no-op. +** ** CURSOR_REQUIRESEEK: ** The table that this cursor was opened on still exists, but has been ** modified since the cursor was last used. The cursor position is saved @@ -544,8 +549,9 @@ struct BtCursor { */ #define CURSOR_INVALID 0 #define CURSOR_VALID 1 -#define CURSOR_REQUIRESEEK 2 -#define CURSOR_FAULT 3 +#define CURSOR_SKIPNEXT 2 +#define CURSOR_REQUIRESEEK 3 +#define CURSOR_FAULT 4 /* ** The database page the PENDING_BYTE occupies. This page is never used. diff --git a/src/vdbe.c b/src/vdbe.c index 4bd26b377c..516e8ae9b4 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -588,7 +588,7 @@ int sqlite3VdbeExec( #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( db->xProgress ){ assert( 0 < db->nProgressOps ); - nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP-1]; + nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP]; if( nProgressLimit==0 ){ nProgressLimit = db->nProgressOps; }else{ @@ -1449,7 +1449,7 @@ case OP_Function: { sqlite3VdbeMemMove(&ctx.s, pOut); MemSetTypeFlag(&ctx.s, MEM_Null); - ctx.isError = 0; + ctx.fErrorOrAux = 0; if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); @@ -1460,11 +1460,6 @@ case OP_Function: { (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */ lastRowid = db->lastRowid; - /* If any auxiliary data functions have been called by this user function, - ** immediately call the destructor for any non-static values. - */ - sqlite3VdbeDeleteAuxData(p, pc, pOp->p1); - if( db->mallocFailed ){ /* Even though a malloc() has failed, the implementation of the ** user function may have called an sqlite3_result_XXX() function @@ -1476,9 +1471,12 @@ case OP_Function: { } /* If the function returned an error, throw an exception */ - if( ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); - rc = ctx.isError; + if( ctx.fErrorOrAux ){ + if( ctx.isError ){ + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); + rc = ctx.isError; + } + sqlite3VdbeDeleteAuxData(p, pc, pOp->p1); } /* Copy the result of the function into register P3 */ @@ -1850,12 +1848,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ - if( pOp->p5 & SQLITE_STOREP2 ){ + if( pOp->p5 & SQLITE_JUMPIFNULL ){ + pc = pOp->p2-1; + }else if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; MemSetTypeFlag(pOut, MEM_Null); REGISTER_TRACE(pOp->p2, pOut); - }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; } break; } @@ -4440,7 +4438,7 @@ case OP_Sort: { /* jump */ sqlite3_sort_count++; sqlite3_search_count--; #endif - p->aCounter[SQLITE_STMTSTATUS_SORT-1]++; + p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ } /* Opcode: Rewind P1 P2 * * * @@ -4519,7 +4517,7 @@ case OP_Next: { /* jump */ int res; assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( pOp->p5<=ArraySize(p->aCounter) ); + assert( pOp->p5aCounter) ); pC = p->apCsr[pOp->p1]; if( pC==0 ){ break; /* See ticket #2273 */ @@ -4529,7 +4527,7 @@ case OP_Next: { /* jump */ assert( pOp->opcode==OP_SorterNext ); rc = sqlite3VdbeSorterNext(db, pC, &res); }else{ - res = 1; + /* res = 1; // Always initialized by the xAdvance() call */ assert( pC->deferredMoveto==0 ); assert( pC->pCursor ); assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); @@ -4540,7 +4538,7 @@ case OP_Next: { /* jump */ pC->cacheStatus = CACHE_STALE; if( res==0 ){ pc = pOp->p2 - 1; - if( pOp->p5 ) p->aCounter[pOp->p5-1]++; + p->aCounter[pOp->p5]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif @@ -6243,7 +6241,7 @@ vdbe_error_halt: vdbe_return: db->lastRowid = lastRowid; testcase( nVmStep>0 ); - p->aCounter[SQLITE_STMTSTATUS_VM_STEP-1] += (int)nVmStep; + p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep; sqlite3VdbeLeave(p); return rc; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 9ee82b4ea0..a699c414b0 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -266,10 +266,11 @@ struct sqlite3_context { Mem s; /* The return value is stored here */ Mem *pMem; /* Memory cell used to store aggregate context */ CollSeq *pColl; /* Collating sequence */ - int isError; /* Error code returned by the function. */ - int skipFlag; /* Skip skip accumulator loading if true */ - int iOp; /* Instruction number of OP_Function */ Vdbe *pVdbe; /* The VM that owns this context */ + int iOp; /* Instruction number of OP_Function */ + int isError; /* Error code returned by the function. */ + u8 skipFlag; /* Skip skip accumulator loading if true */ + u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */ }; /* @@ -345,7 +346,7 @@ struct Vdbe { yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ - int aCounter[4]; /* Counters used by sqlite3_stmt_status() */ + u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 7c9db9beec..52c6b2a797 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -211,12 +211,14 @@ void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); pCtx->isError = SQLITE_ERROR; + pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); pCtx->isError = SQLITE_ERROR; + pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif @@ -280,6 +282,7 @@ void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ } void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ pCtx->isError = errCode; + pCtx->fErrorOrAux = 1; if( pCtx->s.flags & MEM_Null ){ sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1, SQLITE_UTF8, SQLITE_STATIC); @@ -290,6 +293,7 @@ void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ void sqlite3_result_error_toobig(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); pCtx->isError = SQLITE_TOOBIG; + pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1, SQLITE_UTF8, SQLITE_STATIC); } @@ -299,6 +303,7 @@ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE_NOMEM; + pCtx->fErrorOrAux = 1; pCtx->s.db->mallocFailed = 1; } @@ -621,6 +626,10 @@ void sqlite3_set_auxdata( pAuxData->iArg = iArg; pAuxData->pNext = pVdbe->pAuxData; pVdbe->pAuxData = pAuxData; + if( pCtx->fErrorOrAux==0 ){ + pCtx->isError = 0; + pCtx->fErrorOrAux = 1; + } }else if( pAuxData->xDelete ){ pAuxData->xDelete(pAuxData->pAux); } @@ -1289,7 +1298,7 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ */ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ Vdbe *pVdbe = (Vdbe*)pStmt; - int v = pVdbe->aCounter[op-1]; - if( resetFlag ) pVdbe->aCounter[op-1] = 0; - return v; + u32 v = pVdbe->aCounter[op]; + if( resetFlag ) pVdbe->aCounter[op] = 0; + return (int)v; }