mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Consolidate memory allocations made while loading stat4 data in a way that may be more efficient on systems under load.
FossilOrigin-Name: af65a902d10e50d827ff31f9ded7d05bc7ab0956a767366e1321b28fcb60b0bd
This commit is contained in:
209
src/analyze.c
209
src/analyze.c
@@ -256,6 +256,13 @@ static void openStatTable(
|
||||
# define SQLITE_STAT4_SAMPLES 24
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Assumed number of of samples when loading sqlite_stat4 data. It doesn't
|
||||
** matter if there are more or fewer samples than this, but is more efficient
|
||||
** if this estimate turns out to be true.
|
||||
*/
|
||||
#define SQLITE_STAT4_EST_SAMPLES SQLITE_STAT4_SAMPLES
|
||||
|
||||
/*
|
||||
** Three SQL functions - stat_init(), stat_push(), and stat_get() -
|
||||
** share an instance of the following structure to hold their state
|
||||
@@ -1550,6 +1557,9 @@ static void decodeIntArray(
|
||||
#endif
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
if( aOut ){
|
||||
for(/* no-op */; i<nOut; i++){ aOut[i] = 0; }
|
||||
}
|
||||
#ifndef SQLITE_ENABLE_STAT4
|
||||
assert( pIndex!=0 ); {
|
||||
#else
|
||||
@@ -1672,11 +1682,14 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
IndexSample *p = &pIdx->aSample[j];
|
||||
sqlite3DbFree(db, p->p);
|
||||
}
|
||||
sqlite3DbFree(db, pIdx->aSample);
|
||||
if( pIdx->nSampleAlloc!=SQLITE_STAT4_EST_SAMPLES ){
|
||||
sqlite3DbFree(db, pIdx->aSample);
|
||||
}
|
||||
}
|
||||
if( db->pnBytesFreed==0 ){
|
||||
pIdx->nSample = 0;
|
||||
pIdx->aSample = 0;
|
||||
pIdx->nSampleAlloc = 0;
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
@@ -1762,8 +1775,110 @@ static Index *findIndexOrPrimaryKey(
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content from either the sqlite_stat4
|
||||
** into the relevant Index.aSample[] arrays.
|
||||
** Grow the pIdx->aSample[] array. Return SQLITE_OK if successful, or
|
||||
** SQLITE_NOMEM otherwise.
|
||||
*/
|
||||
static int growSampleArray(sqlite3 *db, Index *pIdx, int *piOff){
|
||||
int nIdxCol = pIdx->nSampleCol;
|
||||
int nNew = 0;
|
||||
IndexSample *aNew = 0;
|
||||
int nByte = 0;
|
||||
tRowcnt *pSpace; /* Available allocated memory space */
|
||||
u8 *pPtr; /* Available memory as a u8 for easier manipulation */
|
||||
int i;
|
||||
u64 t;
|
||||
|
||||
assert( pIdx->nSample==pIdx->nSampleAlloc );
|
||||
nNew = SQLITE_STAT4_EST_SAMPLES;
|
||||
if( pIdx->nSample ){
|
||||
nNew = pIdx->nSample*2;
|
||||
}
|
||||
|
||||
/* Set nByte to the required amount of space */
|
||||
nByte = ROUND8(sizeof(IndexSample) * nNew);
|
||||
nByte += sizeof(tRowcnt) * nIdxCol * 3 * nNew;
|
||||
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
|
||||
|
||||
if( nNew==SQLITE_STAT4_EST_SAMPLES ){
|
||||
aNew = (IndexSample*)&((u8*)pIdx->pSchema->pStat4Space)[*piOff];
|
||||
*piOff += nByte;
|
||||
assert( *piOff<=sqlite3_msize(pIdx->pSchema->pStat4Space) );
|
||||
}else{
|
||||
aNew = (IndexSample*)sqlite3DbMallocRaw(db, nByte);
|
||||
if( aNew==0 ) return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
pPtr = (u8*)aNew;
|
||||
pPtr += ROUND8(nNew*sizeof(pIdx->aSample[0]));
|
||||
pSpace = (tRowcnt*)pPtr;
|
||||
|
||||
pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
|
||||
assert( EIGHT_BYTE_ALIGNMENT( pSpace ) );
|
||||
|
||||
if( pIdx->nSample ){
|
||||
/* Copy the contents of the anEq[], anLt[], anDLt[] arrays for all
|
||||
** extant samples to the new location. */
|
||||
int nByte = nIdxCol * 3 * sizeof(tRowcnt) * pIdx->nSample;
|
||||
memcpy(pSpace, pIdx->aSample[0].anEq, nByte);
|
||||
}
|
||||
for(i=0; i<nNew; i++){
|
||||
aNew[i].anEq = pSpace; pSpace += nIdxCol;
|
||||
aNew[i].anLt = pSpace; pSpace += nIdxCol;
|
||||
aNew[i].anDLt = pSpace; pSpace += nIdxCol;
|
||||
if( i<pIdx->nSample ){
|
||||
aNew[i].p = pIdx->aSample[i].p;
|
||||
aNew[i].n = pIdx->aSample[i].n;
|
||||
}
|
||||
}
|
||||
assert( ((u8*)pSpace)-nByte==(u8*)aNew );
|
||||
|
||||
if( pIdx->nSample!=SQLITE_STAT4_EST_SAMPLES ){
|
||||
sqlite3DbFree(db, pIdx->aSample);
|
||||
}
|
||||
pIdx->aSample = aNew;
|
||||
pIdx->nSampleAlloc = nNew;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate the space that will likely be required for the Index.aSample[]
|
||||
** arrays populated by loading data from the sqlite_stat4 table. Return
|
||||
** SQLITE_OK if successful, or SQLITE_NOMEM otherwise.
|
||||
*/
|
||||
static int stat4AllocSpace(sqlite3 *db, const char *zDb){
|
||||
int iDb = sqlite3FindDbName(db, zDb);
|
||||
Schema *pSchema = db->aDb[iDb].pSchema;
|
||||
int nByte = 0;
|
||||
HashElem *k;
|
||||
|
||||
assert( iDb>=0 );
|
||||
assert( pSchema->pStat4Space==0 );
|
||||
for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){
|
||||
Index *pIdx = sqliteHashData(k);
|
||||
int nIdxCol;
|
||||
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
|
||||
nIdxCol = pIdx->nKeyCol;
|
||||
}else{
|
||||
nIdxCol = pIdx->nColumn;
|
||||
}
|
||||
nByte += ROUND8(sizeof(IndexSample) * SQLITE_STAT4_EST_SAMPLES);
|
||||
nByte += sizeof(tRowcnt) * nIdxCol * 3 * SQLITE_STAT4_EST_SAMPLES;
|
||||
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
|
||||
}
|
||||
|
||||
if( nByte>0 ){
|
||||
pSchema->pStat4Space = sqlite3_malloc(nByte);
|
||||
if( pSchema->pStat4Space==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content from the sqlite_stat4 into the relevant Index.aSample[]
|
||||
** arrays.
|
||||
**
|
||||
** Arguments zSql1 and zSql2 must point to SQL statements that return
|
||||
** data equivalent to the following:
|
||||
@@ -1784,69 +1899,14 @@ static int loadStatTbl(
|
||||
char *zSql; /* Text of the SQL statement */
|
||||
Index *pPrevIdx = 0; /* Previous index in the loop */
|
||||
IndexSample *pSample; /* A slot in pIdx->aSample[] */
|
||||
int iBlockOff = 0; /* Offset into Schema.pStat4Space */
|
||||
|
||||
assert( db->lookaside.bDisable );
|
||||
zSql = sqlite3MPrintf(db, zSql1, zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( rc ) return rc;
|
||||
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
int nIdxCol = 1; /* Number of columns in stat4 records */
|
||||
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
int nSample; /* Number of samples */
|
||||
i64 nByte; /* Bytes of space required */
|
||||
i64 i; /* Bytes of space required */
|
||||
tRowcnt *pSpace; /* Available allocated memory space */
|
||||
u8 *pPtr; /* Available memory as a u8 for easier manipulation */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
nSample = sqlite3_column_int(pStmt, 1);
|
||||
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
||||
assert( pIdx==0 || pIdx->nSample==0 );
|
||||
if( pIdx==0 ) continue;
|
||||
if( pIdx->aSample!=0 ){
|
||||
/* The same index appears in sqlite_stat4 under multiple names */
|
||||
continue;
|
||||
}
|
||||
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
|
||||
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
|
||||
nIdxCol = pIdx->nKeyCol;
|
||||
}else{
|
||||
nIdxCol = pIdx->nColumn;
|
||||
}
|
||||
pIdx->nSampleCol = nIdxCol;
|
||||
pIdx->mxSample = nSample;
|
||||
nByte = ROUND8(sizeof(IndexSample) * nSample);
|
||||
nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
|
||||
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
|
||||
|
||||
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
|
||||
if( pIdx->aSample==0 ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
pPtr = (u8*)pIdx->aSample;
|
||||
pPtr += ROUND8(nSample*sizeof(pIdx->aSample[0]));
|
||||
pSpace = (tRowcnt*)pPtr;
|
||||
assert( EIGHT_BYTE_ALIGNMENT( pSpace ) );
|
||||
pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
|
||||
pIdx->pTable->tabFlags |= TF_HasStat4;
|
||||
for(i=0; i<nSample; i++){
|
||||
pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
|
||||
pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
|
||||
pIdx->aSample[i].anDLt = pSpace; pSpace += nIdxCol;
|
||||
}
|
||||
assert( ((u8*)pSpace)-nByte==(u8*)(pIdx->aSample) );
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc ) return rc;
|
||||
/* Allocate the Schema.pStat4Space block that will be used for the
|
||||
** Index.aSample[] arrays populated by this call. */
|
||||
rc = stat4AllocSpace(db, zDb);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
zSql = sqlite3MPrintf(db, zSql2, zDb);
|
||||
if( !zSql ){
|
||||
@@ -1865,18 +1925,23 @@ static int loadStatTbl(
|
||||
if( zIndex==0 ) continue;
|
||||
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
if( pIdx->nSample>=pIdx->mxSample ){
|
||||
/* Too many slots used because the same index appears in
|
||||
** sqlite_stat4 using multiple names */
|
||||
continue;
|
||||
|
||||
if( pIdx->nSample==pIdx->nSampleAlloc ){
|
||||
pIdx->pTable->tabFlags |= TF_HasStat4;
|
||||
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
|
||||
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
|
||||
pIdx->nSampleCol = pIdx->nKeyCol;
|
||||
}else{
|
||||
pIdx->nSampleCol = pIdx->nColumn;
|
||||
}
|
||||
if( growSampleArray(db, pIdx, &iBlockOff) ) break;
|
||||
}
|
||||
/* This next condition is true if data has already been loaded from
|
||||
** the sqlite_stat4 table. */
|
||||
nCol = pIdx->nSampleCol;
|
||||
|
||||
if( pIdx!=pPrevIdx ){
|
||||
initAvgEq(pPrevIdx);
|
||||
pPrevIdx = pIdx;
|
||||
}
|
||||
nCol = pIdx->nSampleCol;
|
||||
pSample = &pIdx->aSample[pIdx->nSample];
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,1),nCol,pSample->anEq,0,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0);
|
||||
@@ -1973,6 +2038,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
pIdx->aSample = 0;
|
||||
#endif
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3_free(pSchema->pStat4Space);
|
||||
pSchema->pStat4Space = 0;
|
||||
#endif
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
sInfo.db = db;
|
||||
|
Reference in New Issue
Block a user