github.com/klaytn/klaytn@v1.10.2/storage/database/leveldb_bench_multidisks_test.go (about) 1 // Copyright 2018 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn 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 klaytn 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 klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 //go:build multidisktest 17 // +build multidisktest 18 19 package database 20 21 import ( 22 "io/ioutil" 23 "math/rand" 24 "sync" 25 "testing" 26 "time" 27 ) 28 29 type multiDiskOption struct { 30 numDisks int 31 numShards int 32 numRows int 33 numBytes int 34 } 35 36 // adjust diskPaths according to your setting 37 var diskPaths = [...]string{"", "/disk1", "/disk2", "/disk3", "/disk4", "/disk5", "/disk6", "/disk7"} 38 39 func genDirForMDPTest(b *testing.B, numDisks, numShards int) []string { 40 if numDisks > len(diskPaths) { 41 b.Fatalf("entered numDisks %v is larger than diskPaths %v", numDisks, len(diskPaths)) 42 } 43 44 dirs := make([]string, numShards, numShards) 45 for i := 0; i < numShards; i++ { 46 diskNum := i % numDisks 47 dir, err := ioutil.TempDir(diskPaths[diskNum], "klaytn-db-bench-mdp") 48 if err != nil { 49 b.Fatalf("cannot create temporary directory: %v", err) 50 } 51 dirs[i] = dir 52 } 53 return dirs 54 } 55 56 func benchmark_MDP_Put_GoRoutine(b *testing.B, mdo *multiDiskOption) { 57 b.StopTimer() 58 59 dirs := genDirForMDPTest(b, mdo.numDisks, mdo.numShards) 60 defer removeDirs(dirs) 61 62 opts := getKlayLDBOptions() 63 databases := genDatabases(b, dirs, opts) 64 defer closeDBs(databases) 65 66 for i := 0; i < b.N; i++ { 67 b.StopTimer() 68 keys, values := genKeysAndValues(mdo.numBytes, mdo.numRows) 69 b.StartTimer() 70 71 var wait sync.WaitGroup 72 wait.Add(mdo.numRows) 73 74 for k := 0; k < mdo.numRows; k++ { 75 shard := getShardForTest(keys, k, mdo.numShards) 76 db := databases[shard] 77 78 go func(currDB Database, idx int) { 79 defer wait.Done() 80 currDB.Put(keys[idx], values[idx]) 81 }(db, k) 82 } 83 wait.Wait() 84 } 85 } 86 87 func benchmark_MDP_Put_NoGoRoutine(b *testing.B, mdo *multiDiskOption) { 88 b.StopTimer() 89 90 numDisks := mdo.numDisks 91 numBytes := mdo.numBytes 92 numRows := mdo.numRows 93 numShards := mdo.numShards 94 95 dirs := genDirForMDPTest(b, numDisks, numShards) 96 defer removeDirs(dirs) 97 98 opts := getKlayLDBOptions() 99 databases := genDatabases(b, dirs, opts) 100 defer closeDBs(databases) 101 102 for i := 0; i < b.N; i++ { 103 b.StopTimer() 104 keys, values := genKeysAndValues(numBytes, numRows) 105 b.StartTimer() 106 107 for k := 0; k < numRows; k++ { 108 shard := getShardForTest(keys, k, mdo.numShards) 109 db := databases[shard] 110 db.Put(keys[k], values[k]) 111 } 112 } 113 } 114 115 // please change below rowSize to change the size of an input row for MDP_Put tests (GoRoutine & NoGoRoutine) 116 const rowSizePutMDP = 250 117 const numRowsPutMDP = 1000 * 10 118 119 var putMDPBenchmarks = [...]struct { 120 name string 121 mdo multiDiskOption 122 }{ 123 // 10k Rows, 250 bytes, 1 disk, different number of shards 124 {"10k_1D_1P_250", multiDiskOption{1, 1, numRowsPutMDP, rowSizePutMDP}}, 125 {"10k_1D_2P_250", multiDiskOption{1, 2, numRowsPutMDP, rowSizePutMDP}}, 126 {"10k_1D_4P_250", multiDiskOption{1, 4, numRowsPutMDP, rowSizePutMDP}}, 127 {"10k_1D_8P_250", multiDiskOption{1, 8, numRowsPutMDP, rowSizePutMDP}}, 128 129 // 10k Rows, 250 bytes, 8 shards (fixed), different number of disks 130 {"10k_1D_8P_250", multiDiskOption{1, 8, numRowsPutMDP, rowSizePutMDP}}, 131 {"10k_2D_8P_250", multiDiskOption{2, 8, numRowsPutMDP, rowSizePutMDP}}, 132 {"10k_4D_8P_250", multiDiskOption{4, 8, numRowsPutMDP, rowSizePutMDP}}, 133 {"10k_8D_8P_250", multiDiskOption{8, 8, numRowsPutMDP, rowSizePutMDP}}, 134 135 // 10k Rows, 250 bytes, different number of disks & shards 136 {"10k_1D_1P_250", multiDiskOption{1, 1, numRowsPutMDP, rowSizePutMDP}}, 137 {"10k_2D_2P_250", multiDiskOption{2, 2, numRowsPutMDP, rowSizePutMDP}}, 138 {"10k_4D_4P_250", multiDiskOption{4, 4, numRowsPutMDP, rowSizePutMDP}}, 139 {"10k_8D_8P_250", multiDiskOption{8, 8, numRowsPutMDP, rowSizePutMDP}}, 140 } 141 142 func Benchmark_MDP_Put_GoRoutine(b *testing.B) { 143 for _, bm := range putMDPBenchmarks { 144 b.Run(bm.name, func(b *testing.B) { 145 benchmark_MDP_Put_GoRoutine(b, &bm.mdo) 146 }) 147 } 148 } 149 150 func Benchmark_MDP_Put_NoGoRoutine(b *testing.B) { 151 for _, bm := range putMDPBenchmarks { 152 b.Run(bm.name, func(b *testing.B) { 153 benchmark_MDP_Put_NoGoRoutine(b, &bm.mdo) 154 }) 155 } 156 } 157 158 func benchmark_MDP_Batch_GoRoutine(b *testing.B, mdo *multiDiskOption) { 159 b.StopTimer() 160 161 numDisks := mdo.numDisks 162 numBytes := mdo.numBytes 163 numRows := mdo.numRows 164 numShards := mdo.numShards 165 166 dirs := genDirForMDPTest(b, numDisks, numShards) 167 defer removeDirs(dirs) 168 169 opts := getKlayLDBOptions() 170 databases := genDatabases(b, dirs, opts) 171 defer closeDBs(databases) 172 173 zeroSizeBatch := 0 174 batchSizeSum := 0 175 numBatches := 0 176 for i := 0; i < b.N; i++ { 177 b.StopTimer() 178 // make same number of batches as numShards 179 batches := make([]Batch, numShards, numShards) 180 for k := 0; k < numShards; k++ { 181 batches[k] = databases[k].NewBatch() 182 } 183 keys, values := genKeysAndValues(numBytes, numRows) 184 b.StartTimer() 185 for k := 0; k < numRows; k++ { 186 shard := getShardForTest(keys, k, numShards) 187 batches[shard].Put(keys[k], values[k]) 188 } 189 190 for _, batch := range batches { 191 if batch.ValueSize() == 0 { 192 zeroSizeBatch++ 193 } 194 batchSizeSum += batch.ValueSize() 195 numBatches++ 196 } 197 var wait sync.WaitGroup 198 wait.Add(numShards) 199 for _, batch := range batches { 200 go func(currBatch Batch) { 201 defer wait.Done() 202 currBatch.Write() 203 }(batch) 204 } 205 wait.Wait() 206 } 207 208 if zeroSizeBatch != 0 { 209 b.Log("zeroSizeBatch: ", zeroSizeBatch) 210 } 211 } 212 213 func benchmark_MDP_Batch_NoGoRoutine(b *testing.B, mdo *multiDiskOption) { 214 b.StopTimer() 215 216 numDisks := mdo.numDisks 217 numBytes := mdo.numBytes 218 numRows := mdo.numRows 219 numShards := mdo.numShards 220 221 dirs := genDirForMDPTest(b, numDisks, numShards) 222 defer removeDirs(dirs) 223 224 opts := getKlayLDBOptions() 225 databases := genDatabases(b, dirs, opts) 226 defer closeDBs(databases) 227 228 zeroSizeBatch := 0 229 batchSizeSum := 0 230 numBatches := 0 231 for i := 0; i < b.N; i++ { 232 b.StopTimer() 233 // make same number of batches as numShards 234 batches := make([]Batch, numShards, numShards) 235 for k := 0; k < numShards; k++ { 236 batches[k] = databases[k].NewBatch() 237 } 238 keys, values := genKeysAndValues(numBytes, numRows) 239 b.StartTimer() 240 for k := 0; k < numRows; k++ { 241 shard := getShardForTest(keys, k, numShards) 242 batches[shard].Put(keys[k], values[k]) 243 } 244 245 for _, batch := range batches { 246 if batch.ValueSize() == 0 { 247 zeroSizeBatch++ 248 } 249 batchSizeSum += batch.ValueSize() 250 numBatches++ 251 } 252 253 for _, batch := range batches { 254 batch.Write() 255 } 256 257 } 258 259 if zeroSizeBatch != 0 { 260 b.Log("zeroSizeBatch: ", zeroSizeBatch) 261 } 262 } 263 264 // please change below rowSize to change the size of an input row for MDP_Batch tests (GoRoutine & NoGoRoutine) 265 const rowSizeBatchMDP = 250 266 const numRowsBatchMDP = 1000 * 10 267 268 var batchMDPBenchmarks = [...]struct { 269 name string 270 mdo multiDiskOption 271 }{ 272 // 10k Rows, 250 bytes, 1 disk, different number of shards 273 {"10k_1D_1P_250", multiDiskOption{1, 1, numRowsBatchMDP, rowSizeBatchMDP}}, 274 {"10k_1D_2P_250", multiDiskOption{1, 2, numRowsBatchMDP, rowSizeBatchMDP}}, 275 {"10k_1D_4P_250", multiDiskOption{1, 4, numRowsBatchMDP, rowSizeBatchMDP}}, 276 {"10k_1D_8P_250", multiDiskOption{1, 8, numRowsBatchMDP, rowSizeBatchMDP}}, 277 278 // 10k Rows, 250 bytes, 8 shards (fixed), different number of disks 279 {"10k_1D_8P_250", multiDiskOption{1, 8, numRowsBatchMDP, rowSizeBatchMDP}}, 280 {"10k_2D_8P_250", multiDiskOption{2, 8, numRowsBatchMDP, rowSizeBatchMDP}}, 281 {"10k_4D_8P_250", multiDiskOption{4, 8, numRowsBatchMDP, rowSizeBatchMDP}}, 282 {"10k_8D_8P_250", multiDiskOption{8, 8, numRowsBatchMDP, rowSizeBatchMDP}}, 283 284 // 10k Rows, 250 bytes, different number of disks & shards 285 {"10k_1D_1P_250", multiDiskOption{1, 1, numRowsBatchMDP, rowSizeBatchMDP}}, 286 {"10k_2D_2P_250", multiDiskOption{2, 2, numRowsBatchMDP, rowSizeBatchMDP}}, 287 {"10k_4D_4P_250", multiDiskOption{4, 4, numRowsBatchMDP, rowSizeBatchMDP}}, 288 {"10k_8D_8P_250", multiDiskOption{8, 8, numRowsBatchMDP, rowSizeBatchMDP}}, 289 } 290 291 func Benchmark_MDP_Batch_GoRoutine(b *testing.B) { 292 for _, bm := range batchMDPBenchmarks { 293 b.Run(bm.name, func(b *testing.B) { 294 benchmark_MDP_Batch_GoRoutine(b, &bm.mdo) 295 }) 296 } 297 } 298 299 func Benchmark_MDP_Batch_NoGoRoutine(b *testing.B) { 300 for _, bm := range batchMDPBenchmarks { 301 b.Run(bm.name, func(b *testing.B) { 302 benchmark_MDP_Batch_NoGoRoutine(b, &bm.mdo) 303 }) 304 } 305 } 306 307 func benchmark_MDP_Get_NoGoRotine(b *testing.B, mdo *multiDiskOption, numReads int, readType func(int, int) int) { 308 b.StopTimer() 309 310 numDisks := mdo.numDisks 311 numBytes := mdo.numBytes 312 numRows := mdo.numRows 313 numShards := mdo.numShards 314 315 dirs := genDirForMDPTest(b, numDisks, numShards) 316 defer removeDirs(dirs) 317 318 opts := getKlayLDBOptions() 319 databases := genDatabases(b, dirs, opts) 320 defer closeDBs(databases) 321 322 for i := 0; i < b.N; i++ { 323 b.StopTimer() 324 325 keys, values := genKeysAndValues(numBytes, numRows) 326 327 for k := 0; k < numRows; k++ { 328 shard := getShardForTest(keys, k, numShards) 329 db := databases[shard] 330 db.Put(keys[k], values[k]) 331 } 332 333 b.StartTimer() 334 for k := 0; k < numReads; k++ { 335 keyPos := readType(k, numRows) 336 if keyPos >= len(keys) { 337 b.Fatal("index out of range", keyPos) 338 } 339 shard := getShardForTest(keys, k, numShards) 340 db := databases[shard] 341 db.Get(keys[keyPos]) 342 } 343 } 344 } 345 346 func benchmark_MDP_Get_GoRoutine(b *testing.B, mdo *multiDiskOption, numReads int, readType func(int, int) int) { 347 b.StopTimer() 348 349 numDisks := mdo.numDisks 350 numBytes := mdo.numBytes 351 numRows := mdo.numRows 352 numShards := mdo.numShards 353 354 dirs := genDirForMDPTest(b, numDisks, numShards) 355 defer removeDirs(dirs) 356 357 opts := getKlayLDBOptions() 358 databases := genDatabases(b, dirs, opts) 359 defer closeDBs(databases) 360 361 for i := 0; i < b.N; i++ { 362 b.StopTimer() 363 364 keys, values := genKeysAndValues(numBytes, numRows) 365 366 for k := 0; k < numRows; k++ { 367 shard := getShardForTest(keys, k, numShards) 368 db := databases[shard] 369 db.Put(keys[k], values[k]) 370 } 371 372 b.StartTimer() 373 var wg sync.WaitGroup 374 wg.Add(numReads) 375 for k := 0; k < numReads; k++ { 376 keyPos := readType(k, numRows) 377 if keyPos >= len(keys) { 378 b.Fatalf("index out of range: keyPos: %v, k: %v, numRows: %v", keyPos, k, numRows) 379 } 380 381 shard := getShardForTest(keys, keyPos, numShards) 382 db := databases[shard] 383 384 go func(currDB Database, kPos int) { 385 defer wg.Done() 386 _, err := currDB.Get(keys[kPos]) 387 if err != nil { 388 b.Fatalf("get failed: %v", err) 389 } 390 }(db, keyPos) 391 392 } 393 wg.Wait() 394 } 395 } 396 397 // please change below rowSize to change the size of an input row for MDP_Get tests (GoRoutine & NoGoRoutine) 398 const rowSizeGetMDP = 250 399 400 const ( 401 insertedRowsBeforeGetMDP = 1000 * 100 // pre-insertion size before read 402 numReadsMDP = 1000 403 ) 404 405 var getMDPBenchmarks = [...]struct { 406 name string 407 mdo multiDiskOption 408 numReads int 409 }{ 410 // 10k Rows, 250 bytes, 1 disk, different number of shards 411 {"10k_1D_1P_250", multiDiskOption{1, 1, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 412 {"10k_1D_2P_250", multiDiskOption{1, 2, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 413 {"10k_1D_4P_250", multiDiskOption{1, 4, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 414 {"10k_1D_8P_250", multiDiskOption{1, 8, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 415 416 // 10k Rows, 250 bytes, 8 shards (fixed), different number of disks 417 {"10k_1D_8P_250", multiDiskOption{1, 8, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 418 {"10k_2D_8P_250", multiDiskOption{2, 8, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 419 {"10k_4D_8P_250", multiDiskOption{4, 8, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 420 {"10k_8D_8P_250", multiDiskOption{8, 8, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 421 422 // 10k Rows, 250 bytes, different number of disks & shards 423 {"10k_1D_1P_250", multiDiskOption{1, 1, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 424 {"10k_2D_2P_250", multiDiskOption{2, 2, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 425 {"10k_4D_4P_250", multiDiskOption{4, 4, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 426 {"10k_8D_8P_250", multiDiskOption{8, 8, insertedRowsBeforeGetMDP, rowSizeGetMDP}, numReadsMDP}, 427 } 428 429 func Benchmark_MDP_Get_Random_1kRows_GoRoutine(b *testing.B) { 430 for _, bm := range getMDPBenchmarks { 431 b.Run(bm.name, func(b *testing.B) { 432 benchmark_MDP_Get_GoRoutine(b, &bm.mdo, bm.numReads, randomRead) 433 }) 434 } 435 } 436 437 func Benchmark_MDP_Get_Random_1kRows_NoGoRoutine(b *testing.B) { 438 for _, bm := range getMDPBenchmarks { 439 b.Run(bm.name, func(b *testing.B) { 440 benchmark_MDP_Get_NoGoRotine(b, &bm.mdo, bm.numReads, randomRead) 441 }) 442 } 443 } 444 445 func Benchmark_MDP_Parallel_Get(b *testing.B) { 446 for _, bm := range getMDPBenchmarks { 447 b.Run(bm.name, func(b *testing.B) { 448 b.StopTimer() 449 mdo := bm.mdo 450 numDisks := mdo.numDisks 451 numBytes := mdo.numBytes 452 numRows := mdo.numRows 453 numShards := mdo.numShards 454 455 dirs := genDirForMDPTest(b, numDisks, numShards) 456 defer removeDirs(dirs) 457 458 opts := getKlayLDBOptions() 459 databases := genDatabases(b, dirs, opts) 460 defer closeDBs(databases) 461 462 keys, values := genKeysAndValues(numBytes, numRows) 463 464 for k := 0; k < numRows; k++ { 465 shard := getShardForTest(keys, k, numShards) 466 db := databases[shard] 467 db.Put(keys[k], values[k]) 468 } 469 470 rand.Seed(time.Now().UnixNano()) 471 b.StartTimer() 472 b.RunParallel(func(pb *testing.PB) { 473 for pb.Next() { 474 idx := rand.Intn(numRows) 475 shard := getShardForTest(keys, idx, numShards) 476 _, err := databases[shard].Get(keys[idx]) 477 if err != nil { 478 b.Fatalf("get failed: %v", err) 479 } 480 } 481 }) 482 }) 483 } 484 } 485 486 func Benchmark_MDP_Parallel_Put(b *testing.B) { 487 for _, bm := range putMDPBenchmarks { 488 b.Run(bm.name, func(b *testing.B) { 489 b.StopTimer() 490 mdo := bm.mdo 491 numDisks := mdo.numDisks 492 numBytes := mdo.numBytes 493 numRows := mdo.numRows * 10 // extend candidate keys and values for randomness 494 numShards := mdo.numShards 495 496 dirs := genDirForMDPTest(b, numDisks, numShards) 497 defer removeDirs(dirs) 498 499 opts := getKlayLDBOptions() 500 databases := genDatabases(b, dirs, opts) 501 defer closeDBs(databases) 502 503 keys, values := genKeysAndValues(numBytes, numRows) 504 505 rand.Seed(time.Now().UnixNano()) 506 b.StartTimer() 507 b.RunParallel(func(pb *testing.PB) { 508 for pb.Next() { 509 idx := rand.Intn(numRows) 510 shard := getShardForTest(keys, idx, numShards) 511 db := databases[shard] 512 db.Put(keys[idx], values[idx]) 513 } 514 }) 515 }) 516 } 517 } 518 519 const parallelBatchSizeMDP = 100 520 521 func Benchmark_MDP_Parallel_Batch(b *testing.B) { 522 for _, bm := range batchMDPBenchmarks { 523 b.Run(bm.name, func(b *testing.B) { 524 b.StopTimer() 525 mdo := bm.mdo 526 numDisks := mdo.numDisks 527 numBytes := mdo.numBytes 528 numRows := mdo.numRows * 10 // extend candidate keys and values for randomness 529 numShards := mdo.numShards 530 531 dirs := genDirForMDPTest(b, numDisks, numShards) 532 defer removeDirs(dirs) 533 534 opts := getKlayLDBOptions() 535 databases := genDatabases(b, dirs, opts) 536 defer closeDBs(databases) 537 538 keys, values := genKeysAndValues(numBytes, numRows) 539 540 rand.Seed(time.Now().UnixNano()) 541 b.StartTimer() 542 b.RunParallel(func(pb *testing.PB) { 543 for pb.Next() { 544 shard := rand.Intn(numShards) 545 batch := databases[shard].NewBatch() 546 for k := 0; k < parallelBatchSizeMDP; k++ { 547 idx := rand.Intn(numRows) 548 batch.Put(keys[idx], values[idx]) 549 } 550 batch.Write() 551 } 552 }) 553 }) 554 } 555 } 556 557 // TODO-Klaytn: MAKE PRE-LOADED TEST FOR BATCH, PUT