Buckets must be a power of 2. Move from % to & for determining the bucket.

This commit is contained in:
Karl Seguin 2014-11-02 18:09:49 +07:00
parent 624c03cd3e
commit 5e131cc17c
7 changed files with 36 additions and 12 deletions

View file

@ -12,7 +12,7 @@ type Cache struct {
*Configuration
list *list.List
buckets []*Bucket
bucketCount uint32
bucketMask uint32
deletables chan *Item
promotables chan *Item
}
@ -21,7 +21,7 @@ func New(config *Configuration) *Cache {
c := &Cache{
list: list.New(),
Configuration: config,
bucketCount: uint32(config.buckets),
bucketMask: uint32(config.buckets) - 1,
buckets: make([]*Bucket, config.buckets),
deletables: make(chan *Item, config.deleteBuffer),
promotables: make(chan *Item, config.promoteBuffer),
@ -102,7 +102,7 @@ func (c *Cache) deleteItem(bucket *Bucket, item *Item) {
func (c *Cache) bucket(key string) *Bucket {
h := fnv.New32a()
h.Write([]byte(key))
return c.buckets[h.Sum32()%c.bucketCount]
return c.buckets[h.Sum32()&c.bucketMask]
}
func (c *Cache) conditionalPromote(item *Item) {

View file

@ -30,9 +30,12 @@ func (c *Configuration) MaxItems(max uint64) *Configuration {
}
// Keys are hashed into % bucket count to provide greater concurrency (every set
// requires a write lock on the bucket)
// [64]
// requires a write lock on the bucket). Must be a power of 2 (1, 2, 4, 8, 16, ...)
// [16]
func (c *Configuration) Buckets(count uint32) *Configuration {
if count == 0 || ((count&(^count+1)) == count) == false {
count = 16
}
c.buckets = int(count)
return c
}

23
configuration_test.go Normal file
View file

@ -0,0 +1,23 @@
package ccache
import (
. "github.com/karlseguin/expect"
"testing"
)
type ConfigurationTests struct{}
func Test_Configuration(t *testing.T) {
Expectify(new(ConfigurationTests), t)
}
func (_ *ConfigurationTests) BucketsPowerOf2() {
for i := uint32(0); i < 31; i++ {
c := Configure().Buckets(i)
if i == 1 || i == 2 || i == 4 || i == 8 || i == 16 {
Expect(c.buckets).ToEqual(int(i))
} else {
Expect(c.buckets).ToEqual(16)
}
}
}

View file

@ -6,7 +6,6 @@ import (
"time"
)
type TrackedItem interface {
Value() interface{}
Release()
@ -80,7 +79,7 @@ func (i *Item) Expired() bool {
func (i *Item) TTL() time.Duration {
expires := atomic.LoadInt64(&i.expires)
return time.Second * time.Duration(expires - time.Now().Unix())
return time.Second * time.Duration(expires-time.Now().Unix())
}
func (i *Item) Expires() time.Time {

View file

@ -40,7 +40,6 @@ func (i *ItemTests) Expires() {
Expect(item.Expires().Unix()).To.Equal(now + 10)
}
func (i *ItemTests) Extend() {
item := &Item{expires: time.Now().Unix() + 10}
item.Extend(time.Minute * 2)

View file

@ -12,7 +12,7 @@ type LayeredCache struct {
*Configuration
list *list.List
buckets []*LayeredBucket
bucketCount uint32
bucketMask uint32
deletables chan *Item
promotables chan *Item
}
@ -21,7 +21,7 @@ func Layered(config *Configuration) *LayeredCache {
c := &LayeredCache{
list: list.New(),
Configuration: config,
bucketCount: uint32(config.buckets),
bucketMask: uint32(config.buckets) - 1,
buckets: make([]*LayeredBucket, config.buckets),
deletables: make(chan *Item, config.deleteBuffer),
promotables: make(chan *Item, config.promoteBuffer),
@ -101,7 +101,7 @@ func (c *LayeredCache) Clear() {
func (c *LayeredCache) bucket(key string) *LayeredBucket {
h := fnv.New32a()
h.Write([]byte(key))
return c.buckets[h.Sum32()%c.bucketCount]
return c.buckets[h.Sum32()&c.bucketMask]
}
func (c *LayeredCache) conditionalPromote(item *Item) {

View file

@ -39,7 +39,7 @@ The most likely configuration options to tweak are:
Configurations that change the internals of the cache, which aren't as likely to need tweaking:
* `Buckets` - ccache shards its internal map to provide a greater amount of concurrency. The number of buckets is configurable (default: 16)
* `Buckets` - ccache shards its internal map to provide a greater amount of concurrency. Must be a power of 2 (default: 16).
* `PromoteBuffer(int)` - the size of the buffer to use to queue promotions (default: 1024)
* `DeleteBuffer(int)` the size of the buffer to use to queue deletions (default: 1024)