github.com/janotchain/janota@v0.0.0-20220824112012-93ea4c5dee78/swarm/storage/chunker_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "bytes" 21 "crypto/rand" 22 "encoding/binary" 23 "errors" 24 "fmt" 25 "io" 26 "sync" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/crypto/sha3" 31 ) 32 33 /* 34 Tests TreeChunker by splitting and joining a random byte slice 35 */ 36 37 type test interface { 38 Fatalf(string, ...interface{}) 39 Logf(string, ...interface{}) 40 } 41 42 type chunkerTester struct { 43 inputs map[uint64][]byte 44 chunks map[string]*Chunk 45 t test 46 } 47 48 func (self *chunkerTester) Split(chunker Splitter, data io.Reader, size int64, chunkC chan *Chunk, swg *sync.WaitGroup, expectedError error) (key Key, err error) { 49 // reset 50 self.chunks = make(map[string]*Chunk) 51 52 if self.inputs == nil { 53 self.inputs = make(map[uint64][]byte) 54 } 55 56 quitC := make(chan bool) 57 timeout := time.After(600 * time.Second) 58 if chunkC != nil { 59 go func() error { 60 for { 61 select { 62 case <-timeout: 63 return errors.New(("Split timeout error")) 64 case <-quitC: 65 return nil 66 case chunk := <-chunkC: 67 // self.chunks = append(self.chunks, chunk) 68 self.chunks[chunk.Key.String()] = chunk 69 if chunk.wg != nil { 70 chunk.wg.Done() 71 } 72 } 73 74 } 75 }() 76 } 77 78 key, err = chunker.Split(data, size, chunkC, swg, nil) 79 if err != nil && expectedError == nil { 80 err = errors.New(fmt.Sprintf("Split error: %v", err)) 81 } 82 83 if chunkC != nil { 84 if swg != nil { 85 swg.Wait() 86 } 87 close(quitC) 88 } 89 return key, err 90 } 91 92 func (self *chunkerTester) Append(chunker Splitter, rootKey Key, data io.Reader, chunkC chan *Chunk, swg *sync.WaitGroup, expectedError error) (key Key, err error) { 93 quitC := make(chan bool) 94 timeout := time.After(60 * time.Second) 95 if chunkC != nil { 96 go func() error { 97 for { 98 select { 99 case <-timeout: 100 return errors.New(("Append timeout error")) 101 case <-quitC: 102 return nil 103 case chunk := <-chunkC: 104 if chunk != nil { 105 stored, success := self.chunks[chunk.Key.String()] 106 if !success { 107 // Requesting data 108 self.chunks[chunk.Key.String()] = chunk 109 if chunk.wg != nil { 110 chunk.wg.Done() 111 } 112 } else { 113 // getting data 114 chunk.SData = stored.SData 115 chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 116 close(chunk.C) 117 } 118 } 119 } 120 } 121 }() 122 } 123 124 key, err = chunker.Append(rootKey, data, chunkC, swg, nil) 125 if err != nil && expectedError == nil { 126 err = errors.New(fmt.Sprintf("Append error: %v", err)) 127 } 128 129 if chunkC != nil { 130 if swg != nil { 131 swg.Wait() 132 } 133 close(quitC) 134 } 135 return key, err 136 } 137 138 func (self *chunkerTester) Join(chunker Chunker, key Key, c int, chunkC chan *Chunk, quitC chan bool) LazySectionReader { 139 // reset but not the chunks 140 141 reader := chunker.Join(key, chunkC) 142 143 timeout := time.After(600 * time.Second) 144 i := 0 145 go func() error { 146 for { 147 select { 148 case <-timeout: 149 return errors.New(("Join timeout error")) 150 case chunk, ok := <-chunkC: 151 if !ok { 152 close(quitC) 153 return nil 154 } 155 // this just mocks the behaviour of a chunk store retrieval 156 stored, success := self.chunks[chunk.Key.String()] 157 if !success { 158 return errors.New(("Not found")) 159 } 160 chunk.SData = stored.SData 161 chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 162 close(chunk.C) 163 i++ 164 } 165 } 166 }() 167 return reader 168 } 169 170 func testRandomBrokenData(splitter Splitter, n int, tester *chunkerTester) { 171 data := io.LimitReader(rand.Reader, int64(n)) 172 brokendata := brokenLimitReader(data, n, n/2) 173 174 buf := make([]byte, n) 175 _, err := brokendata.Read(buf) 176 if err == nil || err.Error() != "Broken reader" { 177 tester.t.Fatalf("Broken reader is not broken, hence broken. Returns: %v", err) 178 } 179 180 data = io.LimitReader(rand.Reader, int64(n)) 181 brokendata = brokenLimitReader(data, n, n/2) 182 183 chunkC := make(chan *Chunk, 1000) 184 swg := &sync.WaitGroup{} 185 186 expectedError := fmt.Errorf("Broken reader") 187 key, err := tester.Split(splitter, brokendata, int64(n), chunkC, swg, expectedError) 188 if err == nil || err.Error() != expectedError.Error() { 189 tester.t.Fatalf("Not receiving the correct error! Expected %v, received %v", expectedError, err) 190 } 191 tester.t.Logf(" Key = %v\n", key) 192 } 193 194 func testRandomData(splitter Splitter, n int, tester *chunkerTester) Key { 195 if tester.inputs == nil { 196 tester.inputs = make(map[uint64][]byte) 197 } 198 input, found := tester.inputs[uint64(n)] 199 var data io.Reader 200 if !found { 201 data, input = testDataReaderAndSlice(n) 202 tester.inputs[uint64(n)] = input 203 } else { 204 data = io.LimitReader(bytes.NewReader(input), int64(n)) 205 } 206 207 chunkC := make(chan *Chunk, 1000) 208 swg := &sync.WaitGroup{} 209 210 key, err := tester.Split(splitter, data, int64(n), chunkC, swg, nil) 211 if err != nil { 212 tester.t.Fatalf(err.Error()) 213 } 214 tester.t.Logf(" Key = %v\n", key) 215 216 chunkC = make(chan *Chunk, 1000) 217 quitC := make(chan bool) 218 219 chunker := NewTreeChunker(NewChunkerParams()) 220 reader := tester.Join(chunker, key, 0, chunkC, quitC) 221 output := make([]byte, n) 222 r, err := reader.Read(output) 223 if r != n || err != io.EOF { 224 tester.t.Fatalf("read error read: %v n = %v err = %v\n", r, n, err) 225 } 226 if input != nil { 227 if !bytes.Equal(output, input) { 228 tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", input, output) 229 } 230 } 231 close(chunkC) 232 <-quitC 233 234 return key 235 } 236 237 func testRandomDataAppend(splitter Splitter, n, m int, tester *chunkerTester) { 238 if tester.inputs == nil { 239 tester.inputs = make(map[uint64][]byte) 240 } 241 input, found := tester.inputs[uint64(n)] 242 var data io.Reader 243 if !found { 244 data, input = testDataReaderAndSlice(n) 245 tester.inputs[uint64(n)] = input 246 } else { 247 data = io.LimitReader(bytes.NewReader(input), int64(n)) 248 } 249 250 chunkC := make(chan *Chunk, 1000) 251 swg := &sync.WaitGroup{} 252 253 key, err := tester.Split(splitter, data, int64(n), chunkC, swg, nil) 254 if err != nil { 255 tester.t.Fatalf(err.Error()) 256 } 257 tester.t.Logf(" Key = %v\n", key) 258 259 //create a append data stream 260 appendInput, found := tester.inputs[uint64(m)] 261 var appendData io.Reader 262 if !found { 263 appendData, appendInput = testDataReaderAndSlice(m) 264 tester.inputs[uint64(m)] = appendInput 265 } else { 266 appendData = io.LimitReader(bytes.NewReader(appendInput), int64(m)) 267 } 268 269 chunkC = make(chan *Chunk, 1000) 270 swg = &sync.WaitGroup{} 271 272 newKey, err := tester.Append(splitter, key, appendData, chunkC, swg, nil) 273 if err != nil { 274 tester.t.Fatalf(err.Error()) 275 } 276 tester.t.Logf(" NewKey = %v\n", newKey) 277 278 chunkC = make(chan *Chunk, 1000) 279 quitC := make(chan bool) 280 281 chunker := NewTreeChunker(NewChunkerParams()) 282 reader := tester.Join(chunker, newKey, 0, chunkC, quitC) 283 newOutput := make([]byte, n+m) 284 r, err := reader.Read(newOutput) 285 if r != (n + m) { 286 tester.t.Fatalf("read error read: %v n = %v err = %v\n", r, n, err) 287 } 288 289 newInput := append(input, appendInput...) 290 if !bytes.Equal(newOutput, newInput) { 291 tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", newInput, newOutput) 292 } 293 294 close(chunkC) 295 } 296 297 func TestSha3ForCorrectness(t *testing.T) { 298 tester := &chunkerTester{t: t} 299 300 size := 4096 301 input := make([]byte, size+8) 302 binary.LittleEndian.PutUint64(input[:8], uint64(size)) 303 304 io.LimitReader(bytes.NewReader(input[8:]), int64(size)) 305 306 rawSha3 := sha3.NewKeccak256() 307 rawSha3.Reset() 308 rawSha3.Write(input) 309 rawSha3Output := rawSha3.Sum(nil) 310 311 sha3FromMakeFunc := MakeHashFunc(SHA3Hash)() 312 sha3FromMakeFunc.ResetWithLength(input[:8]) 313 sha3FromMakeFunc.Write(input[8:]) 314 sha3FromMakeFuncOutput := sha3FromMakeFunc.Sum(nil) 315 316 if len(rawSha3Output) != len(sha3FromMakeFuncOutput) { 317 tester.t.Fatalf("Original SHA3 and abstracted Sha3 has different length %v:%v\n", len(rawSha3Output), len(sha3FromMakeFuncOutput)) 318 } 319 320 if !bytes.Equal(rawSha3Output, sha3FromMakeFuncOutput) { 321 tester.t.Fatalf("Original SHA3 and abstracted Sha3 mismatch %v:%v\n", rawSha3Output, sha3FromMakeFuncOutput) 322 } 323 324 } 325 326 func TestDataAppend(t *testing.T) { 327 sizes := []int{1, 1, 1, 4095, 4096, 4097, 1, 1, 1, 123456, 2345678, 2345678} 328 appendSizes := []int{4095, 4096, 4097, 1, 1, 1, 8191, 8192, 8193, 9000, 3000, 5000} 329 330 tester := &chunkerTester{t: t} 331 chunker := NewPyramidChunker(NewChunkerParams()) 332 for i, s := range sizes { 333 testRandomDataAppend(chunker, s, appendSizes[i], tester) 334 335 } 336 } 337 338 func TestRandomData(t *testing.T) { 339 sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678} 340 tester := &chunkerTester{t: t} 341 342 chunker := NewTreeChunker(NewChunkerParams()) 343 pyramid := NewPyramidChunker(NewChunkerParams()) 344 for _, s := range sizes { 345 treeChunkerKey := testRandomData(chunker, s, tester) 346 pyramidChunkerKey := testRandomData(pyramid, s, tester) 347 if treeChunkerKey.String() != pyramidChunkerKey.String() { 348 tester.t.Fatalf("tree chunker and pyramid chunker key mismatch for size %v\n TC: %v\n PC: %v\n", s, treeChunkerKey.String(), pyramidChunkerKey.String()) 349 } 350 } 351 352 cp := NewChunkerParams() 353 cp.Hash = BMTHash 354 chunker = NewTreeChunker(cp) 355 pyramid = NewPyramidChunker(cp) 356 for _, s := range sizes { 357 treeChunkerKey := testRandomData(chunker, s, tester) 358 pyramidChunkerKey := testRandomData(pyramid, s, tester) 359 if treeChunkerKey.String() != pyramidChunkerKey.String() { 360 tester.t.Fatalf("tree chunker BMT and pyramid chunker BMT key mismatch for size %v \n TC: %v\n PC: %v\n", s, treeChunkerKey.String(), pyramidChunkerKey.String()) 361 } 362 } 363 364 } 365 366 func TestRandomBrokenData(t *testing.T) { 367 sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678} 368 tester := &chunkerTester{t: t} 369 chunker := NewTreeChunker(NewChunkerParams()) 370 for _, s := range sizes { 371 testRandomBrokenData(chunker, s, tester) 372 } 373 } 374 375 func benchReadAll(reader LazySectionReader) { 376 size, _ := reader.Size(nil) 377 output := make([]byte, 1000) 378 for pos := int64(0); pos < size; pos += 1000 { 379 reader.ReadAt(output, pos) 380 } 381 } 382 383 func benchmarkJoin(n int, t *testing.B) { 384 t.ReportAllocs() 385 for i := 0; i < t.N; i++ { 386 chunker := NewTreeChunker(NewChunkerParams()) 387 tester := &chunkerTester{t: t} 388 data := testDataReader(n) 389 390 chunkC := make(chan *Chunk, 1000) 391 swg := &sync.WaitGroup{} 392 393 key, err := tester.Split(chunker, data, int64(n), chunkC, swg, nil) 394 if err != nil { 395 tester.t.Fatalf(err.Error()) 396 } 397 chunkC = make(chan *Chunk, 1000) 398 quitC := make(chan bool) 399 reader := tester.Join(chunker, key, i, chunkC, quitC) 400 benchReadAll(reader) 401 close(chunkC) 402 <-quitC 403 } 404 } 405 406 func benchmarkSplitTreeSHA3(n int, t *testing.B) { 407 t.ReportAllocs() 408 for i := 0; i < t.N; i++ { 409 chunker := NewTreeChunker(NewChunkerParams()) 410 tester := &chunkerTester{t: t} 411 data := testDataReader(n) 412 _, err := tester.Split(chunker, data, int64(n), nil, nil, nil) 413 if err != nil { 414 tester.t.Fatalf(err.Error()) 415 } 416 } 417 } 418 419 func benchmarkSplitTreeBMT(n int, t *testing.B) { 420 t.ReportAllocs() 421 for i := 0; i < t.N; i++ { 422 cp := NewChunkerParams() 423 cp.Hash = BMTHash 424 chunker := NewTreeChunker(cp) 425 tester := &chunkerTester{t: t} 426 data := testDataReader(n) 427 _, err := tester.Split(chunker, data, int64(n), nil, nil, nil) 428 if err != nil { 429 tester.t.Fatalf(err.Error()) 430 } 431 } 432 } 433 434 func benchmarkSplitPyramidSHA3(n int, t *testing.B) { 435 t.ReportAllocs() 436 for i := 0; i < t.N; i++ { 437 splitter := NewPyramidChunker(NewChunkerParams()) 438 tester := &chunkerTester{t: t} 439 data := testDataReader(n) 440 _, err := tester.Split(splitter, data, int64(n), nil, nil, nil) 441 if err != nil { 442 tester.t.Fatalf(err.Error()) 443 } 444 } 445 } 446 447 func benchmarkSplitPyramidBMT(n int, t *testing.B) { 448 t.ReportAllocs() 449 for i := 0; i < t.N; i++ { 450 cp := NewChunkerParams() 451 cp.Hash = BMTHash 452 splitter := NewPyramidChunker(cp) 453 tester := &chunkerTester{t: t} 454 data := testDataReader(n) 455 _, err := tester.Split(splitter, data, int64(n), nil, nil, nil) 456 if err != nil { 457 tester.t.Fatalf(err.Error()) 458 } 459 } 460 } 461 462 func benchmarkAppendPyramid(n, m int, t *testing.B) { 463 t.ReportAllocs() 464 for i := 0; i < t.N; i++ { 465 chunker := NewPyramidChunker(NewChunkerParams()) 466 tester := &chunkerTester{t: t} 467 data := testDataReader(n) 468 data1 := testDataReader(m) 469 470 chunkC := make(chan *Chunk, 1000) 471 swg := &sync.WaitGroup{} 472 key, err := tester.Split(chunker, data, int64(n), chunkC, swg, nil) 473 if err != nil { 474 tester.t.Fatalf(err.Error()) 475 } 476 477 chunkC = make(chan *Chunk, 1000) 478 swg = &sync.WaitGroup{} 479 480 _, err = tester.Append(chunker, key, data1, chunkC, swg, nil) 481 if err != nil { 482 tester.t.Fatalf(err.Error()) 483 } 484 485 close(chunkC) 486 } 487 } 488 489 func BenchmarkJoin_2(t *testing.B) { benchmarkJoin(100, t) } 490 func BenchmarkJoin_3(t *testing.B) { benchmarkJoin(1000, t) } 491 func BenchmarkJoin_4(t *testing.B) { benchmarkJoin(10000, t) } 492 func BenchmarkJoin_5(t *testing.B) { benchmarkJoin(100000, t) } 493 func BenchmarkJoin_6(t *testing.B) { benchmarkJoin(1000000, t) } 494 func BenchmarkJoin_7(t *testing.B) { benchmarkJoin(10000000, t) } 495 func BenchmarkJoin_8(t *testing.B) { benchmarkJoin(100000000, t) } 496 497 func BenchmarkSplitTreeSHA3_2(t *testing.B) { benchmarkSplitTreeSHA3(100, t) } 498 func BenchmarkSplitTreeSHA3_2h(t *testing.B) { benchmarkSplitTreeSHA3(500, t) } 499 func BenchmarkSplitTreeSHA3_3(t *testing.B) { benchmarkSplitTreeSHA3(1000, t) } 500 func BenchmarkSplitTreeSHA3_3h(t *testing.B) { benchmarkSplitTreeSHA3(5000, t) } 501 func BenchmarkSplitTreeSHA3_4(t *testing.B) { benchmarkSplitTreeSHA3(10000, t) } 502 func BenchmarkSplitTreeSHA3_4h(t *testing.B) { benchmarkSplitTreeSHA3(50000, t) } 503 func BenchmarkSplitTreeSHA3_5(t *testing.B) { benchmarkSplitTreeSHA3(100000, t) } 504 func BenchmarkSplitTreeSHA3_6(t *testing.B) { benchmarkSplitTreeSHA3(1000000, t) } 505 func BenchmarkSplitTreeSHA3_7(t *testing.B) { benchmarkSplitTreeSHA3(10000000, t) } 506 func BenchmarkSplitTreeSHA3_8(t *testing.B) { benchmarkSplitTreeSHA3(100000000, t) } 507 508 func BenchmarkSplitTreeBMT_2(t *testing.B) { benchmarkSplitTreeBMT(100, t) } 509 func BenchmarkSplitTreeBMT_2h(t *testing.B) { benchmarkSplitTreeBMT(500, t) } 510 func BenchmarkSplitTreeBMT_3(t *testing.B) { benchmarkSplitTreeBMT(1000, t) } 511 func BenchmarkSplitTreeBMT_3h(t *testing.B) { benchmarkSplitTreeBMT(5000, t) } 512 func BenchmarkSplitTreeBMT_4(t *testing.B) { benchmarkSplitTreeBMT(10000, t) } 513 func BenchmarkSplitTreeBMT_4h(t *testing.B) { benchmarkSplitTreeBMT(50000, t) } 514 func BenchmarkSplitTreeBMT_5(t *testing.B) { benchmarkSplitTreeBMT(100000, t) } 515 func BenchmarkSplitTreeBMT_6(t *testing.B) { benchmarkSplitTreeBMT(1000000, t) } 516 func BenchmarkSplitTreeBMT_7(t *testing.B) { benchmarkSplitTreeBMT(10000000, t) } 517 func BenchmarkSplitTreeBMT_8(t *testing.B) { benchmarkSplitTreeBMT(100000000, t) } 518 519 func BenchmarkSplitPyramidSHA3_2(t *testing.B) { benchmarkSplitPyramidSHA3(100, t) } 520 func BenchmarkSplitPyramidSHA3_2h(t *testing.B) { benchmarkSplitPyramidSHA3(500, t) } 521 func BenchmarkSplitPyramidSHA3_3(t *testing.B) { benchmarkSplitPyramidSHA3(1000, t) } 522 func BenchmarkSplitPyramidSHA3_3h(t *testing.B) { benchmarkSplitPyramidSHA3(5000, t) } 523 func BenchmarkSplitPyramidSHA3_4(t *testing.B) { benchmarkSplitPyramidSHA3(10000, t) } 524 func BenchmarkSplitPyramidSHA3_4h(t *testing.B) { benchmarkSplitPyramidSHA3(50000, t) } 525 func BenchmarkSplitPyramidSHA3_5(t *testing.B) { benchmarkSplitPyramidSHA3(100000, t) } 526 func BenchmarkSplitPyramidSHA3_6(t *testing.B) { benchmarkSplitPyramidSHA3(1000000, t) } 527 func BenchmarkSplitPyramidSHA3_7(t *testing.B) { benchmarkSplitPyramidSHA3(10000000, t) } 528 func BenchmarkSplitPyramidSHA3_8(t *testing.B) { benchmarkSplitPyramidSHA3(100000000, t) } 529 530 func BenchmarkSplitPyramidBMT_2(t *testing.B) { benchmarkSplitPyramidBMT(100, t) } 531 func BenchmarkSplitPyramidBMT_2h(t *testing.B) { benchmarkSplitPyramidBMT(500, t) } 532 func BenchmarkSplitPyramidBMT_3(t *testing.B) { benchmarkSplitPyramidBMT(1000, t) } 533 func BenchmarkSplitPyramidBMT_3h(t *testing.B) { benchmarkSplitPyramidBMT(5000, t) } 534 func BenchmarkSplitPyramidBMT_4(t *testing.B) { benchmarkSplitPyramidBMT(10000, t) } 535 func BenchmarkSplitPyramidBMT_4h(t *testing.B) { benchmarkSplitPyramidBMT(50000, t) } 536 func BenchmarkSplitPyramidBMT_5(t *testing.B) { benchmarkSplitPyramidBMT(100000, t) } 537 func BenchmarkSplitPyramidBMT_6(t *testing.B) { benchmarkSplitPyramidBMT(1000000, t) } 538 func BenchmarkSplitPyramidBMT_7(t *testing.B) { benchmarkSplitPyramidBMT(10000000, t) } 539 func BenchmarkSplitPyramidBMT_8(t *testing.B) { benchmarkSplitPyramidBMT(100000000, t) } 540 541 func BenchmarkAppendPyramid_2(t *testing.B) { benchmarkAppendPyramid(100, 1000, t) } 542 func BenchmarkAppendPyramid_2h(t *testing.B) { benchmarkAppendPyramid(500, 1000, t) } 543 func BenchmarkAppendPyramid_3(t *testing.B) { benchmarkAppendPyramid(1000, 1000, t) } 544 func BenchmarkAppendPyramid_4(t *testing.B) { benchmarkAppendPyramid(10000, 1000, t) } 545 func BenchmarkAppendPyramid_4h(t *testing.B) { benchmarkAppendPyramid(50000, 1000, t) } 546 func BenchmarkAppendPyramid_5(t *testing.B) { benchmarkAppendPyramid(1000000, 1000, t) } 547 func BenchmarkAppendPyramid_6(t *testing.B) { benchmarkAppendPyramid(1000000, 1000, t) } 548 func BenchmarkAppendPyramid_7(t *testing.B) { benchmarkAppendPyramid(10000000, 1000, t) } 549 func BenchmarkAppendPyramid_8(t *testing.B) { benchmarkAppendPyramid(100000000, 1000, t) } 550 551 // go test -timeout 20m -cpu 4 -bench=./swarm/storage -run no 552 // If you dont add the timeout argument above .. the benchmark will timeout and dump