mirror of https://github.com/hak5/bolt.git
Merge pull request #636 from josharian/perf
Don't allocate huge slices to merge pgids in freelist.writemaster
commit
f0cf3bfd5b
34
freelist.go
34
freelist.go
|
@ -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
31
page.go
|
@ -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
4
tx.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue