From e1ffca3629c16a5a19eebe8f7805176ce69cbf97 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Thu, 28 Jan 2016 09:51:40 -0800 Subject: Use sync.Pool for small pages in db.allocate Benchmark results: benchmark old ns/op new ns/op delta BenchmarkDBBatchAutomatic-4 2552625 2485200 -2.64% BenchmarkDBBatchSingle-4 59632698 50757603 -14.88% BenchmarkDBBatchManual10x100-4 2564789 2452735 -4.37% benchmark old allocs new allocs delta BenchmarkDBBatchAutomatic-4 10199 10202 +0.03% BenchmarkDBBatchSingle-4 56642 56653 +0.02% BenchmarkDBBatchManual10x100-4 5986 5995 +0.15% benchmark old bytes new bytes delta BenchmarkDBBatchAutomatic-4 433587 382462 -11.79% BenchmarkDBBatchSingle-4 32504533 16308931 -49.83% BenchmarkDBBatchManual10x100-4 1362370 881765 -35.28% Signed-off-by: Alexander Morozov --- db.go | 18 ++++++++++++++++-- tx.go | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/db.go b/db.go index 0f1e1bc..5d48709 100644 --- a/db.go +++ b/db.go @@ -36,6 +36,9 @@ const ( DefaultAllocSize = 16 * 1024 * 1024 ) +// default page size for db is set to the OS page size. +var defaultPageSize = os.Getpagesize() + // DB represents a collection of buckets persisted to a file on disk. // All data access is performed through transactions which can be obtained through the DB. // All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called. @@ -320,7 +323,7 @@ func (db *DB) mmapSize(size int) (int, error) { // init creates a new database file and initializes its meta pages. func (db *DB) init() error { // Set the page size to the OS page size. - db.pageSize = os.Getpagesize() + db.pageSize = defaultPageSize // Create two meta pages on a buffer. buf := make([]byte, db.pageSize*4) @@ -779,10 +782,21 @@ func (db *DB) meta() *meta { return db.meta1 } +var pagePool = sync.Pool{ + New: func() interface{} { + return make([]byte, defaultPageSize) + }, +} + // allocate returns a contiguous block of memory starting at a given page. func (db *DB) allocate(count int) (*page, error) { // Allocate a temporary buffer for the page. - buf := make([]byte, count*db.pageSize) + var buf []byte + if count == 1 && db.pageSize == defaultPageSize { + buf = pagePool.Get().([]byte) + } else { + buf = make([]byte, count*db.pageSize) + } p := (*page)(unsafe.Pointer(&buf[0])) p.overflow = uint32(count - 1) diff --git a/tx.go b/tx.go index e74d2ca..09a39fb 100644 --- a/tx.go +++ b/tx.go @@ -441,6 +441,8 @@ func (tx *Tx) write() error { for _, p := range tx.pages { pages = append(pages, p) } + // Clear out page cache early. + tx.pages = make(map[pgid]*page) sort.Sort(pages) // Write pages to disk in order. @@ -485,8 +487,18 @@ func (tx *Tx) write() error { } } - // Clear out page cache. - tx.pages = make(map[pgid]*page) + // put small pages back to sync.Pool + for _, p := range pages { + if int(p.overflow) != 0 || tx.db.pageSize != defaultPageSize { + continue + } + buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:defaultPageSize] + // See https://go.googlesource.com/go/+/f03c9202c43e0abb130669852082117ca50aa9b1 + for i := range buf { + buf[i] = 0 + } + pagePool.Put(buf) + } return nil } -- cgit v1.2.3 From f5f0f7af77195e5339bff58ea4346aa5491835c1 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Fri, 22 Apr 2016 14:24:11 -0600 Subject: move page pool to db --- db.go | 21 ++++++++++++--------- tx.go | 12 ++++++++---- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/db.go b/db.go index 9f80681..9048f6c 100644 --- a/db.go +++ b/db.go @@ -110,6 +110,8 @@ type DB struct { freelist *freelist stats Stats + pagePool sync.Pool + batchMu sync.Mutex batch *batch @@ -209,6 +211,13 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) { } } + // Initialize page pool. + db.pagePool = sync.Pool{ + New: func() interface{} { + return make([]byte, db.pageSize) + }, + } + // Memory map the data file. if err := db.mmap(options.InitialMmapSize); err != nil { _ = db.close() @@ -324,7 +333,7 @@ func (db *DB) mmapSize(size int) (int, error) { // init creates a new database file and initializes its meta pages. func (db *DB) init() error { // Set the page size to the OS page size. - db.pageSize = defaultPageSize + db.pageSize = os.Getpagesize() // Create two meta pages on a buffer. buf := make([]byte, db.pageSize*4) @@ -787,18 +796,12 @@ func (db *DB) meta() *meta { return db.meta1 } -var pagePool = sync.Pool{ - New: func() interface{} { - return make([]byte, defaultPageSize) - }, -} - // allocate returns a contiguous block of memory starting at a given page. func (db *DB) allocate(count int) (*page, error) { // Allocate a temporary buffer for the page. var buf []byte - if count == 1 && db.pageSize == defaultPageSize { - buf = pagePool.Get().([]byte) + if count == 1 { + buf = db.pagePool.Get().([]byte) } else { buf = make([]byte, count*db.pageSize) } diff --git a/tx.go b/tx.go index 73538d9..1cfb4cd 100644 --- a/tx.go +++ b/tx.go @@ -519,17 +519,21 @@ func (tx *Tx) write() error { } } - // put small pages back to sync.Pool + // Put small pages back to page pool. for _, p := range pages { - if int(p.overflow) != 0 || tx.db.pageSize != defaultPageSize { + // Ignore page sizes over 1 page. + // These are allocated using make() instead of the page pool. + if int(p.overflow) != 0 { continue } - buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:defaultPageSize] + + buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:tx.db.pageSize] + // See https://go.googlesource.com/go/+/f03c9202c43e0abb130669852082117ca50aa9b1 for i := range buf { buf[i] = 0 } - pagePool.Put(buf) + tx.db.pagePool.Put(buf) } return nil -- cgit v1.2.3