On 01/23/2013 04:20 AM, Heiles, Katrina wrote:
I am in the process of updating from 3.6.4 to the latest and greatest version
(finally :-) ).
While running performance unit tests, i found a major regression (10K/sec vs
30k/sec)
on a common use case for us. The regression occurs when using "insert or
replace".
I narrowed this down as follows:
- the regression was introduced in version 3.6.19, and has been there ever
since.
- If i comment out line 74643 in sqlite3.c of version 3.6.19, performance is
good again.
QUESTION: I realize that commenting this line out is not the correct solution.
Is there any way to improve performance of "insert or replace"?
Out of interest, is performance improved any with 3.7.15.2 if you
execute "PRAGMA temp_store = memory" before calling _do_batch_insert()?
Thanks,
Dan.
I am including 2 other attachments:
------------------------------------
1. test_batch_insert.c
- this is the unit test i created to reproduce the issue. It output
2. notes.txt
- this contains the performance output from running test_batch_insert.c
on 3 different versions of sqlite
- 3.6.18 (fast)
- 3.6.19 (slow)
- 3.6.19 with line 74643 commented out (fast)
Below are detailed (but cryptic) notes on what causes the issue. Please let me
know if you need more info.
system:
Linux 2.6.17-1.2142_FC4smp #1 SMP Tue Jul 11 22:59:20 EDT 2006 x86_64 x86_64
x86_64 GNU/Linux
NOTE: all line number references are based on 3_6_19 version of sqlite3.c,
where the regression was first introduced.
ROOT CAUSE:
If I remove one line of code from sqlite3.c (line 74643) that was added in this
release, performance
regression is resolved.
-----------------------------------------------------------------------------------------------
74640 default: {
74641 Trigger *pTrigger = 0;
74642 assert( onError==OE_Replace );
74643 sqlite3MultiWrite(pParse);<----------- THIS IS THE GUILTY
LINE!!!!
74644 if( pParse->db->flags&SQLITE_RecTriggers ){
74645 pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0,
0);
74646 }
74647 sqlite3GenerateRowDelete(
74648 pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace
74649 );
74650 seenReplace = 1;
74651 break;
74652 }
-----------------------------------------------------------------------------------------------
DETAILS OF WHY THIS LINE CAUSES THE REGRESSION:
The effect of including line 74643 when running a batch insert:
HIGH LEVEL:
in pager_write function, we<sometimes> end up going into this if statement,
which creates a
subjournal, causing creation of etilqs_xxx file.
NOTE: using the attached test_batch_insert.c file with max_entries set to
40000, this
results in creation of 15 etilqs_xxx and 262122 MORE writes!!
-----------------------------------------------------------------------------------------------
35536 /* If the statement journal is open and the page is not in it,
35537 ** then write the current page to the statement journal. Note
that
35538 ** the statement journal format differs from the standard
journal format
35539 ** in that it omits the checksums and the header.
35540 */
35541 if( subjRequiresPage(pPg) ){
35542 rc = subjournalPage(pPg);<---- we create a subjournal
35543 }
35544 }
-----------------------------------------------------------------------------------------------
LOWER LEVEL DETAILS OF WHY subJRequiresPage IS CALLED WHEN LINE 74643 IS IN THE
CODE
when running sample test_batch_insert.c file (included in this bug)
The reason that subjRequiresPage() returns true is that when
sqlite3GenerateConstraintChecks is called from sqlite3Insert,
this calls the guilty line,(74643)sqlite3MultiWrite(pParse);
- This sets pParse->isMultiWrite to 1
- then sqlite3FinishCoding calls sqlite3VdbeMakeReady with
pParse->isMultiWrite =1
causing 3rd param of call to be TRUE.
- This causes Vdbe.usesStmtJournal to be set to TRUE in sqliteVdbeMakeReady
- Then sqlite3BtreeBeginStmt is called from sqlite3VdbeExec (case
OP_Transaction:)
here, p->iStatement is set to 1 because p->usesStmtJournal is 1
54698 if( pOp->p2&& p->usesStmtJournal<--- we go INTO this
if statement
54699&& (db->autoCommit==0 || db->activeVdbeCnt>1)
54700 ){
54701 assert( sqlite3BtreeIsInTrans(u.as.pBt) );
54702 if( p->iStatement==0 ){
54703 assert( db->nStatement>=0&& db->nSavepoint>=0 );
54704 db->nStatement++;<---- this sets to 1, causing
next line to set iStatement to 1
54705 p->iStatement = db->nSavepoint + db->nStatement;
54706 }
54707 rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement);
- sqlite3BtreeBeginStmt calls sqlite3PagerOpenSavepoint using iStatement
as 2nd parameter,
therefore nSavepoint is set to 1
- eventually we call sqlite3BtreeInsert which calls insertCell which calls
sqlite3PagerWrite which
calls pager_write a second time for the SAME pPg->pgno (see below for
example page 6)
- because we've gone through once for the same page, we do not
enter the if clause
on line 35464,
35464 /* The transaction journal now exists and we have a
RESERVED or an
35465 ** EXCLUSIVE lock on the main database file. Write
the current page to
35466 ** the transaction journal if it is not there already.
35467 */
35468 if( !pageInJournal(pPg)&& isOpen(pPager->jfd) ){
- therefore we DO NOT add this page to the savepoint bitVec on line 35517
35517 rc = sqlite3BitvecSet(pPager->pInJournal,
pPg->pgno);
- therefore we DO go into the if(subjRequiresPage(pPg)), because
nSavepoint is 1 but the bit is NOT set
35536 /* If the statement journal is open and the page is
not in it,
35537 ** then write the current page to the statement
journal. Note that
35538 ** the statement journal format differs from the
standard journal format
35539 ** in that it omits the checksums and the header.
35540 */
35541 if( subjRequiresPage(pPg) ){
35542 rc = subjournalPage(pPg);
35543 }
from gdb...
- (gdb) p *pPg
$17= {pPage = 0x2b84618, pData = 0x2b83618, pExtra =
0x2b84698, pDirty = 0x0, pPager = 0x2b666c8,
pgno = 6, flags = 6, nRef = 1, pCache = 0x2b667d0,
pDirtyNext = 0x2b83540, pDirtyPrev = 0x0}
(gdb) p pageInJournal(pPg)
$18 = 1
(gdb) p sqlite3BitvecTest(pPager->aSavepoint[0]->pInSavepoint,6)
$19 = 0
- therefore we end up CALLING subjournalPage(pPg) from line 35542,
causing temp file to be created and lots of small writes to the file
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users