benchmarks_test.go 10.9 KB
Newer Older
1
package bitswap_test
2 3 4 5 6 7

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"math/rand"
8 9
	"os"
	"strconv"
10 11 12 13
	"sync"
	"testing"
	"time"

14
	"github.com/ipfs/go-bitswap/testutil"
15
	blocks "github.com/ipfs/go-block-format"
16

17
	bitswap "github.com/ipfs/go-bitswap"
18
	bssession "github.com/ipfs/go-bitswap/session"
19
	testinstance "github.com/ipfs/go-bitswap/testinstance"
20
	tn "github.com/ipfs/go-bitswap/testnet"
21 22 23 24 25 26
	cid "github.com/ipfs/go-cid"
	blocksutil "github.com/ipfs/go-ipfs-blocksutil"
	delay "github.com/ipfs/go-ipfs-delay"
	mockrouting "github.com/ipfs/go-ipfs-routing/mock"
)

27
type fetchFunc func(b *testing.B, bs *bitswap.Bitswap, ks []cid.Cid)
28

29
type distFunc func(b *testing.B, provs []testinstance.Instance, blocks []blocks.Block)
30 31 32 33 34 35 36 37 38 39 40

type runStats struct {
	Dups    uint64
	MsgSent uint64
	MsgRecd uint64
	Time    time.Duration
	Name    string
}

var benchmarkLog []runStats

41
func BenchmarkDups2Nodes(b *testing.B) {
42
	benchmarkLog = nil
43
	fixedDelay := delay.Fixed(10 * time.Millisecond)
44
	b.Run("AllToAll-OneAtATime", func(b *testing.B) {
45
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, allToAll, oneAtATime)
46
	})
47
	b.Run("AllToAll-BigBatch", func(b *testing.B) {
48
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, allToAll, batchFetchAll)
49 50
	})

51
	b.Run("Overlap1-OneAtATime", func(b *testing.B) {
52
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, overlap1, oneAtATime)
53 54
	})

55
	b.Run("Overlap3-OneAtATime", func(b *testing.B) {
56
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, overlap2, oneAtATime)
57
	})
58
	b.Run("Overlap3-BatchBy10", func(b *testing.B) {
59
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, overlap2, batchFetchBy10)
60
	})
61
	b.Run("Overlap3-AllConcurrent", func(b *testing.B) {
62
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, overlap2, fetchAllConcurrent)
63
	})
64
	b.Run("Overlap3-BigBatch", func(b *testing.B) {
65
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, overlap2, batchFetchAll)
66
	})
67
	b.Run("Overlap3-UnixfsFetch", func(b *testing.B) {
68
		subtestDistributeAndFetch(b, 3, 100, fixedDelay, overlap2, unixfsFileFetch)
69
	})
70
	b.Run("10Nodes-AllToAll-OneAtATime", func(b *testing.B) {
71
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, allToAll, oneAtATime)
72
	})
73
	b.Run("10Nodes-AllToAll-BatchFetchBy10", func(b *testing.B) {
74
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, allToAll, batchFetchBy10)
75
	})
76
	b.Run("10Nodes-AllToAll-BigBatch", func(b *testing.B) {
77
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, allToAll, batchFetchAll)
78
	})
79
	b.Run("10Nodes-AllToAll-AllConcurrent", func(b *testing.B) {
80
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, allToAll, fetchAllConcurrent)
81
	})
82
	b.Run("10Nodes-AllToAll-UnixfsFetch", func(b *testing.B) {
83
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, allToAll, unixfsFileFetch)
84
	})
85
	b.Run("10Nodes-OnePeerPerBlock-OneAtATime", func(b *testing.B) {
86
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, onePeerPerBlock, oneAtATime)
87
	})
88
	b.Run("10Nodes-OnePeerPerBlock-BigBatch", func(b *testing.B) {
89
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, onePeerPerBlock, batchFetchAll)
90
	})
91
	b.Run("10Nodes-OnePeerPerBlock-UnixfsFetch", func(b *testing.B) {
92
		subtestDistributeAndFetch(b, 10, 100, fixedDelay, onePeerPerBlock, unixfsFileFetch)
93
	})
94
	b.Run("200Nodes-AllToAll-BigBatch", func(b *testing.B) {
95
		subtestDistributeAndFetch(b, 200, 20, fixedDelay, allToAll, batchFetchAll)
96 97
	})
	out, _ := json.MarshalIndent(benchmarkLog, "", "  ")
Steven Allen's avatar
Steven Allen committed
98
	_ = ioutil.WriteFile("tmp/benchmark.json", out, 0666)
99 100
}

101 102 103 104 105
const fastSpeed = 60 * time.Millisecond
const mediumSpeed = 200 * time.Millisecond
const slowSpeed = 800 * time.Millisecond
const superSlowSpeed = 4000 * time.Millisecond
const distribution = 20 * time.Millisecond
106 107 108 109 110 111 112
const fastBandwidth = 1250000.0
const fastBandwidthDeviation = 300000.0
const mediumBandwidth = 500000.0
const mediumBandwidthDeviation = 80000.0
const slowBandwidth = 100000.0
const slowBandwidthDeviation = 16500.0
const stdBlockSize = 8000
113 114

func BenchmarkDupsManyNodesRealWorldNetwork(b *testing.B) {
115
	benchmarkLog = nil
116 117 118 119 120 121
	benchmarkSeed, err := strconv.ParseInt(os.Getenv("BENCHMARK_SEED"), 10, 64)
	var randomGen *rand.Rand = nil
	if err == nil {
		randomGen = rand.New(rand.NewSource(benchmarkSeed))
	}

122 123
	fastNetworkDelayGenerator := tn.InternetLatencyDelayGenerator(
		mediumSpeed-fastSpeed, slowSpeed-fastSpeed,
124
		0.0, 0.0, distribution, randomGen)
125
	fastNetworkDelay := delay.Delay(fastSpeed, fastNetworkDelayGenerator)
126
	fastBandwidthGenerator := tn.VariableRateLimitGenerator(fastBandwidth, fastBandwidthDeviation, randomGen)
127 128
	averageNetworkDelayGenerator := tn.InternetLatencyDelayGenerator(
		mediumSpeed-fastSpeed, slowSpeed-fastSpeed,
129
		0.3, 0.3, distribution, randomGen)
130
	averageNetworkDelay := delay.Delay(fastSpeed, averageNetworkDelayGenerator)
131
	averageBandwidthGenerator := tn.VariableRateLimitGenerator(mediumBandwidth, mediumBandwidthDeviation, randomGen)
132 133
	slowNetworkDelayGenerator := tn.InternetLatencyDelayGenerator(
		mediumSpeed-fastSpeed, superSlowSpeed-fastSpeed,
134
		0.3, 0.3, distribution, randomGen)
135
	slowNetworkDelay := delay.Delay(fastSpeed, slowNetworkDelayGenerator)
136
	slowBandwidthGenerator := tn.VariableRateLimitGenerator(slowBandwidth, slowBandwidthDeviation, randomGen)
137 138

	b.Run("200Nodes-AllToAll-BigBatch-FastNetwork", func(b *testing.B) {
139
		subtestDistributeAndFetchRateLimited(b, 300, 200, fastNetworkDelay, fastBandwidthGenerator, stdBlockSize, allToAll, batchFetchAll)
140 141
	})
	b.Run("200Nodes-AllToAll-BigBatch-AverageVariableSpeedNetwork", func(b *testing.B) {
142
		subtestDistributeAndFetchRateLimited(b, 300, 200, averageNetworkDelay, averageBandwidthGenerator, stdBlockSize, allToAll, batchFetchAll)
143 144
	})
	b.Run("200Nodes-AllToAll-BigBatch-SlowVariableSpeedNetwork", func(b *testing.B) {
145
		subtestDistributeAndFetchRateLimited(b, 300, 200, slowNetworkDelay, slowBandwidthGenerator, stdBlockSize, allToAll, batchFetchAll)
146
	})
147
	out, _ := json.MarshalIndent(benchmarkLog, "", "  ")
Steven Allen's avatar
Steven Allen committed
148
	_ = ioutil.WriteFile("tmp/rw-benchmark.json", out, 0666)
149 150 151
}

func subtestDistributeAndFetch(b *testing.B, numnodes, numblks int, d delay.D, df distFunc, ff fetchFunc) {
152 153 154
	for i := 0; i < b.N; i++ {
		start := time.Now()
		net := tn.VirtualNetwork(mockrouting.NewServer(), d)
155

156 157
		ig := testinstance.NewTestInstanceGenerator(net)
		defer ig.Close()
158

159
		bg := blocksutil.NewBlockGenerator()
160

161 162 163 164
		instances := ig.Instances(numnodes)
		blocks := bg.Blocks(numblks)
		runDistribution(b, instances, blocks, df, ff, start)
	}
165 166 167
}

func subtestDistributeAndFetchRateLimited(b *testing.B, numnodes, numblks int, d delay.D, rateLimitGenerator tn.RateLimitGenerator, blockSize int64, df distFunc, ff fetchFunc) {
168
	for i := 0; i < b.N; i++ {
169

170 171
		start := time.Now()
		net := tn.RateLimitedVirtualNetwork(mockrouting.NewServer(), d, rateLimitGenerator)
172

173 174
		ig := testinstance.NewTestInstanceGenerator(net)
		defer ig.Close()
175

176 177 178 179 180
		instances := ig.Instances(numnodes)
		blocks := testutil.GenerateBlocksOfSize(numblks, blockSize)

		runDistribution(b, instances, blocks, df, ff, start)
	}
181 182
}

183
func runDistribution(b *testing.B, instances []testinstance.Instance, blocks []blocks.Block, df distFunc, ff fetchFunc, start time.Time) {
184 185

	numnodes := len(instances)
186 187 188

	fetcher := instances[numnodes-1]

189
	df(b, instances[:numnodes-1], blocks)
190 191 192 193 194 195

	var ks []cid.Cid
	for _, blk := range blocks {
		ks = append(ks, blk.Cid())
	}

196
	ff(b, fetcher.Exchange, ks)
197 198 199

	st, err := fetcher.Exchange.Stat()
	if err != nil {
200
		b.Fatal(err)
201 202
	}

203
	nst := fetcher.Adapter.Stats()
204 205 206 207 208
	stats := runStats{
		Time:    time.Now().Sub(start),
		MsgRecd: nst.MessagesRecvd,
		MsgSent: nst.MessagesSent,
		Dups:    st.DupBlksReceived,
209
		Name:    b.Name(),
210 211
	}
	benchmarkLog = append(benchmarkLog, stats)
212
	b.Logf("send/recv: %d / %d", nst.MessagesSent, nst.MessagesRecvd)
213 214
}

215
func allToAll(b *testing.B, provs []testinstance.Instance, blocks []blocks.Block) {
216 217
	for _, p := range provs {
		if err := p.Blockstore().PutMany(blocks); err != nil {
218
			b.Fatal(err)
219 220 221 222 223 224
		}
	}
}

// overlap1 gives the first 75 blocks to the first peer, and the last 75 blocks
// to the second peer. This means both peers have the middle 50 blocks
225
func overlap1(b *testing.B, provs []testinstance.Instance, blks []blocks.Block) {
226
	if len(provs) != 2 {
227
		b.Fatal("overlap1 only works with 2 provs")
228 229 230 231 232
	}
	bill := provs[0]
	jeff := provs[1]

	if err := bill.Blockstore().PutMany(blks[:75]); err != nil {
233
		b.Fatal(err)
234 235
	}
	if err := jeff.Blockstore().PutMany(blks[25:]); err != nil {
236
		b.Fatal(err)
237 238 239 240 241
	}
}

// overlap2 gives every even numbered block to the first peer, odd numbered
// blocks to the second.  it also gives every third block to both peers
242
func overlap2(b *testing.B, provs []testinstance.Instance, blks []blocks.Block) {
243
	if len(provs) != 2 {
244
		b.Fatal("overlap2 only works with 2 provs")
245 246 247 248 249
	}
	bill := provs[0]
	jeff := provs[1]

	for i, blk := range blks {
250 251 252 253 254 255
		even := i%2 == 0
		third := i%3 == 0
		if third || even {
			if err := bill.Blockstore().Put(blk); err != nil {
				b.Fatal(err)
			}
256
		}
257 258 259 260
		if third || !even {
			if err := jeff.Blockstore().Put(blk); err != nil {
				b.Fatal(err)
			}
261 262 263 264 265 266 267
		}
	}
}

// onePeerPerBlock picks a random peer to hold each block
// with this layout, we shouldnt actually ever see any duplicate blocks
// but we're mostly just testing performance of the sync algorithm
268
func onePeerPerBlock(b *testing.B, provs []testinstance.Instance, blks []blocks.Block) {
269
	for _, blk := range blks {
Steven Allen's avatar
Steven Allen committed
270 271 272 273
		err := provs[rand.Intn(len(provs))].Blockstore().Put(blk)
		if err != nil {
			b.Fatal(err)
		}
274 275 276
	}
}

277
func oneAtATime(b *testing.B, bs *bitswap.Bitswap, ks []cid.Cid) {
278
	ses := bs.NewSession(context.Background()).(*bssession.Session)
279 280 281
	for _, c := range ks {
		_, err := ses.GetBlock(context.Background(), c)
		if err != nil {
282
			b.Fatal(err)
283 284
		}
	}
285
	b.Logf("Session fetch latency: %s", ses.GetAverageLatency())
286 287 288
}

// fetch data in batches, 10 at a time
289
func batchFetchBy10(b *testing.B, bs *bitswap.Bitswap, ks []cid.Cid) {
290 291 292 293
	ses := bs.NewSession(context.Background())
	for i := 0; i < len(ks); i += 10 {
		out, err := ses.GetBlocks(context.Background(), ks[i:i+10])
		if err != nil {
294
			b.Fatal(err)
295 296 297 298 299 300 301
		}
		for range out {
		}
	}
}

// fetch each block at the same time concurrently
302
func fetchAllConcurrent(b *testing.B, bs *bitswap.Bitswap, ks []cid.Cid) {
303 304 305 306 307 308 309 310 311
	ses := bs.NewSession(context.Background())

	var wg sync.WaitGroup
	for _, c := range ks {
		wg.Add(1)
		go func(c cid.Cid) {
			defer wg.Done()
			_, err := ses.GetBlock(context.Background(), c)
			if err != nil {
312
				b.Fatal(err)
313 314 315 316 317 318
			}
		}(c)
	}
	wg.Wait()
}

319
func batchFetchAll(b *testing.B, bs *bitswap.Bitswap, ks []cid.Cid) {
320 321 322
	ses := bs.NewSession(context.Background())
	out, err := ses.GetBlocks(context.Background(), ks)
	if err != nil {
323
		b.Fatal(err)
324 325 326 327 328 329
	}
	for range out {
	}
}

// simulates the fetch pattern of trying to sync a unixfs file graph as fast as possible
330
func unixfsFileFetch(b *testing.B, bs *bitswap.Bitswap, ks []cid.Cid) {
331 332 333
	ses := bs.NewSession(context.Background())
	_, err := ses.GetBlock(context.Background(), ks[0])
	if err != nil {
334
		b.Fatal(err)
335 336 337 338
	}

	out, err := ses.GetBlocks(context.Background(), ks[1:11])
	if err != nil {
339
		b.Fatal(err)
340 341 342 343 344 345
	}
	for range out {
	}

	out, err = ses.GetBlocks(context.Background(), ks[11:])
	if err != nil {
346
		b.Fatal(err)
347 348 349 350
	}
	for range out {
	}
}