Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Benchmark MMap #22

Merged
merged 1 commit into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion mmap/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ type BucketKey[R RootKey] struct {

// String ...
func (k BucketKey[R]) String() string {
rootKey := k.RootKey.String()

var buf strings.Builder

_, _ = buf.WriteString(k.RootKey.String())
// 2 byte for size log
// 8 byte for hash value => can contain 4 byte uint32
buf.Grow(len(rootKey) + 2*len(k.Sep) + 10)

_, _ = buf.WriteString(rootKey)
_, _ = buf.WriteString(k.Sep)
_, _ = buf.WriteString(strconv.FormatInt(int64(k.SizeLog), 10))
_, _ = buf.WriteString(k.Sep)
Expand Down
7 changes: 6 additions & 1 deletion mmap/mmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import (
// RootKey constraints
type RootKey interface {
item.Key

// AvgBucketSizeLog returns the logarithm base 2 of expected average size per bucket
// values should be between [0, 8]
// value = 0 => average 1 element per bucket
// value = 3 => average 8 elements per bucket
AvgBucketSizeLog() uint8
}

Expand Down Expand Up @@ -80,7 +85,7 @@ func New[T Value, R RootKey, K Key](
}
}

// Option ...
// Option an optional value
type Option[T any] struct {
Valid bool
Data T
Expand Down
169 changes: 169 additions & 0 deletions mmap/mmap_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package mmap

import (
"context"
"encoding/binary"
"strconv"
"testing"

"github.com/spaolacci/murmur3"
"github.com/stretchr/testify/assert"

"github.com/QuangTung97/memproxy"
)

type benchValue struct {
rootKey benchRootKey
key benchKey
value int64
}

type benchRootKey struct {
value uint64
}

type benchKey struct {
value uint64
}

const benchKeyNum = 229
const benchValueNum = 331

func newBenchMapCache(pipe memproxy.Pipeline) *Map[benchValue, benchRootKey, benchKey] {
return New[benchValue, benchRootKey, benchKey](
pipe,
unmarshalBenchValue,
func(ctx context.Context, rootKey benchRootKey, hashRange HashRange) func() ([]benchValue, error) {
return func() ([]benchValue, error) {
return []benchValue{
{
rootKey: rootKey,
key: benchKey{
value: benchKeyNum,
},
value: benchValueNum,
},
}, nil
}
},
benchValue.getKey,
)
}

func doGetMapElemFromMemcache(mc memproxy.Memcache, numKeys int) {
pipe := mc.Pipeline(context.Background())
defer pipe.Finish()

mapCache := newBenchMapCache(pipe)

fnList := make([]func() (Option[benchValue], error), 0, numKeys)
for i := 0; i < numKeys; i++ {
fn := mapCache.Get(context.Background(), uint64(numKeys), benchRootKey{
value: uint64(1000 + i),
}, benchKey{
value: benchKeyNum,
})
fnList = append(fnList, fn)
}

for _, fn := range fnList {
result, err := fn()
if err != nil {
panic(err)
}
if !result.Valid {
panic("not valid")
}
if result.Data.value != benchValueNum {
panic("wrong value")
}
}
}

func Benchmark_Proxy__Map_Get_Batch_100(b *testing.B) {
mc := newMemcacheWithProxy(b)

const numKeys = 100

doGetMapElemFromMemcache(mc, numKeys)

b.ResetTimer()

for n := 0; n < b.N; n++ {
doGetMapElemFromMemcache(mc, numKeys)
}
}

func Benchmark_Proxy__Map_Get_Batch_1000(b *testing.B) {
mc := newMemcacheWithProxy(b)

const numKeys = 1000

doGetMapElemFromMemcache(mc, numKeys)

b.ResetTimer()

for n := 0; n < b.N; n++ {
doGetMapElemFromMemcache(mc, numKeys)
}
}

func (v benchValue) getKey() benchKey {
return v.key
}

func (k benchKey) Hash() uint64 {
var data [8]byte
binary.LittleEndian.PutUint64(data[:], k.value)
return murmur3.Sum64(data[:])
}

func (k benchRootKey) String() string {
return strconv.FormatUint(k.value, 10)
}

func (benchRootKey) AvgBucketSizeLog() uint8 {
return 1
}

func (v benchValue) Marshal() ([]byte, error) {
var result [24]byte
binary.LittleEndian.PutUint64(result[:], v.rootKey.value)
binary.LittleEndian.PutUint64(result[8:], v.key.value)
binary.LittleEndian.PutUint64(result[16:], uint64(v.value))
return result[:], nil
}

func unmarshalBenchValue(data []byte) (benchValue, error) {
rootKey := binary.LittleEndian.Uint64(data[:])
key := binary.LittleEndian.Uint64(data[8:])
val := binary.LittleEndian.Uint64(data[16:])

return benchValue{
rootKey: benchRootKey{
value: rootKey,
},
key: benchKey{
value: key,
},
value: int64(val),
}, nil
}

func TestMarshalBenchValue(t *testing.T) {
b := benchValue{
rootKey: benchRootKey{
value: 41,
},
key: benchKey{
value: 31,
},
value: 55,
}
data, err := b.Marshal()
assert.Equal(t, nil, err)

newVal, err := unmarshalBenchValue(data)
assert.Equal(t, nil, err)
assert.Equal(t, b, newVal)
}
6 changes: 5 additions & 1 deletion mmap/mmap_property_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ func clearMemcache() {
}
}

func newMemcacheWithProxy(t *testing.T) memproxy.Memcache {
type cleanerInterface interface {
Cleanup(fn func())
}

func newMemcacheWithProxy(t cleanerInterface) memproxy.Memcache {
clearMemcache()

server1 := proxy.SimpleServerConfig{
Expand Down
Loading