Skip to content
This repository has been archived by the owner on Jun 19, 2023. It is now read-only.

test: add simple arc cache benchmarks #65

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 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
280 changes: 279 additions & 1 deletion arc_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ package blockstore

import (
"context"
"fmt"
"math/rand"
"testing"
"time"

blocks "github.com/ipfs/go-block-format"
cid "github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
delaystore "github.com/ipfs/go-datastore/delayed"
syncds "github.com/ipfs/go-datastore/sync"
delay "github.com/ipfs/go-ipfs-delay"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var exampleBlock = blocks.NewBlock([]byte("foo"))
Expand All @@ -26,7 +33,7 @@ func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) {
return nil, err
}

func createStores(t *testing.T) (*arccache, Blockstore, *callbackDatastore) {
func createStores(t testing.TB) (*arccache, Blockstore, *callbackDatastore) {
cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()}
bs := NewBlockstore(syncds.MutexWrap(cd))
arc, err := testArcCached(context.TODO(), bs)
Expand All @@ -36,6 +43,17 @@ func createStores(t *testing.T) (*arccache, Blockstore, *callbackDatastore) {
return arc, bs, cd
}

func createStoresWithDelay(t testing.TB, delayed delay.D) (*arccache, Blockstore, *callbackDatastore) {
cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()}
slowStore := delaystore.New(cd, delayed)
bs := NewBlockstore(syncds.MutexWrap(slowStore))
arc, err := testArcCached(context.TODO(), bs)
if err != nil {
t.Fatal(err)
}
return arc, bs, cd
}

func trap(message string, cd *callbackDatastore, t *testing.T) {
cd.SetFunc(func() {
t.Fatal(message)
Expand All @@ -45,6 +63,82 @@ func untrap(cd *callbackDatastore) {
cd.SetFunc(func() {})
}

type storeThrasher struct {
store *arccache

trace []blocks.Block

numBlocks int
numThreads int


ctx context.Context
cancel context.CancelFunc
}

func NewThrasher(store *arccache, numBlocks, numThreads int) (*storeThrasher, []blocks.Block) {
t := &storeThrasher{
numBlocks: numBlocks,
numThreads: numThreads,
store: store,
}
trace := make([]blocks.Block, t.numBlocks)
for i := 0; i < t.numBlocks; i++ {
token := make([]byte, 4)
rand.Read(token)
trace[i] = blocks.NewBlock(token)
}
t.trace = trace
t.ctx, t.cancel = context.WithCancel(context.Background())

return t, trace
}

func (t *storeThrasher) Destroy() {
t.cancel()
t.store = nil
}

func (t *storeThrasher) Start() {
for i := 0; i < t.numThreads; i++ {
go func() {
for {
select {
case <-t.ctx.Done():
return
default:
idx := rand.Intn(t.numBlocks - 1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: give each goroutine it's own rand.Source. The package level functions like rand.Intn use a shared source with a mutex.

t.store.Put(t.trace[idx])
}
}
}()

go func() {
for {
select {
case <-t.ctx.Done():
return
default:
idx := rand.Intn(t.numBlocks - 1)
t.store.Get(t.trace[idx].Cid())
}
}
}()

go func() {
for {
select {
case <-t.ctx.Done():
return
default:
idx := rand.Intn(t.numBlocks - 1)
t.store.DeleteBlock(t.trace[idx].Cid())
}
}
}()
}
}

func TestRemoveCacheEntryOnDelete(t *testing.T) {
arc, _, cd := createStores(t)

Expand Down Expand Up @@ -259,3 +353,187 @@ func TestPutManyCaches(t *testing.T) {
trap("PunMany has hit datastore", cd, t)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's not actually in the diff, but I happen to see a typo here.

(And it's the best typo.)

arc.PutMany([]blocks.Block{exampleBlock})
}

func init() {
rand.Seed(time.Now().UnixNano())
}

func Benchmark_SimplePutGet(b *testing.B) {
arc, _, _ := createStores(b)

trace := make([]blocks.Block, b.N)
for i := 0; i < b.N; i++ {
token := make([]byte, 4)
rand.Read(token)
trace[i] = blocks.NewBlock(token)
}
b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
if i%2 == 0 {
require.NoError(b, arc.Put(trace[i]))
}
}

for i := 0; i < b.N; i++ {
_, err := arc.Get(trace[i].Cid())
if i%2 == 0 {
assert.NoError(b, err)
}
}
}

func Benchmark_SimplePutDelete(b *testing.B) {
arc, _, _ := createStores(b)

trace := make([]blocks.Block, b.N)
for i := 0; i < b.N; i++ {
token := make([]byte, 4)
rand.Read(token)
trace[i] = blocks.NewBlock(token)
}
b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
require.NoError(b, arc.Put(trace[i]))
}

for i := 0; i < b.N; i++ {
err := arc.DeleteBlock(trace[i].Cid())
require.NoError(b, err)
}
}

func Benchmark_ThrashPut(b *testing.B) {
table := []struct {
numBlocks int
threads int
delay time.Duration
}{
{
numBlocks: 1_000_000,
threads: 1,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 32,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 64,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 500,
delay: time.Millisecond * 1,
},
}

for _, test := range table {
arc, _, _ := createStoresWithDelay(b, delay.Fixed(test.delay))
thrasher, trace := NewThrasher(arc, test.numBlocks, test.threads)
thrasher.Start()

b.Run(fmt.Sprintf("%d_threads-%d_blocks", test.threads, test.numBlocks), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
require.NoError(b, arc.Put(trace[i]))
}
})
thrasher.Destroy()
}
}

func Benchmark_ThrashGet(b *testing.B) {
table := []struct {
numBlocks int
threads int
delay time.Duration
}{
{
numBlocks: 1_000_000,
threads: 1,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 32,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 64,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 500,
delay: time.Millisecond * 1,
},
}

for _, test := range table {
arc, _, _ := createStoresWithDelay(b, delay.Fixed(test.delay))
thrasher, trace := NewThrasher(arc, test.numBlocks, test.threads)
thrasher.Start()

b.Run(fmt.Sprintf("%d_threads-%d_blocks", test.threads, test.numBlocks), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
arc.Get(trace[i].Cid())
}
})
thrasher.Destroy()
}
}

func Benchmark_ThrashDelete(b *testing.B) {
table := []struct {
numBlocks int
threads int
delay time.Duration
}{
{
numBlocks: 1_000_000,
threads: 1,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 32,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 64,
delay: time.Millisecond * 1,
},
{
numBlocks: 1_000_000,
threads: 500,
delay: time.Millisecond * 1,
},
}

for _, test := range table {
arc, _, _ := createStoresWithDelay(b, delay.Fixed(test.delay))
thrasher, trace := NewThrasher(arc, test.numBlocks, test.threads)
thrasher.Start()

b.Run(fmt.Sprintf("%d_threads-%d_blocks", test.threads, test.numBlocks), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
arc.DeleteBlock(trace[i].Cid())
}
})
thrasher.Destroy()
}
}
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
module github.com/ipfs/go-ipfs-blockstore

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.4
github.com/ipfs/bbloom v0.0.4
github.com/ipfs/go-block-format v0.0.2
github.com/ipfs/go-cid v0.0.5
github.com/ipfs/go-datastore v0.4.1
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8
github.com/ipfs/go-ipfs-ds-help v1.0.0
github.com/ipfs/go-ipfs-util v0.0.1
github.com/ipfs/go-log v0.0.1
github.com/ipfs/go-metrics-interface v0.0.1
github.com/multiformats/go-multihash v0.0.13
github.com/stretchr/testify v1.3.0
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
)

go 1.13
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
Expand All @@ -20,6 +22,7 @@ github.com/ipfs/go-cid v0.0.5 h1:o0Ix8e/ql7Zb5UVUJEUfjsWCIY8t48++9lR8qi6oiJU=
github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
github.com/ipfs/go-datastore v0.4.1 h1:W4ZfzyhNi3xmuU5dQhjfuRn/wFuqEE1KnOmmQiOevEY=
github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8 h1:NAviDvJ0WXgD+yiL2Rj35AmnfgI11+pHXbdciD917U0=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
github.com/ipfs/go-ipfs-ds-help v1.0.0 h1:bEQ8hMGs80h0sR8O4tfDgV6B01aaF9qeTrujrTLYV3g=
github.com/ipfs/go-ipfs-ds-help v1.0.0/go.mod h1:ujAbkeIgkKAWtxxNkoZHWLCyk5JpPoKnGyCcsoF6ueE=
Expand Down Expand Up @@ -82,6 +85,8 @@ golang.org/x/net v0.0.0-20190227160552-c95aed5357e7 h1:C2F/nMkR/9sfUTpvR3QrjBuTd
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
Expand Down