Merge pull request #75 from ava-labs/set-cache

pre-allocate slices in various places
This commit is contained in:
Stephen Buttolph 2020-06-16 23:04:27 -04:00 committed by GitHub
commit ea9dcec5a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 16 deletions

8
cache/lru_cache.go vendored
View File

@ -10,6 +10,10 @@ import (
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
) )
const (
minCacheSize = 32
)
type entry struct { type entry struct {
Key ids.ID Key ids.ID
Value interface{} Value interface{}
@ -59,7 +63,7 @@ func (c *LRU) Flush() {
func (c *LRU) init() { func (c *LRU) init() {
if c.entryMap == nil { if c.entryMap == nil {
c.entryMap = make(map[[32]byte]*list.Element) c.entryMap = make(map[[32]byte]*list.Element, minCacheSize)
} }
if c.entryList == nil { if c.entryList == nil {
c.entryList = list.New() c.entryList = list.New()
@ -134,6 +138,6 @@ func (c *LRU) evict(key ids.ID) {
func (c *LRU) flush() { func (c *LRU) flush() {
c.init() c.init()
c.entryMap = make(map[[32]byte]*list.Element) c.entryMap = make(map[[32]byte]*list.Element, minCacheSize)
c.entryList = list.New() c.entryList = list.New()
} }

53
cache/lru_cache_benchmark_test.go vendored Normal file
View File

@ -0,0 +1,53 @@
package cache
import (
"crypto/rand"
"testing"
"github.com/ava-labs/gecko/ids"
)
func BenchmarkLRUCachePutSmall(b *testing.B) {
smallLen := 5
cache := &LRU{Size: smallLen}
for n := 0; n < b.N; n++ {
for i := 0; i < smallLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
cache.Put(ids.NewID(idBytes), n)
}
b.StopTimer()
cache.Flush()
b.StartTimer()
}
}
func BenchmarkLRUCachePutMedium(b *testing.B) {
mediumLen := 250
cache := &LRU{Size: mediumLen}
for n := 0; n < b.N; n++ {
for i := 0; i < mediumLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
cache.Put(ids.NewID(idBytes), n)
}
b.StopTimer()
cache.Flush()
b.StartTimer()
}
}
func BenchmarkLRUCachePutLarge(b *testing.B) {
largeLen := 10000
cache := &LRU{Size: largeLen}
for n := 0; n < b.N; n++ {
for i := 0; i < largeLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
cache.Put(ids.NewID(idBytes), n)
}
b.StopTimer()
cache.Flush()
b.StartTimer()
}
}

View File

@ -8,6 +8,10 @@ import (
"strings" "strings"
) )
const (
minBagSize = 16
)
// Bag is a multiset of IDs. // Bag is a multiset of IDs.
// //
// A bag has the ability to split and filter on it's bits for ease of use for // A bag has the ability to split and filter on it's bits for ease of use for
@ -25,7 +29,7 @@ type Bag struct {
func (b *Bag) init() { func (b *Bag) init() {
if b.counts == nil { if b.counts == nil {
b.counts = make(map[[32]byte]int) b.counts = make(map[[32]byte]int, minBagSize)
} }
} }
@ -72,16 +76,21 @@ func (b *Bag) AddCount(id ID, count int) {
} }
// Count returns the number of times the id has been added. // Count returns the number of times the id has been added.
func (b *Bag) Count(id ID) int { return b.counts[*id.ID] } func (b *Bag) Count(id ID) int {
b.init()
return b.counts[*id.ID]
}
// Len returns the number of times an id has been added. // Len returns the number of times an id has been added.
func (b *Bag) Len() int { return b.size } func (b *Bag) Len() int { return b.size }
// List returns a list of all ids that have been added. // List returns a list of all ids that have been added.
func (b *Bag) List() []ID { func (b *Bag) List() []ID {
idList := []ID(nil) idList := make([]ID, len(b.counts), len(b.counts))
i := 0
for id := range b.counts { for id := range b.counts {
idList = append(idList, NewID(id)) idList[i] = NewID(id)
i++
} }
return idList return idList
} }

53
ids/bag_benchmark_test.go Normal file
View File

@ -0,0 +1,53 @@
package ids
import (
"crypto/rand"
"testing"
)
//
func BenchmarkBagListSmall(b *testing.B) {
smallLen := 5
bag := Bag{}
for i := 0; i < smallLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
NewID(idBytes)
bag.Add(NewID(idBytes))
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
bag.List()
}
}
func BenchmarkBagListMedium(b *testing.B) {
mediumLen := 25
bag := Bag{}
for i := 0; i < mediumLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
NewID(idBytes)
bag.Add(NewID(idBytes))
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
bag.List()
}
}
func BenchmarkBagListLarge(b *testing.B) {
largeLen := 100000
bag := Bag{}
for i := 0; i < largeLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
NewID(idBytes)
bag.Add(NewID(idBytes))
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
bag.List()
}
}

View File

@ -18,8 +18,8 @@ func TestBagAdd(t *testing.T) {
} else if count := bag.Count(id1); count != 0 { } else if count := bag.Count(id1); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0) t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if size := bag.Len(); size != 0 { } else if size := bag.Len(); size != 0 {
t.Fatalf("Bag.Len returned %d expected %d", count, 0) t.Fatalf("Bag.Len returned %d elements expected %d", count, 0)
} else if list := bag.List(); list != nil { } else if list := bag.List(); len(list) != 0 {
t.Fatalf("Bag.List returned %v expected %v", list, nil) t.Fatalf("Bag.List returned %v expected %v", list, nil)
} else if mode, freq := bag.Mode(); !mode.IsZero() { } else if mode, freq := bag.Mode(); !mode.IsZero() {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, ID{}) t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, ID{})

View File

@ -7,11 +7,19 @@ import (
"strings" "strings"
) )
const (
// The minimum capacity of a set
minSetSize = 16
)
// Set is a set of IDs // Set is a set of IDs
type Set map[[32]byte]bool type Set map[[32]byte]bool
func (ids *Set) init(size int) { func (ids *Set) init(size int) {
if *ids == nil { if *ids == nil {
if minSetSize > size {
size = minSetSize
}
*ids = make(map[[32]byte]bool, size) *ids = make(map[[32]byte]bool, size)
} }
} }
@ -70,9 +78,11 @@ func (ids *Set) Clear() { *ids = nil }
// List converts this set into a list // List converts this set into a list
func (ids Set) List() []ID { func (ids Set) List() []ID {
idList := []ID(nil) idList := make([]ID, ids.Len(), ids.Len())
i := 0
for id := range ids { for id := range ids {
idList = append(idList, NewID(id)) idList[i] = NewID(id)
i++
} }
return idList return idList
} }

53
ids/set_benchmark_test.go Normal file
View File

@ -0,0 +1,53 @@
package ids
import (
"crypto/rand"
"testing"
)
//
func BenchmarkSetListSmall(b *testing.B) {
smallLen := 5
set := Set{}
for i := 0; i < smallLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
NewID(idBytes)
set.Add(NewID(idBytes))
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
set.List()
}
}
func BenchmarkSetListMedium(b *testing.B) {
mediumLen := 25
set := Set{}
for i := 0; i < mediumLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
NewID(idBytes)
set.Add(NewID(idBytes))
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
set.List()
}
}
func BenchmarkSetListLarge(b *testing.B) {
largeLen := 100000
set := Set{}
for i := 0; i < largeLen; i++ {
var idBytes [32]byte
rand.Read(idBytes[:])
NewID(idBytes)
set.Add(NewID(idBytes))
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
set.List()
}
}

View File

@ -5,11 +5,18 @@ package ids
import "strings" import "strings"
const (
minShortSetSize = 16
)
// ShortSet is a set of ShortIDs // ShortSet is a set of ShortIDs
type ShortSet map[[20]byte]bool type ShortSet map[[20]byte]bool
func (ids *ShortSet) init(size int) { func (ids *ShortSet) init(size int) {
if *ids == nil { if *ids == nil {
if minShortSetSize > size {
size = minShortSetSize
}
*ids = make(map[[20]byte]bool, size) *ids = make(map[[20]byte]bool, size)
} }
} }
@ -65,9 +72,11 @@ func (ids ShortSet) CappedList(size int) []ShortID {
// List converts this set into a list // List converts this set into a list
func (ids ShortSet) List() []ShortID { func (ids ShortSet) List() []ShortID {
idList := make([]ShortID, len(ids))[:0] idList := make([]ShortID, len(ids), len(ids))
i := 0
for id := range ids { for id := range ids {
idList = append(idList, NewShortID(id)) idList[i] = NewShortID(id)
i++
} }
return idList return idList
} }

View File

@ -8,12 +8,16 @@ import (
"strings" "strings"
) )
const (
minUniqueBagSize = 16
)
// UniqueBag ... // UniqueBag ...
type UniqueBag map[[32]byte]BitSet type UniqueBag map[[32]byte]BitSet
func (b *UniqueBag) init() { func (b *UniqueBag) init() {
if *b == nil { if *b == nil {
*b = make(map[[32]byte]BitSet) *b = make(map[[32]byte]BitSet, minUniqueBagSize)
} }
} }

View File

@ -7,6 +7,10 @@ import (
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
) )
const (
minRequestsSize = 32
)
type req struct { type req struct {
vdr ids.ShortID vdr ids.ShortID
id uint32 id uint32
@ -22,7 +26,7 @@ type Requests struct {
// are only in one request at a time. // are only in one request at a time.
func (r *Requests) Add(vdr ids.ShortID, requestID uint32, containerID ids.ID) { func (r *Requests) Add(vdr ids.ShortID, requestID uint32, containerID ids.ID) {
if r.reqsToID == nil { if r.reqsToID == nil {
r.reqsToID = make(map[[20]byte]map[uint32]ids.ID) r.reqsToID = make(map[[20]byte]map[uint32]ids.ID, minRequestsSize)
} }
vdrKey := vdr.Key() vdrKey := vdr.Key()
vdrReqs, ok := r.reqsToID[vdrKey] vdrReqs, ok := r.reqsToID[vdrKey]
@ -33,7 +37,7 @@ func (r *Requests) Add(vdr ids.ShortID, requestID uint32, containerID ids.ID) {
vdrReqs[requestID] = containerID vdrReqs[requestID] = containerID
if r.idToReq == nil { if r.idToReq == nil {
r.idToReq = make(map[[32]byte]req) r.idToReq = make(map[[32]byte]req, minRequestsSize)
} }
r.idToReq[containerID.Key()] = req{ r.idToReq[containerID.Key()] = req{
vdr: vdr, vdr: vdr,

View File

@ -10,12 +10,16 @@ import (
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
) )
const (
minBlockerSize = 16
)
// Blocker tracks objects that are blocked // Blocker tracks objects that are blocked
type Blocker map[[32]byte][]Blockable type Blocker map[[32]byte][]Blockable
func (b *Blocker) init() { func (b *Blocker) init() {
if *b == nil { if *b == nil {
*b = make(map[[32]byte][]Blockable) *b = make(map[[32]byte][]Blockable, minBlockerSize)
} }
} }