Merge pull request #636 from josharian/perf

Don't allocate huge slices to merge pgids in freelist.write
master
Ben Johnson 2016-12-21 16:46:06 -07:00 committed by GitHub
commit f0cf3bfd5b
3 changed files with 49 additions and 20 deletions

View File

@ -46,16 +46,24 @@ func (f *freelist) pending_count() int {
return count return count
} }
// all returns a list of all free ids and all pending ids in one sorted list. // lenall returns the combined number of all free ids and all pending ids.
func (f *freelist) all() []pgid { func (f *freelist) lenall() int {
m := make(pgids, 0) n := len(f.ids)
for _, list := range f.pending {
n += len(list)
}
return n
}
// all copies into dst a list of all free ids and all pending ids in one sorted list.
// f.lenall returns the minimum length required for dst.
func (f *freelist) copyall(dst []pgid) {
m := make(pgids, 0, len(f.pending)) // len(f.pending) undercounts, but it is a start
for _, list := range f.pending { for _, list := range f.pending {
m = append(m, list...) m = append(m, list...)
} }
sort.Sort(m) sort.Sort(m)
return pgids(f.ids).merge(m) mergepgids(dst, f.ids, m)
} }
// allocate returns the starting page id of a contiguous list of pages of a given size. // allocate returns the starting page id of a contiguous list of pages of a given size.
@ -186,22 +194,22 @@ func (f *freelist) read(p *page) {
// become free. // become free.
func (f *freelist) write(p *page) error { func (f *freelist) write(p *page) error {
// Combine the old free pgids and pgids waiting on an open transaction. // Combine the old free pgids and pgids waiting on an open transaction.
ids := f.all()
// Update the header flag. // Update the header flag.
p.flags |= freelistPageFlag p.flags |= freelistPageFlag
// The page.count can only hold up to 64k elements so if we overflow that // The page.count can only hold up to 64k elements so if we overflow that
// number then we handle it by putting the size in the first element. // number then we handle it by putting the size in the first element.
if len(ids) == 0 { lenids := f.lenall()
p.count = uint16(len(ids)) if lenids == 0 {
} else if len(ids) < 0xFFFF { p.count = uint16(lenids)
p.count = uint16(len(ids)) } else if lenids < 0xFFFF {
copy(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[:], ids) p.count = uint16(lenids)
f.copyall(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[:])
} else { } else {
p.count = 0xFFFF p.count = 0xFFFF
((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[0] = pgid(len(ids)) ((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[0] = pgid(lenids)
copy(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[1:], ids) f.copyall(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[1:])
} }
return nil return nil

31
page.go
View File

@ -145,12 +145,33 @@ func (a pgids) merge(b pgids) pgids {
// Return the opposite slice if one is nil. // Return the opposite slice if one is nil.
if len(a) == 0 { if len(a) == 0 {
return b return b
} else if len(b) == 0 { }
if len(b) == 0 {
return a return a
} }
merged := make(pgids, len(a)+len(b))
mergepgids(merged, a, b)
return merged
}
// Create a list to hold all elements from both lists. // merge copies the sorted union of a and b into dst.
merged := make(pgids, 0, len(a)+len(b)) // If dst is too small, it panics.
func mergepgids(dst, a, b pgids) {
if len(dst) < len(a)+len(b) {
panic(fmt.Errorf("mergepgids bad len %d < %d + %d", len(dst), len(a), len(b)))
}
// Copy in the opposite slice if one is nil.
if len(a) == 0 {
copy(dst, b)
return
}
if len(b) == 0 {
copy(dst, a)
return
}
// Merged will hold all elements from both lists.
merged := dst[:0]
// Assign lead to the slice with a lower starting value, follow to the higher value. // Assign lead to the slice with a lower starting value, follow to the higher value.
lead, follow := a, b lead, follow := a, b
@ -172,7 +193,5 @@ func (a pgids) merge(b pgids) pgids {
} }
// Append what's left in follow. // Append what's left in follow.
merged = append(merged, follow...) _ = append(merged, follow...)
return merged
} }

4
tx.go
View File

@ -381,7 +381,9 @@ func (tx *Tx) Check() <-chan error {
func (tx *Tx) check(ch chan error) { func (tx *Tx) check(ch chan error) {
// Check if any pages are double freed. // Check if any pages are double freed.
freed := make(map[pgid]bool) freed := make(map[pgid]bool)
for _, id := range tx.db.freelist.all() { all := make([]pgid, tx.db.freelist.lenall())
tx.db.freelist.copyall(all)
for _, id := range all {
if freed[id] { if freed[id] {
ch <- fmt.Errorf("page %d: already freed", id) ch <- fmt.Errorf("page %d: already freed", id)
} }