improve the sync.Pool used for pages, avoid excess page allocations for WAL

Several changes. One is, we don't provide a `New` for pagePool, which
allows allocPage to check whether a page was returned, and thus, zero
pages which were found in the pool, or make new pages, but never zero
pages it just created with make. We then also make many more things
which were making pages use the pool.

Reuse the same page allocation for multiple header pages dumped into
the WAL; the bitmap header pages aren't stashed in our page map,
they're only written to the disk, so we don't need to make a new page
each time, we can just make one new page for the whole batch.

Internally in the pool, we pool pointers to [PageSize]byte, rather
than slices. sync.Pool needs pointer-like things. To store a pointer
to a slice, you have to heap-allocate the slice, also. So, instead
of heap-allocating copies of these slices, we just use pointers to
the raw data.
This commit is contained in:
Seebs 2022-01-10 10:33:01 -06:00
parent 49972939ec
commit adcd5adb02
3 changed files with 30 additions and 18 deletions

View file

@ -526,7 +526,7 @@ func (c *Cursor) putLeafCellFast(in leafCell, isInsert bool) (err error) {
}
// Write page header.
dst := allocPage() // make([]byte, PageSize)
dst := allocPage()
writePageNo(dst, readPageNo(src))
writeFlags(dst, PageTypeLeaf)
writeCellN(dst, dstCellN)
@ -616,7 +616,7 @@ func (c *Cursor) deleteLeafCell(key uint64) (err error) {
cells = cells[:len(cells)-1]
// Write cells to page.
buf := make([]byte, PageSize)
buf := allocPage()
writePageNo(buf[:], elem.pgno)
writeFlags(buf[:], PageTypeLeaf)
writeCellN(buf[:], len(cells))
@ -800,7 +800,7 @@ func (c *Cursor) deleteBranchCell(stackIndex int, key uint64) (err error) {
return err
}
buf := make([]byte, PageSize)
buf := allocPage()
copy(buf, target)
writePageNo(buf[:], elem.pgno)

View file

@ -11,6 +11,7 @@ import (
"sort"
"sync"
"syscall"
"unsafe"
"github.com/benbjohnson/immutable"
"github.com/molecula/featurebase/v2/logger"
@ -560,7 +561,7 @@ func (db *DB) init() error {
// initMetaPage initializes the meta page.
func (db *DB) initMetaPage() error {
page := make([]byte, PageSize)
page := allocPage()
writeMetaMagic(page)
writeMetaPageN(page, 3)
writeMetaRootRecordPageNo(page, 1)
@ -572,7 +573,7 @@ func (db *DB) initMetaPage() error {
// initRootRecordPage initializes the initial root record page.
func (db *DB) initRootRecordPage() error {
page := make([]byte, PageSize)
page := allocPage()
writePageNo(page, 1)
writeFlags(page, PageTypeRootRecord)
_, err := db.file.WriteAt(page, 1*PageSize)
@ -582,7 +583,7 @@ func (db *DB) initRootRecordPage() error {
// initFreelistPage initializes the initial freelist btree page.
func (db *DB) initFreelistPage() error {
page := make([]byte, PageSize)
page := allocPage()
writePageNo(page, 2)
writeFlags(page, PageTypeLeaf)
_, err := db.file.WriteAt(page, 2*PageSize)
@ -829,18 +830,22 @@ type DebugInfo struct {
// Shared pool for in-memory database pages.
// These are used before being flushed to disk.
var pagePool = &sync.Pool{
New: func() interface{} {
page := make([]byte, PageSize)
return &page
},
}
var pagePool = &sync.Pool{}
func allocPage() []byte {
page := pagePool.Get().(*[]byte)
return *page
existing := pagePool.Get()
if existing == nil {
return make([]byte, PageSize)
}
// zero the existing page before returning it
page := existing.(*[PageSize]byte)[:]
for i := range page {
page[i] = 0
}
return page
}
func freePage(page []byte) {
pagePool.Put(&page)
data := (*[PageSize]byte)(unsafe.Pointer(&page[0]))
pagePool.Put(data)
}

View file

@ -236,7 +236,7 @@ func (tx *Tx) createBitmap(name string) error {
}
// Write root page.
page := make([]byte, PageSize)
page := allocPage()
writePageNo(page, pgno)
writeFlags(page, PageTypeLeaf)
writeCellN(page, 0)
@ -449,7 +449,7 @@ func (tx *Tx) writeRootRecordPages(records *immutable.SortedMap) (err error) {
// Write new root record pages.
for itr := records.Iterator(); !itr.Done(); {
// Initialize page & write as many records as will fit.
page := make([]byte, PageSize)
page := allocPage()
writePageNo(page, pgno)
writeFlags(page, PageTypeRootRecord)
@ -1800,9 +1800,16 @@ func (tx *Tx) flush() error {
}
// Write bitmap headers & pages to WAL.
//
// We need to write a bitmap header before each such page. We only allocate
// one header, and we reuse it, because each write is flushing it out to
// disk, and it doesn't get stored in-memory.
var hdr []byte
if len(tx.dirtyBitmapPages) > 0 {
hdr = allocPage()
}
for _, pgno := range dirtyPageMapKeys(tx.dirtyBitmapPages) {
// Write header page.
hdr := make([]byte, PageSize)
writePageNo(hdr[:], pgno)
writeFlags(hdr[:], PageTypeBitmapHeader)
if _, err := tx.writeToWAL(w, hdr); err != nil {