Skip to content

Commit b08e6f1

Browse files
committed
Optimization: Calculate index positions instead of sequential search based on offsets.
Optimization: Pre-set big endian format of only changed data to speed up save routine. Optimization: Stop storing offsets in mapping tables.
1 parent 9447c53 commit b08e6f1

File tree

1 file changed

+84
-69
lines changed

1 file changed

+84
-69
lines changed

ceshim.c

Lines changed: 84 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ struct ceshim_header {
5656
*/
5757
typedef struct CeshimMMTblEntry CeshimMMTblEntry;
5858
struct __attribute__ ((__packed__)) CeshimMMTblEntry {
59-
sqlite3_int64 uppOfst; // 08 upper pager offset
6059
Pgno lwrPgno; // 04 lower pager pgno where actual page map data is stored
6160
};
6261

@@ -82,7 +81,6 @@ struct CeshimMemPage {
8281
*/
8382
typedef struct ceshim_map_entry ceshim_map_entry;
8483
struct __attribute__ ((__packed__)) ceshim_map_entry {
85-
sqlite3_int64 uppOfst; // 08 upper pager offset
8684
Pgno lwrPgno; // 04 mapped lower pager pgno
8785
CeshimCmpSize cmprSz; // 02 size of compressed page
8886
CeshimCmpOfst cmprOfst; // 02 lower page offset for compressed page
@@ -114,9 +112,10 @@ struct ceshim_info {
114112
CeshimMMTblEntry *mmTbl; // The master mapping table
115113
u16 mmTblCurrIx; // Index of the current page map in mmTbl
116114
ceshim_map_entry *pPgMap; // The current page map
117-
ceshim_map_entry *pTmpPgMap; // Used for converting integers to big-endian when saving
115+
ceshim_map_entry *pBigEndianPgMap; // Used for converting integers to big-endian when saving
118116
u16 pgMapMaxCnt; // Max entries for a page map, based on page size
119117
u16 pgMapSz; // Size in bytes for the page map allocation
118+
u32 nBytesPerPgMap; // Performance optimization premultiplication store
120119

121120
// bools
122121
u8 bPgMapDirty:1; // Curr page map needs to be persisted
@@ -353,32 +352,27 @@ static int ceshimCreateMMTbl(ceshim_file *p, int *memSzOut){
353352
ceshim_info *pInfo = p->pInfo;
354353
u16 maxSz = p->pageSize - CESHIM_DB_HEADER_SIZE;
355354
u16 maxEntries = maxSz / sizeof(CeshimMMTblEntry);
356-
357-
// At this point, header has been loaded from persistent storage
355+
356+
// At this point, header may already be loaded from persistent storage
357+
// so be careful modifying header values
358358
pInfo->ceshimHeader.mmTblMaxCnt = maxEntries;
359-
// pInfo->ceshimHeader.mmTblCurrCnt = 0;
360-
pInfo->mmTblCurrIx = 0 - 1; // Use max number to mean "not defined"
361-
359+
pInfo->mmTblCurrIx = -1; // u16, so results in some large number to mean "not defined"
360+
361+
// allocate
362362
int memSz = maxEntries*sizeof(CeshimMMTblEntry);
363363
if( !(pInfo->mmTbl = sqlite3_malloc(memSz)) ) return SQLITE_NOMEM;
364+
365+
// out param
364366
if( memSzOut ) *memSzOut = memSz;
365367
return SQLITE_OK;
366368
}
367369

368370
static int ceshimSavePagemapData(ceshim_file *p){
369371
int rc = SQLITE_OK;
370372
ceshim_info *pInfo = p->pInfo;
371-
ceshim_header *header = &pInfo->ceshimHeader;
372373
if( pInfo->bPgMapDirty ){
373374
Pgno pgno = pInfo->mmTbl[pInfo->mmTblCurrIx].lwrPgno;
374-
u16 maxCnt = pInfo->mmTblCurrIx==header->mmTblCurrCnt-1 ? header->pgMapCnt : pInfo->pgMapMaxCnt;
375-
for (u16 i = 0; i<maxCnt; i++) {
376-
sqlite3PutVarint((unsigned char *)&pInfo->pTmpPgMap[i].uppOfst, (u64)pInfo->pPgMap[i].uppOfst);
377-
put4byte((u8 *)&pInfo->pTmpPgMap[i].lwrPgno, pInfo->pPgMap[i].lwrPgno);
378-
put2byte((u8 *)&pInfo->pTmpPgMap[i].cmprSz, pInfo->pPgMap[i].cmprSz);
379-
put2byte((u8 *)&pInfo->pTmpPgMap[i].cmprOfst, pInfo->pPgMap[i].cmprOfst);
380-
}
381-
rc = ceshimWriteUncompressed(p, pgno, 0, pInfo->pTmpPgMap, pInfo->pgMapSz);
375+
rc = ceshimWriteUncompressed(p, pgno, 0, pInfo->pBigEndianPgMap, pInfo->pgMapSz);
382376
if( rc==SQLITE_OK ) pInfo->bPgMapDirty = 0;
383377
}
384378
return rc;
@@ -392,7 +386,6 @@ static int ceshimSaveMMTbl(ceshim_file *p){
392386
CeshimMMTblEntry *buf = sqlite3_malloc(memSz);
393387
if( buf ){
394388
for(u16 i=0; i<header->mmTblCurrCnt; i++){
395-
sqlite3PutVarint((unsigned char *)&buf[i].uppOfst, (u64)pInfo->mmTbl[i].uppOfst);
396389
put2byte((u8 *)&buf[i].lwrPgno, pInfo->mmTbl[i].lwrPgno);
397390
}
398391
if( (rc = ceshimWriteUncompressed(p, 1, CESHIM_DB_MMTBL_OFST, buf, memSz))==SQLITE_OK){
@@ -408,16 +401,15 @@ static int ceshimLoadPagemapData(ceshim_file *p, u16 ix){
408401
ceshim_info *pInfo = p->pInfo;
409402
ceshim_header *header = &pInfo->ceshimHeader;
410403
assert( pInfo->bPgMapDirty==0 );
411-
assert( ix != pInfo->mmTblCurrIx );
404+
assert( ix != pInfo->mmTblCurrIx ); // mmTblCurrIx initially large number to mean no entries yet
412405
Pgno pgno = pInfo->mmTbl[ix].lwrPgno;
413-
rc = ceshimReadUncompressed(p, pgno, 0, pInfo->pTmpPgMap, pInfo->pgMapSz);
406+
rc = ceshimReadUncompressed(p, pgno, 0, pInfo->pBigEndianPgMap, pInfo->pgMapSz);
414407
if( rc==SQLITE_OK ){
415408
u16 maxCnt = ix==header->mmTblCurrCnt-1 ? header->pgMapCnt : pInfo->pgMapMaxCnt;
416409
for (u16 i = 0; i<maxCnt; i++) {
417-
sqlite3GetVarint((unsigned char *)&pInfo->pTmpPgMap[i].uppOfst, (u64 *)&pInfo->pPgMap[i].uppOfst);
418-
pInfo->pPgMap[i].lwrPgno = get4byte((u8 *)&pInfo->pTmpPgMap[i].lwrPgno);
419-
pInfo->pPgMap[i].cmprSz = get2byte((u8 *)&pInfo->pTmpPgMap[i].cmprSz);
420-
pInfo->pPgMap[i].cmprOfst = get2byte((u8 *)&pInfo->pTmpPgMap[i].cmprOfst);
410+
pInfo->pPgMap[i].lwrPgno = get4byte((u8 *)&pInfo->pBigEndianPgMap[i].lwrPgno);
411+
pInfo->pPgMap[i].cmprSz = get2byte((u8 *)&pInfo->pBigEndianPgMap[i].cmprSz);
412+
pInfo->pPgMap[i].cmprOfst = get2byte((u8 *)&pInfo->pBigEndianPgMap[i].cmprOfst);
421413
}
422414
pInfo->mmTblCurrIx = ix;
423415
}
@@ -426,19 +418,19 @@ static int ceshimLoadPagemapData(ceshim_file *p, u16 ix){
426418

427419
static int ceshimLoadMMTbl(ceshim_file *p){
428420
int rc;
421+
int memSz;
429422
ceshim_info *pInfo = p->pInfo;
430423
ceshim_header *header = &pInfo->ceshimHeader;
431-
// TODO: ensure header has already been loaded
432-
433-
// assert( header->tblMaxCnt>0 );
434424
assert( pInfo->mmTbl==NULL );
435-
int memSz;
425+
426+
// Header must have already been loaded
427+
assert( header->mmTblCurrCnt>0 );
428+
436429
if( (rc = ceshimCreateMMTbl(p, &memSz))==SQLITE_OK ){
437430
CeshimMMTblEntry *buf = sqlite3_malloc(memSz);
438431
if( buf ){
439432
if( (rc = ceshimReadUncompressed(p, 1, CESHIM_DB_MMTBL_OFST, buf, memSz))==SQLITE_OK){
440433
for(u16 i=0; i<header->mmTblCurrCnt; i++){
441-
sqlite3GetVarint((unsigned char *)&buf[i].uppOfst, (u64 *)&pInfo->mmTbl[i].uppOfst);
442434
pInfo->mmTbl[i].lwrPgno = get2byte((u8 *)&buf[i].lwrPgno);
443435
}
444436
sqlite3_free(buf);
@@ -463,13 +455,16 @@ static int ceshimPagerLock(ceshim_file *p){
463455
pInfo->pgMapMaxCnt = p->pageSize / sizeof(ceshim_map_entry);
464456
pInfo->pgMapSz = pInfo->pgMapMaxCnt * sizeof(ceshim_map_entry);
465457

458+
// Optimization: Do this multiplication and store it for later use.
459+
pInfo->nBytesPerPgMap = pInfo->pgMapMaxCnt * pInfo->ceshimHeader.uppPgSz;
460+
466461
/* Allocate space for a single page map.
467462
Only one page map will be in memory at a time. */
468463
pInfo->pPgMap = sqlite3_malloc(pInfo->pgMapSz);
469-
pInfo->pTmpPgMap = sqlite3_malloc(pInfo->pgMapSz);
470-
if( pInfo->pPgMap && pInfo->pTmpPgMap ){
464+
pInfo->pBigEndianPgMap = sqlite3_malloc(pInfo->pgMapSz);
465+
if( pInfo->pPgMap && pInfo->pBigEndianPgMap ){
471466
memset((void *)pInfo->pPgMap, 0, pInfo->pgMapSz);
472-
memset((void *)pInfo->pTmpPgMap, 0, pInfo->pgMapSz);
467+
memset((void *)pInfo->pBigEndianPgMap, 0, pInfo->pgMapSz);
473468
if( nPageFile==0 ){
474469
/* We will be creating a new database so set up some data that is
475470
needed right away that would be too late to do in ceshimNewDatabase(). */
@@ -597,27 +592,23 @@ static int ceshimNewDatabase(ceshim_file *pFile){
597592
}
598593

599594
/*
600-
static int ceshimCreateNewPageMap(ceshim_file *p){
601-
ceshim_info *pInfo = p->pInfo;
602-
ceshim_header *header = &pInfo->ceshimHeader;
603-
assert( pInfo->pgMap==NULL || (pInfo->pgMapMaxCnt && header->pgMapCnt==pInfo->pgMapMaxCnt) );
604-
int rc = SQLITE_OK;
605-
// first save current pagemap
606-
if( p->pInfo->pgMap ) rc = ceshimSavePagemapData(p, pInfo->nCurrPgMap);
607-
if( rc==SQLITE_OK ){
608-
pInfo->pgMap = sqlite3_malloc(pInfo->pgMapSz);
609-
if( !pInfo->pgMap ) rc = SQLITE_NOMEM;
610-
}
611-
return rc;
612-
}*/
613-
614-
static int ceshimSwitchPageMap(ceshim_file *p, sqlite_int64 iUppOfst){
595+
** Switch to a specific page map based on pager offset,
596+
** saving the current page map if needed.
597+
** @returns index# of page map switched to.
598+
*/
599+
static u16 ceshimSwitchPageMap(ceshim_file *p, sqlite_int64 iUppOfst){
615600
int rc = SQLITE_ERROR;
616601
ceshim_info *pInfo = p->pInfo;
617602
ceshim_header *header = &pInfo->ceshimHeader;
618603
u16 ix = 0;
619604

620605
// find page map
606+
#if 1
607+
ix = iUppOfst >= (pInfo->nBytesPerPgMap * header->mmTblCurrCnt)
608+
? header->mmTblCurrCnt-1
609+
: (u16)(iUppOfst / pInfo->nBytesPerPgMap);
610+
if( ix<header->mmTblCurrCnt ) rc=SQLITE_OK;
611+
#else
621612
// check last entry first (edge case)
622613
if( iUppOfst >= pInfo->mmTbl[header->mmTblCurrCnt-1].uppOfst ){
623614
ix = header->mmTblCurrCnt-1;
@@ -634,7 +625,9 @@ static int ceshimSwitchPageMap(ceshim_file *p, sqlite_int64 iUppOfst){
634625
}
635626
}
636627
}
628+
#endif
637629

630+
// switch
638631
if( rc==SQLITE_OK && ix != pInfo->mmTblCurrIx ){
639632
ceshim_printf(pInfo, "\nSwitching to map #%u for offset %lld\n", (unsigned)ix, iUppOfst);
640633
// save
@@ -646,7 +639,7 @@ static int ceshimSwitchPageMap(ceshim_file *p, sqlite_int64 iUppOfst){
646639
if( rc==SQLITE_OK ) pInfo->mmTblCurrIx = ix;
647640
}
648641
}
649-
return rc;
642+
return ix;
650643
}
651644

652645
static int ceshimPageMapGet(
@@ -661,11 +654,25 @@ static int ceshimPageMapGet(
661654
ceshim_info *pInfo = pFile->pInfo;
662655
ceshim_header *header = &pInfo->ceshimHeader;
663656
if( outUppPgno ) *outUppPgno = (Pgno)(uSrcOfst/header->uppPgSz+1);
664-
ceshimSwitchPageMap(pFile, uSrcOfst);
657+
int currPgMapNo = ceshimSwitchPageMap(pFile, uSrcOfst);
665658
if( pInfo->pPgMap ){
666659
// determine max elements based on if last page map is currently in memory
667660
u16 maxCnt = pInfo->mmTblCurrIx==header->mmTblCurrCnt-1 ? header->pgMapCnt : pInfo->pgMapMaxCnt;
668-
for(int i=0; i<maxCnt; i++){
661+
#if 1
662+
u16 pgMapIx = (u16)(uSrcOfst/pInfo->nBytesPerPgMap);
663+
int ix = uSrcOfst % pInfo->nBytesPerPgMap / header->uppPgSz;
664+
if(
665+
ix<maxCnt // if we go beyond maxCnt, entry doesn't exist yet
666+
&& pgMapIx==currPgMapNo // if pgMap not yet created, entry doesn't exist yet
667+
){
668+
if( outLwrPgno ) *outLwrPgno = pInfo->pPgMap[ix].lwrPgno;
669+
if( outCmpSz ) *outCmpSz = pInfo->pPgMap[ix].cmprSz;
670+
if( outCmpOfst ) *outCmpOfst = pInfo->pPgMap[ix].cmprOfst;
671+
if( outIx ) *outIx = ix;
672+
return SQLITE_OK;
673+
}
674+
#else
675+
for( int i=0; i<maxCnt; i++ ){
669676
if(
670677
pInfo->pPgMap[i].uppOfst <= uSrcOfst
671678
&& uSrcOfst < pInfo->pPgMap[i].uppOfst+header->uppPgSz
@@ -677,6 +684,7 @@ static int ceshimPageMapGet(
677684
return SQLITE_OK;
678685
}
679686
}
687+
#endif
680688
}
681689
return SQLITE_ERROR;
682690
}
@@ -690,11 +698,13 @@ static int ceshimPageMapGet(
690698
void ceshimAllocCmpPageSpace(
691699
ceshim_file *pFile,
692700
CeshimCmpSize cmpSz, // Current compressed size of data for allocation
693-
ceshim_map_entry *mapEntry // Existing map entry to record allocation data
701+
u16 pgMapIx // Index of map entry to record allocation data
694702
){
695703
ceshim_info *pInfo = pFile->pInfo;
696704
ceshim_header *header = &pInfo->ceshimHeader;
697705
CeshimCmpOfst ofst = header->currPageOfst;
706+
ceshim_map_entry *pMapEntry = &pInfo->pPgMap[pgMapIx];
707+
ceshim_map_entry *pBigEndianPgMapEntry = &pInfo->pBigEndianPgMap[pgMapIx];
698708
// Since we no longer write compressed pages to page 1, we can optimize this
699709
//u32 realPageSize = pFile->pageSize - (header->currPgno == 1 ? CESHIM_DB_HEADER_SIZE : 0);
700710
header->currPageOfst += cmpSz;
@@ -709,9 +719,13 @@ void ceshimAllocCmpPageSpace(
709719
else
710720
header->currPgno++;
711721
}
712-
mapEntry->lwrPgno = header->currPgno;
713-
mapEntry->cmprOfst = ofst;
714-
mapEntry->cmprSz = cmpSz;
722+
// Set data in map and in Big Endian version of map for fast save to persistent storage
723+
pMapEntry->lwrPgno = header->currPgno;
724+
pMapEntry->cmprOfst = ofst;
725+
pMapEntry->cmprSz = cmpSz;
726+
put4byte((u8 *)&pBigEndianPgMapEntry->lwrPgno, pMapEntry->lwrPgno);
727+
put2byte((u8 *)&pBigEndianPgMapEntry->cmprSz, pMapEntry->cmprSz);
728+
put2byte((u8 *)&pBigEndianPgMapEntry->cmprOfst, pMapEntry->cmprOfst);
715729
pInfo->bPgMapDirty = 1;
716730
}
717731

@@ -734,7 +748,6 @@ int ceshimAddPageEntry(
734748
return CESHIM_ERROR_PAGE_SIZE_TOO_SMALL;
735749
}
736750
CeshimMMTblEntry *entry = &pInfo->mmTbl[header->mmTblCurrCnt];
737-
entry->uppOfst = uppOfst;
738751
entry->lwrPgno = header->currPgno+1; // use next pgno but don't incr. counter!
739752
header->mmTblCurrCnt++;
740753
header->pgMapCnt = 0;
@@ -743,11 +756,11 @@ int ceshimAddPageEntry(
743756
}
744757

745758
// add new page map entry
746-
ceshim_map_entry *pPgMapEntry = &pInfo->pPgMap[header->pgMapCnt++];
747-
pPgMapEntry->uppOfst = uppOfst;
759+
u16 ix = header->pgMapCnt++;
760+
ceshim_map_entry *pPgMapEntry = &pInfo->pPgMap[ix];
748761

749762
// assign space to store compressed page
750-
ceshimAllocCmpPageSpace(pFile, cmpSz, pPgMapEntry);
763+
ceshimAllocCmpPageSpace(pFile, cmpSz, ix);
751764

752765
// for placeholder entries, set some data to zero
753766
if( !outLwrPgno ) pPgMapEntry->lwrPgno = 0;
@@ -786,7 +799,7 @@ static int ceshimPageMapSet(
786799
assert( outUppPgno );
787800
assert( outLwrPgno );
788801
assert( outCmpOfst );
789-
ceshimSwitchPageMap(pFile, uppOfst);
802+
790803
if( (rc = ceshimPageMapGet(pFile, uppOfst, outUppPgno, outLwrPgno, outCmpOfst, &oldCmpSz, &ix))==SQLITE_OK ){
791804
/*
792805
** We found a map entry. It's either a placeholder entry that need valid data,
@@ -798,21 +811,23 @@ static int ceshimPageMapSet(
798811
*/
799812
if( oldCmpSz==0 || cmpSz>oldCmpSz ){
800813
// entry found was either a placeholder or we now need more room, so allocate new space.
801-
ceshim_map_entry *mapEntry = &pInfo->pPgMap[ix];
802-
ceshimAllocCmpPageSpace(pFile, cmpSz, mapEntry);
803-
*outLwrPgno = mapEntry->lwrPgno;
804-
*outCmpOfst = mapEntry->cmprOfst;
814+
ceshim_map_entry *pMapEntry = &pInfo->pPgMap[ix];
815+
ceshimAllocCmpPageSpace(pFile, cmpSz, ix);
816+
817+
*outLwrPgno = pMapEntry->lwrPgno;
818+
*outCmpOfst = pMapEntry->cmprOfst;
805819
ceshim_printf(pInfo, "Updated placeholder entry (uppOfst=%lld, lwrPgno=%lu,cmpOfst=%lu,cmpSz=%lu) \n",
806-
(long long)mapEntry->uppOfst, (unsigned long)mapEntry->lwrPgno, (unsigned long)mapEntry->cmprOfst, (unsigned long)mapEntry->cmprSz);
820+
(long long)uppOfst, (unsigned long)pMapEntry->lwrPgno, (unsigned long)pMapEntry->cmprOfst, (unsigned long)pMapEntry->cmprSz);
807821
return SQLITE_OK;
808822
}else if( cmpSz<oldCmpSz ){
809-
// Update map entry data and keep compressed page slot. Abandoned space will be recovered via vacuum.
823+
// Update map entry data and keep compressed page slot. Abandoned space will need to be recovered via a vacuum operaion.
810824
pInfo->pPgMap[ix].cmprSz = cmpSz;
825+
put2byte((u8 *)&pInfo->pBigEndianPgMap[ix].cmprSz, cmpSz);
811826
pInfo->bPgMapDirty = 1;
812827
}
813828
return rc;
814829
}else{
815-
sqlite3_int64 nextOfst = header->pgMapCnt==0 ? 0 : pInfo->pPgMap[header->pgMapCnt-1].uppOfst + header->uppPgSz;
830+
sqlite3_int64 nextOfst = ((header->mmTblCurrCnt-1) * header->uppPgSz * pInfo->pgMapMaxCnt) + (header->pgMapCnt * header->uppPgSz);
816831
while( uppOfst>nextOfst ){
817832
ceshimAddPageEntry(pFile, nextOfst, 0, NULL, NULL);
818833
ceshim_printf(pInfo, "Added intermin entry (uppOfst=%lld, lwrPgno=0,cmpOfst=0,cmpSz=0)\n", (long long)nextOfst);
@@ -869,9 +884,9 @@ static int ceshimClose(sqlite3_file *pFile){
869884
sqlite3_free(pInfo->pPgMap);
870885
pInfo->pPgMap = NULL;
871886
}
872-
if( pInfo->pTmpPgMap ){
873-
sqlite3_free(pInfo->pTmpPgMap);
874-
pInfo->pTmpPgMap = NULL;
887+
if( pInfo->pBigEndianPgMap ){
888+
sqlite3_free(pInfo->pBigEndianPgMap);
889+
pInfo->pBigEndianPgMap = NULL;
875890
}
876891
}
877892
}

0 commit comments

Comments
 (0)