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