Removed old map

This commit is contained in:
Derek Collison
2012-10-30 10:20:04 -07:00
parent a246ffe1b6
commit 3600534b28
2 changed files with 0 additions and 429 deletions

View File

@@ -1,193 +0,0 @@
// Copyright 2012 Apcera Inc. All rights reserved.
// HashMap defines a high performance hashmap based on
// fast hashing and fast key comparison. Simple chaining
// is used, relying on the hashing algorithms for good
// distribution
package hashmap
import (
"bytes"
"errors"
"unsafe"
"github.com/apcera/gnatsd/hash"
)
// Entry represents what the map is actually storing.
// Uses simple linked list resolution for collisions.
type Entry struct {
hk uint32
key []byte
data interface{}
next *Entry
}
// BucketSize, must be power of 2
const _BSZ = 8
// Constants for multiples of sizeof(WORD)
const (
_WSZ = 4 // 4
_DWSZ = _WSZ << 1 // 8
)
// DefaultHash to be used unless overridden.
var DefaultHash = hash.Jesteress
// HashMap stores Entry items using a given Hash function.
// The Hash function can be overridden.
type HashMap struct {
Hash func([]byte) uint32
bkts []*Entry
msk uint32
used uint32
rsz bool
}
// Stats are reported on HashMaps
type Stats struct {
NumElements uint32
NumSlots uint32
NumBuckets uint32
LongChain uint32
AvgChain float32
}
// NewWithBkts creates a new HashMap using the bkts slice argument.
// len(bkts) must be a power of 2.
func NewWithBkts(bkts []*Entry) (*HashMap, error) {
l := len(bkts)
if l == 0 || (l&(l-1) != 0) {
return nil, errors.New("Size of buckets must be power of 2")
}
h := HashMap{}
h.msk = uint32(l - 1)
h.bkts = bkts
h.Hash = DefaultHash
h.rsz = true
return &h, nil
}
// New creates a new HashMap of default size and using the default
// Hashing algorithm.
func New() *HashMap {
h, _ := NewWithBkts(make([]*Entry, _BSZ))
return h
}
func (h *HashMap) Set(key []byte, data interface{}) {
hk := h.Hash(key)
ne := &Entry{hk: hk, key: key, data: data}
ne.next = h.bkts[hk&h.msk]
h.bkts[hk&h.msk] = ne
h.used += 1
// Check for resizing
if h.rsz && (h.used > uint32(len(h.bkts))) {
h.grow()
}
}
func (h *HashMap) Get(key []byte) interface{} {
hk := h.Hash(key)
e := h.bkts[hk&h.msk]
// FIXME: Reorder on GET if chained?
for e != nil && len(key) == len(e.key) {
// We unroll and optimize the key comparison here.
klen := len(key)
for i := 0; klen >= _DWSZ; klen -= _DWSZ {
k1 := *(*uint64)(unsafe.Pointer(&key[i]))
k2 := *(*uint64)(unsafe.Pointer(&e.key[i]))
if k1 != k2 {
goto next
}
i += _DWSZ
}
for i := 0; i < klen; i++ {
if key[i] != e.key[i] {
goto next
}
}
// Success
return e.data
next:
e = e.next
}
return nil
}
func (h *HashMap) Remove(key []byte) {
hk := h.Hash(key)
e := &h.bkts[hk&h.msk]
for *e != nil {
if len(key) == len((*e).key) && bytes.Equal(key, (*e).key) {
// Success
*e = (*e).next
h.used -= 1
// Check for resizing
lbkts := uint32(len(h.bkts))
if h.rsz && lbkts > _BSZ && (h.used < lbkts/4) {
h.shrink()
}
return
}
e = &(*e).next
}
}
// resize is responsible for reallocating the buckets and
// redistributing the hashmap entries.
func (h *HashMap) resize(nsz uint32) {
nmsk := nsz - 1
bkts := make([]*Entry, nsz)
ents := make([]Entry, h.used)
var ne *Entry
var i int
for _, e := range h.bkts {
for ; e != nil; e = e.next {
ne, i = &ents[i], i+1
*ne = *e
ne.next = bkts[e.hk&nmsk]
bkts[e.hk&nmsk] = ne
}
}
h.bkts = bkts
h.msk = nmsk
}
// grow the HashMap's buckets by 2
func (h *HashMap) grow() {
h.resize(uint32(2 * len(h.bkts)))
}
// shrink the HashMap's buckets by 2
func (h *HashMap) shrink() {
h.resize(uint32(len(h.bkts) / 2))
}
// Stats will collect general statistics about the HashMap
func (h *HashMap) Stats() *Stats {
lc, totalc, slots := 0, 0, 0
for _, e := range h.bkts {
if e != nil {
slots += 1
}
i := 0
for ; e != nil; e = e.next {
i += 1
if i > lc {
lc = i
}
}
totalc += i
}
l := uint32(len(h.bkts))
avg := (float32(totalc) / float32(slots))
return &Stats{
NumElements: h.used,
NumBuckets: l,
LongChain: uint32(lc),
AvgChain: avg,
NumSlots: uint32(slots)}
}

View File

@@ -1,236 +0,0 @@
package hashmap
import (
"bytes"
"crypto/rand"
"encoding/hex"
"io"
"testing"
)
func TestMapWithBkts(t *testing.T) {
bkts := make([]*Entry, 3, 3)
_, err := NewWithBkts(bkts)
if err == nil {
t.Fatalf("Buckets size of %d should have failed\n", len(bkts))
}
bkts = make([]*Entry, 8, 8)
_, err = NewWithBkts(bkts)
if err != nil {
t.Fatalf("Buckets size of %d should have succeeded\n", len(bkts))
}
}
var foo = []byte("foo")
var bar = []byte("bar")
var baz = []byte("baz")
var sub = []byte("apcera.continuum.router.foo.bar.baz")
func TestHashMapBasics(t *testing.T) {
h := New()
if h.used != 0 {
t.Fatalf("Wrong number of entries: %d vs 0\n", h.used)
}
h.Set(foo, bar)
if h.used != 1 {
t.Fatalf("Wrong number of entries: %d vs 1\n", h.used)
}
if v := h.Get(foo).([]byte); !bytes.Equal(v, bar) {
t.Fatalf("Did not receive correct answer: '%s' vs '%s'\n", bar, v)
}
h.Remove(foo)
if h.used != 0 {
t.Fatalf("Wrong number of entries: %d vs 0\n", h.used)
}
if v := h.Get(foo); v != nil {
t.Fatal("Did not receive correct answer, should be nil")
}
}
const (
INS = 100
EXP = 128
REM = 75
EXP2 = 64
)
func TestGrowing(t *testing.T) {
h := New()
if len(h.bkts) != _BSZ {
t.Fatalf("Initial bucket size is wrong: %d vs %d\n", len(h.bkts), _BSZ)
}
// Create _INBOX style end tokens
var toks [INS][]byte
for i, _ := range toks {
u := make([]byte, 13)
io.ReadFull(rand.Reader, u)
toks[i] = []byte(hex.EncodeToString(u))
h.Set(toks[i], toks[i])
tg := h.Get(toks[i]).([]byte)
if !bytes.Equal(tg, toks[i]) {
t.Fatalf("Did not match properly, '%s' vs '%s'\n", tg, toks[i])
}
}
if len(h.bkts) != EXP {
t.Fatalf("Expanded bucket size is wrong: %d vs %d\n", len(h.bkts), EXP)
}
}
func TestHashMapCollisions(t *testing.T) {
h := New()
h.rsz = false
// Create _INBOX style end tokens
var toks [INS][]byte
for i, _ := range toks {
u := make([]byte, 13)
io.ReadFull(rand.Reader, u)
toks[i] = []byte(hex.EncodeToString(u))
h.Set(toks[i], toks[i])
tg := h.Get(toks[i]).([]byte)
if !bytes.Equal(tg, toks[i]) {
t.Fatalf("Did not match properly, '%s' vs '%s'\n", tg, toks[i])
}
}
if len(h.bkts) != _BSZ {
t.Fatalf("Bucket size is wrong: %d vs %d\n", len(h.bkts), _BSZ)
}
h.grow()
if len(h.bkts) != 2*_BSZ {
t.Fatalf("Bucket size is wrong: %d vs %d\n", len(h.bkts), 2*_BSZ)
}
ti := 32
tg := h.Get(toks[ti]).([]byte)
if !bytes.Equal(tg, toks[ti]) {
t.Fatalf("Did not match properly, '%s' vs '%s'\n", tg, toks[ti])
}
h.Remove(toks[99])
rg := h.Get(toks[99])
if rg != nil {
t.Fatalf("After remove should have been nil! '%s'\n", rg.([]byte))
}
}
func TestHashMapStats(t *testing.T) {
h := New()
h.rsz = false
// Create _INBOX style end tokens
var toks [INS][]byte
for i, _ := range toks {
u := make([]byte, 13)
io.ReadFull(rand.Reader, u)
toks[i] = []byte(hex.EncodeToString(u))
h.Set(toks[i], toks[i])
tg := h.Get(toks[i]).([]byte)
if !bytes.Equal(tg, toks[i]) {
t.Fatalf("Did not match properly, '%s' vs '%s'\n", tg, toks[i])
}
}
s := h.Stats()
if s.NumElements != INS {
t.Fatalf("NumElements incorrect: %d vs %d\n", s.NumElements, INS)
}
if s.NumBuckets != _BSZ {
t.Fatalf("NumBuckets incorrect: %d vs %d\n", s.NumBuckets, _BSZ)
}
if s.AvgChain > 13 || s.AvgChain < 12 {
t.Fatalf("AvgChain out of bounds: %f vs %f\n", s.AvgChain, 12.5)
}
if s.LongChain > 18 {
t.Fatalf("LongChain out of bounds: %d vs %f\n", s.LongChain, 18)
}
}
func TestShrink(t *testing.T) {
h := New()
if len(h.bkts) != _BSZ {
t.Fatalf("Initial bucket size is wrong: %d vs %d\n", len(h.bkts), _BSZ)
}
// Create _INBOX style end tokens
var toks [INS][]byte
for i, _ := range toks {
u := make([]byte, 13)
io.ReadFull(rand.Reader, u)
toks[i] = []byte(hex.EncodeToString(u))
h.Set(toks[i], toks[i])
tg := h.Get(toks[i]).([]byte)
if !bytes.Equal(tg, toks[i]) {
t.Fatalf("Did not match properly, '%s' vs '%s'\n", tg, toks[i])
}
}
if len(h.bkts) != EXP {
t.Fatalf("Expanded bucket size is wrong: %d vs %d\n", len(h.bkts), EXP)
}
for i := 0; i < REM; i++ {
h.Remove(toks[i])
}
if len(h.bkts) != EXP2 {
t.Fatalf("Shrunk bucket size is wrong: %d vs %d\n", len(h.bkts), EXP2)
}
}
func Benchmark_GoMap___GetSmallKey(b *testing.B) {
b.StopTimer()
b.SetBytes(1)
m := make(map[string][]byte)
m["foo"] = bar
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = m["foo"]
}
}
func Benchmark_HashMap_GetSmallKey(b *testing.B) {
b.StopTimer()
b.SetBytes(1)
m := New()
m.Set(foo, bar)
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = m.Get(foo)
}
}
func Benchmark_GoMap____GetMedKey(b *testing.B) {
b.StopTimer()
b.SetBytes(1)
ts := string(sub)
m := make(map[string][]byte)
m[ts] = bar
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = m[ts]
}
}
func Benchmark_HashMap__GetMedKey(b *testing.B) {
b.StopTimer()
b.SetBytes(1)
m := New()
m.Set(sub, bar)
b.StartTimer()
for i := 0; i < b.N; i++ {
m.Get(sub)
}
}
func Benchmark_HashMap_______Set(b *testing.B) {
b.StopTimer()
b.SetBytes(1)
m := New()
b.StartTimer()
for i := 0; i < b.N; i++ {
m.Set(foo, bar)
}
}