github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/manifest/l0_sublevels_test.go (about) 1 // Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package manifest 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "math" 12 "os" 13 "sort" 14 "strconv" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/stretchr/testify/require" 20 "github.com/zuoyebang/bitalostable/internal/base" 21 "github.com/zuoyebang/bitalostable/internal/datadriven" 22 "github.com/zuoyebang/bitalostable/internal/testkeys" 23 "github.com/zuoyebang/bitalostable/record" 24 "golang.org/x/exp/rand" 25 ) 26 27 func readManifest(filename string) (*Version, error) { 28 f, err := os.Open(filename) 29 if err != nil { 30 return nil, err 31 } 32 defer f.Close() 33 rr := record.NewReader(f, 0 /* logNum */) 34 var v *Version 35 addedByFileNum := make(map[base.FileNum]*FileMetadata) 36 for { 37 r, err := rr.Next() 38 if err == io.EOF { 39 break 40 } 41 if err != nil { 42 return nil, err 43 } 44 var ve VersionEdit 45 if err = ve.Decode(r); err != nil { 46 return nil, err 47 } 48 var bve BulkVersionEdit 49 bve.AddedByFileNum = addedByFileNum 50 if err := bve.Accumulate(&ve); err != nil { 51 return nil, err 52 } 53 if v, _, err = bve.Apply(v, base.DefaultComparer.Compare, base.DefaultFormatter, 10<<20, 32000); err != nil { 54 return nil, err 55 } 56 } 57 return v, nil 58 } 59 60 func TestL0Sublevels_LargeImportL0(t *testing.T) { 61 // TODO(bilal): Fix this test. 62 t.Skip() 63 v, err := readManifest("testdata/MANIFEST_import") 64 require.NoError(t, err) 65 66 sublevels, err := NewL0Sublevels(&v.Levels[0], 67 base.DefaultComparer.Compare, base.DefaultFormatter, 5<<20) 68 require.NoError(t, err) 69 fmt.Printf("L0Sublevels:\n%s\n\n", sublevels) 70 71 for i := 0; ; i++ { 72 c, err := sublevels.PickBaseCompaction(2, LevelSlice{}) 73 require.NoError(t, err) 74 if c == nil { 75 break 76 } 77 fmt.Printf("%d: base compaction: filecount: %d, bytes: %d, interval: [%d, %d], seed depth: %d\n", 78 i, len(c.Files), c.fileBytes, c.minIntervalIndex, c.maxIntervalIndex, c.seedIntervalStackDepthReduction) 79 var files []*FileMetadata 80 for i := range c.Files { 81 if c.FilesIncluded[i] { 82 c.Files[i].CompactionState = CompactionStateCompacting 83 files = append(files, c.Files[i]) 84 } 85 } 86 require.NoError(t, sublevels.UpdateStateForStartedCompaction( 87 []LevelSlice{NewLevelSliceSeqSorted(files)}, true)) 88 } 89 90 for i := 0; ; i++ { 91 c, err := sublevels.PickIntraL0Compaction(math.MaxUint64, 2) 92 require.NoError(t, err) 93 if c == nil { 94 break 95 } 96 fmt.Printf("%d: intra-L0 compaction: filecount: %d, bytes: %d, interval: [%d, %d], seed depth: %d\n", 97 i, len(c.Files), c.fileBytes, c.minIntervalIndex, c.maxIntervalIndex, c.seedIntervalStackDepthReduction) 98 var files []*FileMetadata 99 for i := range c.Files { 100 if c.FilesIncluded[i] { 101 c.Files[i].CompactionState = CompactionStateCompacting 102 c.Files[i].IsIntraL0Compacting = true 103 files = append(files, c.Files[i]) 104 } 105 } 106 require.NoError(t, sublevels.UpdateStateForStartedCompaction( 107 []LevelSlice{NewLevelSliceSeqSorted(files)}, false)) 108 } 109 } 110 111 func visualizeSublevels( 112 s *L0Sublevels, compactionFiles bitSet, otherLevels [][]*FileMetadata, 113 ) string { 114 var buf strings.Builder 115 if compactionFiles == nil { 116 compactionFiles = newBitSet(s.levelMetadata.Len()) 117 } 118 largestChar := byte('a') 119 printLevel := func(files []*FileMetadata, level string, isL0 bool) { 120 lastChar := byte('a') 121 fmt.Fprintf(&buf, "L%s:", level) 122 for i := 0; i < 5-len(level); i++ { 123 buf.WriteByte(' ') 124 } 125 for j, f := range files { 126 for lastChar < f.Smallest.UserKey[0] { 127 buf.WriteString(" ") 128 lastChar++ 129 } 130 buf.WriteByte(f.Smallest.UserKey[0]) 131 middleChar := byte('-') 132 if isL0 { 133 if compactionFiles[f.L0Index] { 134 middleChar = '+' 135 } else if f.IsCompacting() { 136 if f.IsIntraL0Compacting { 137 middleChar = '^' 138 } else { 139 middleChar = 'v' 140 } 141 } 142 } else if f.IsCompacting() { 143 middleChar = '=' 144 } 145 if largestChar < f.Largest.UserKey[0] { 146 largestChar = f.Largest.UserKey[0] 147 } 148 if f.Smallest.UserKey[0] == f.Largest.UserKey[0] { 149 buf.WriteByte(f.Largest.UserKey[0]) 150 if compactionFiles[f.L0Index] { 151 buf.WriteByte('+') 152 } else if j < len(files)-1 { 153 buf.WriteByte(' ') 154 } 155 lastChar++ 156 continue 157 } 158 buf.WriteByte(middleChar) 159 buf.WriteByte(middleChar) 160 lastChar++ 161 for lastChar < f.Largest.UserKey[0] { 162 buf.WriteByte(middleChar) 163 buf.WriteByte(middleChar) 164 buf.WriteByte(middleChar) 165 lastChar++ 166 } 167 if f.Largest.IsExclusiveSentinel() && 168 j < len(files)-1 && files[j+1].Smallest.UserKey[0] == f.Largest.UserKey[0] { 169 // This case happens where two successive files have 170 // matching end/start user keys but where the left-side file 171 // has the sentinel key as its end key trailer. In this case 172 // we print the sstables as: 173 // 174 // a------d------g 175 // 176 continue 177 } 178 buf.WriteByte(middleChar) 179 buf.WriteByte(f.Largest.UserKey[0]) 180 if j < len(files)-1 { 181 buf.WriteByte(' ') 182 } 183 lastChar++ 184 } 185 fmt.Fprintf(&buf, "\n") 186 } 187 for i := len(s.levelFiles) - 1; i >= 0; i-- { 188 printLevel(s.levelFiles[i], fmt.Sprintf("0.%d", i), true) 189 } 190 for i := range otherLevels { 191 if len(otherLevels[i]) == 0 { 192 continue 193 } 194 printLevel(otherLevels[i], strconv.Itoa(i+1), false) 195 } 196 buf.WriteString(" ") 197 for b := byte('a'); b <= largestChar; b++ { 198 buf.WriteByte(b) 199 buf.WriteByte(b) 200 if b < largestChar { 201 buf.WriteByte(' ') 202 } 203 } 204 buf.WriteByte('\n') 205 return buf.String() 206 } 207 208 func TestL0Sublevels(t *testing.T) { 209 parseMeta := func(s string) (*FileMetadata, error) { 210 parts := strings.Split(s, ":") 211 if len(parts) != 2 { 212 t.Fatalf("malformed table spec: %s", s) 213 } 214 fileNum, err := strconv.Atoi(strings.TrimSpace(parts[0])) 215 if err != nil { 216 return nil, err 217 } 218 fields := strings.Fields(parts[1]) 219 keyRange := strings.Split(strings.TrimSpace(fields[0]), "-") 220 m := (&FileMetadata{}).ExtendPointKeyBounds( 221 base.DefaultComparer.Compare, 222 base.ParseInternalKey(strings.TrimSpace(keyRange[0])), 223 base.ParseInternalKey(strings.TrimSpace(keyRange[1])), 224 ) 225 m.SmallestSeqNum = m.Smallest.SeqNum() 226 m.LargestSeqNum = m.Largest.SeqNum() 227 if m.Largest.IsExclusiveSentinel() { 228 m.LargestSeqNum = m.SmallestSeqNum 229 } 230 m.FileNum = base.FileNum(fileNum) 231 m.Size = uint64(256) 232 233 if len(fields) > 1 { 234 for _, field := range fields[1:] { 235 parts := strings.Split(field, "=") 236 switch parts[0] { 237 case "base_compacting": 238 m.IsIntraL0Compacting = false 239 m.CompactionState = CompactionStateCompacting 240 case "intra_l0_compacting": 241 m.IsIntraL0Compacting = true 242 m.CompactionState = CompactionStateCompacting 243 case "compacting": 244 m.CompactionState = CompactionStateCompacting 245 case "size": 246 sizeInt, err := strconv.Atoi(parts[1]) 247 if err != nil { 248 return nil, err 249 } 250 m.Size = uint64(sizeInt) 251 } 252 } 253 } 254 255 return m, nil 256 } 257 258 var err error 259 var fileMetas [NumLevels][]*FileMetadata 260 var explicitSublevels [][]*FileMetadata 261 var activeCompactions []L0Compaction 262 var sublevels *L0Sublevels 263 baseLevel := NumLevels - 1 264 265 datadriven.RunTest(t, "testdata/l0_sublevels", func(td *datadriven.TestData) string { 266 pickBaseCompaction := false 267 level := 0 268 addL0FilesOpt := false 269 switch td.Cmd { 270 case "add-l0-files": 271 addL0FilesOpt = true 272 level = 0 273 fallthrough 274 case "define": 275 if !addL0FilesOpt { 276 fileMetas = [NumLevels][]*FileMetadata{} 277 baseLevel = NumLevels - 1 278 activeCompactions = nil 279 } 280 explicitSublevels = [][]*FileMetadata{} 281 sublevel := -1 282 addedL0Files := make([]*FileMetadata, 0) 283 for _, data := range strings.Split(td.Input, "\n") { 284 data = strings.TrimSpace(data) 285 switch data[:2] { 286 case "L0", "L1", "L2", "L3", "L4", "L5", "L6": 287 level, err = strconv.Atoi(data[1:2]) 288 if err != nil { 289 return err.Error() 290 } 291 if level == 0 && len(data) > 3 { 292 // Sublevel was specified. 293 sublevel, err = strconv.Atoi(data[3:]) 294 if err != nil { 295 return err.Error() 296 } 297 } else { 298 sublevel = -1 299 } 300 default: 301 meta, err := parseMeta(data) 302 if err != nil { 303 return err.Error() 304 } 305 if level != 0 && level < baseLevel { 306 baseLevel = level 307 } 308 fileMetas[level] = append(fileMetas[level], meta) 309 if level == 0 { 310 addedL0Files = append(addedL0Files, meta) 311 } 312 if sublevel != -1 { 313 for len(explicitSublevels) <= sublevel { 314 explicitSublevels = append(explicitSublevels, []*FileMetadata{}) 315 } 316 explicitSublevels[sublevel] = append(explicitSublevels[sublevel], meta) 317 } 318 } 319 } 320 321 flushSplitMaxBytes := 64 322 initialize := true 323 for _, arg := range td.CmdArgs { 324 switch arg.Key { 325 case "flush_split_max_bytes": 326 flushSplitMaxBytes, err = strconv.Atoi(arg.Vals[0]) 327 if err != nil { 328 t.Fatal(err) 329 } 330 case "no_initialize": 331 // This case is for use with explicitly-specified sublevels 332 // only. 333 initialize = false 334 } 335 } 336 SortBySeqNum(fileMetas[0]) 337 for i := 1; i < NumLevels; i++ { 338 SortBySmallest(fileMetas[i], base.DefaultComparer.Compare) 339 } 340 341 levelMetadata := makeLevelMetadata(base.DefaultComparer.Compare, 0, fileMetas[0]) 342 if initialize { 343 if addL0FilesOpt { 344 SortBySeqNum(addedL0Files) 345 sublevels, err = sublevels.AddL0Files(addedL0Files, int64(flushSplitMaxBytes), &levelMetadata) 346 // Check if the output matches a full initialization. 347 sublevels2, _ := NewL0Sublevels(&levelMetadata, base.DefaultComparer.Compare, base.DefaultFormatter, int64(flushSplitMaxBytes)) 348 if sublevels != nil && sublevels2 != nil { 349 require.Equal(t, sublevels.flushSplitUserKeys, sublevels2.flushSplitUserKeys) 350 require.Equal(t, sublevels.levelFiles, sublevels2.levelFiles) 351 } 352 } else { 353 sublevels, err = NewL0Sublevels( 354 &levelMetadata, 355 base.DefaultComparer.Compare, 356 base.DefaultFormatter, 357 int64(flushSplitMaxBytes)) 358 } 359 if err != nil { 360 return err.Error() 361 } 362 sublevels.InitCompactingFileInfo(nil) 363 } else { 364 // This case is for use with explicitly-specified sublevels 365 // only. 366 sublevels = &L0Sublevels{ 367 levelFiles: explicitSublevels, 368 cmp: base.DefaultComparer.Compare, 369 formatKey: base.DefaultFormatter, 370 levelMetadata: &levelMetadata, 371 } 372 for _, files := range explicitSublevels { 373 sublevels.Levels = append(sublevels.Levels, NewLevelSliceSpecificOrder(files)) 374 } 375 } 376 377 if err != nil { 378 t.Fatal(err) 379 } 380 381 var builder strings.Builder 382 builder.WriteString(sublevels.describe(true)) 383 builder.WriteString(visualizeSublevels(sublevels, nil, fileMetas[1:])) 384 return builder.String() 385 case "pick-base-compaction": 386 pickBaseCompaction = true 387 fallthrough 388 case "pick-intra-l0-compaction": 389 minCompactionDepth := 3 390 earliestUnflushedSeqNum := uint64(math.MaxUint64) 391 for _, arg := range td.CmdArgs { 392 switch arg.Key { 393 case "min_depth": 394 minCompactionDepth, err = strconv.Atoi(arg.Vals[0]) 395 if err != nil { 396 t.Fatal(err) 397 } 398 case "earliest_unflushed_seqnum": 399 eusnInt, err := strconv.Atoi(arg.Vals[0]) 400 if err != nil { 401 t.Fatal(err) 402 } 403 earliestUnflushedSeqNum = uint64(eusnInt) 404 } 405 } 406 407 var lcf *L0CompactionFiles 408 if pickBaseCompaction { 409 baseFiles := NewLevelSliceKeySorted(base.DefaultComparer.Compare, fileMetas[baseLevel]) 410 lcf, err = sublevels.PickBaseCompaction(minCompactionDepth, baseFiles) 411 if err == nil && lcf != nil { 412 // Try to extend the base compaction into a more rectangular 413 // shape, using the smallest/largest keys of the files before 414 // and after overlapping base files. This mimics the logic 415 // the compactor is expected to implement. 416 baseFiles := fileMetas[baseLevel] 417 firstFile := sort.Search(len(baseFiles), func(i int) bool { 418 return sublevels.cmp(baseFiles[i].Largest.UserKey, sublevels.orderedIntervals[lcf.minIntervalIndex].startKey.key) >= 0 419 }) 420 lastFile := sort.Search(len(baseFiles), func(i int) bool { 421 return sublevels.cmp(baseFiles[i].Smallest.UserKey, sublevels.orderedIntervals[lcf.maxIntervalIndex+1].startKey.key) >= 0 422 }) 423 startKey := base.InvalidInternalKey 424 endKey := base.InvalidInternalKey 425 if firstFile > 0 { 426 startKey = baseFiles[firstFile-1].Largest 427 } 428 if lastFile < len(baseFiles) { 429 endKey = baseFiles[lastFile].Smallest 430 } 431 sublevels.ExtendL0ForBaseCompactionTo( 432 startKey, 433 endKey, 434 lcf) 435 } 436 } else { 437 lcf, err = sublevels.PickIntraL0Compaction(earliestUnflushedSeqNum, minCompactionDepth) 438 } 439 if err != nil { 440 return fmt.Sprintf("error: %s", err.Error()) 441 } 442 if lcf == nil { 443 return "no compaction picked" 444 } 445 var builder strings.Builder 446 builder.WriteString(fmt.Sprintf("compaction picked with stack depth reduction %d\n", lcf.seedIntervalStackDepthReduction)) 447 for i, file := range lcf.Files { 448 builder.WriteString(file.FileNum.String()) 449 if i < len(lcf.Files)-1 { 450 builder.WriteByte(',') 451 } 452 } 453 startKey := sublevels.orderedIntervals[lcf.seedInterval].startKey 454 endKey := sublevels.orderedIntervals[lcf.seedInterval+1].startKey 455 builder.WriteString(fmt.Sprintf("\nseed interval: %s-%s\n", startKey.key, endKey.key)) 456 builder.WriteString(visualizeSublevels(sublevels, lcf.FilesIncluded, fileMetas[1:])) 457 458 return builder.String() 459 case "read-amp": 460 return strconv.Itoa(sublevels.ReadAmplification()) 461 case "in-use-key-ranges": 462 var buf bytes.Buffer 463 for _, data := range strings.Split(strings.TrimSpace(td.Input), "\n") { 464 keyRange := strings.Split(strings.TrimSpace(data), "-") 465 smallest := []byte(strings.TrimSpace(keyRange[0])) 466 largest := []byte(strings.TrimSpace(keyRange[1])) 467 468 keyRanges := sublevels.InUseKeyRanges(smallest, largest) 469 for i, r := range keyRanges { 470 fmt.Fprintf(&buf, "%s-%s", sublevels.formatKey(r.Start), sublevels.formatKey(r.End)) 471 if i < len(keyRanges)-1 { 472 fmt.Fprint(&buf, ", ") 473 } 474 } 475 if len(keyRanges) == 0 { 476 fmt.Fprint(&buf, ".") 477 } 478 fmt.Fprintln(&buf) 479 } 480 return buf.String() 481 case "flush-split-keys": 482 var builder strings.Builder 483 builder.WriteString("flush user split keys: ") 484 flushSplitKeys := sublevels.FlushSplitKeys() 485 for i, key := range flushSplitKeys { 486 builder.Write(key) 487 if i < len(flushSplitKeys)-1 { 488 builder.WriteString(", ") 489 } 490 } 491 if len(flushSplitKeys) == 0 { 492 builder.WriteString("none") 493 } 494 return builder.String() 495 case "max-depth-after-ongoing-compactions": 496 return strconv.Itoa(sublevels.MaxDepthAfterOngoingCompactions()) 497 case "l0-check-ordering": 498 for sublevel, files := range sublevels.levelFiles { 499 slice := NewLevelSliceSpecificOrder(files) 500 err := CheckOrdering(base.DefaultComparer.Compare, base.DefaultFormatter, 501 L0Sublevel(sublevel), slice.Iter()) 502 if err != nil { 503 return err.Error() 504 } 505 } 506 return "OK" 507 case "update-state-for-compaction": 508 var fileNums []base.FileNum 509 for _, arg := range td.CmdArgs { 510 switch arg.Key { 511 case "files": 512 for _, val := range arg.Vals { 513 fileNum, err := strconv.ParseUint(val, 10, 64) 514 if err != nil { 515 return err.Error() 516 } 517 fileNums = append(fileNums, base.FileNum(fileNum)) 518 } 519 } 520 } 521 files := make([]*FileMetadata, 0, len(fileNums)) 522 for _, num := range fileNums { 523 for _, f := range fileMetas[0] { 524 if f.FileNum == num { 525 f.CompactionState = CompactionStateCompacting 526 files = append(files, f) 527 break 528 } 529 } 530 } 531 slice := NewLevelSliceSeqSorted(files) 532 sm, la := KeyRange(base.DefaultComparer.Compare, slice.Iter()) 533 activeCompactions = append(activeCompactions, L0Compaction{Smallest: sm, Largest: la}) 534 if err := sublevels.UpdateStateForStartedCompaction([]LevelSlice{slice}, true); err != nil { 535 return err.Error() 536 } 537 return "OK" 538 case "describe": 539 var builder strings.Builder 540 builder.WriteString(sublevels.describe(true)) 541 builder.WriteString(visualizeSublevels(sublevels, nil, fileMetas[1:])) 542 return builder.String() 543 } 544 return fmt.Sprintf("unrecognized command: %s", td.Cmd) 545 }) 546 } 547 548 func TestAddL0FilesEquivalence(t *testing.T) { 549 seed := uint64(time.Now().UnixNano()) 550 rng := rand.New(rand.NewSource(seed)) 551 t.Logf("seed: %d", seed) 552 553 var inUseKeys [][]byte 554 const keyReusePct = 0.15 555 var fileMetas []*FileMetadata 556 var s, s2 *L0Sublevels 557 keySpace := testkeys.Alpha(8) 558 559 flushSplitMaxBytes := rng.Int63n(1 << 20) 560 561 // The outer loop runs once for each version edit. The inner loop(s) run 562 // once for each file, or each file bound. 563 for i := 0; i < 100; i++ { 564 var filesToAdd []*FileMetadata 565 numFiles := 1 + rng.Intn(9) 566 keys := make([][]byte, 0, 2*numFiles) 567 for j := 0; j < 2*numFiles; j++ { 568 if rng.Float64() <= keyReusePct && len(inUseKeys) > 0 { 569 keys = append(keys, inUseKeys[rng.Intn(len(inUseKeys))]) 570 } else { 571 newKey := testkeys.Key(keySpace, rng.Intn(keySpace.Count())) 572 inUseKeys = append(inUseKeys, newKey) 573 keys = append(keys, newKey) 574 } 575 } 576 sort.Slice(keys, func(i, j int) bool { 577 return bytes.Compare(keys[i], keys[j]) < 0 578 }) 579 for j := 0; j < numFiles; j++ { 580 startKey := keys[j*2] 581 endKey := keys[j*2+1] 582 if bytes.Equal(startKey, endKey) { 583 continue 584 } 585 meta := (&FileMetadata{ 586 FileNum: base.FileNum(i*10 + j + 1), 587 Size: rng.Uint64n(1 << 20), 588 SmallestSeqNum: uint64(2*i + 1), 589 LargestSeqNum: uint64(2*i + 2), 590 }).ExtendPointKeyBounds( 591 base.DefaultComparer.Compare, 592 base.MakeInternalKey(startKey, uint64(2*i+1), base.InternalKeyKindSet), 593 base.MakeRangeDeleteSentinelKey(endKey), 594 ) 595 fileMetas = append(fileMetas, meta) 596 filesToAdd = append(filesToAdd, meta) 597 } 598 if len(filesToAdd) == 0 { 599 continue 600 } 601 602 levelMetadata := makeLevelMetadata(testkeys.Comparer.Compare, 0, fileMetas) 603 var err error 604 605 if s2 == nil { 606 s2, err = NewL0Sublevels(&levelMetadata, testkeys.Comparer.Compare, testkeys.Comparer.FormatKey, flushSplitMaxBytes) 607 require.NoError(t, err) 608 } else { 609 // AddL0Files relies on the indices in FileMetadatas pointing to that of 610 // the previous L0Sublevels. So it must be called before NewL0Sublevels; 611 // calling it the other way around results in out-of-bounds panics. 612 SortBySeqNum(filesToAdd) 613 s2, err = s2.AddL0Files(filesToAdd, flushSplitMaxBytes, &levelMetadata) 614 require.NoError(t, err) 615 } 616 617 s, err = NewL0Sublevels(&levelMetadata, testkeys.Comparer.Compare, testkeys.Comparer.FormatKey, flushSplitMaxBytes) 618 require.NoError(t, err) 619 620 // Check for equivalence. 621 require.Equal(t, s.flushSplitUserKeys, s2.flushSplitUserKeys) 622 require.Equal(t, s.orderedIntervals, s2.orderedIntervals) 623 require.Equal(t, s.levelFiles, s2.levelFiles) 624 } 625 } 626 627 func BenchmarkManifestApplyWithL0Sublevels(b *testing.B) { 628 b.ResetTimer() 629 for n := 0; n < b.N; n++ { 630 v, err := readManifest("testdata/MANIFEST_import") 631 require.NotNil(b, v) 632 require.NoError(b, err) 633 } 634 } 635 636 func BenchmarkL0SublevelsInit(b *testing.B) { 637 v, err := readManifest("testdata/MANIFEST_import") 638 if err != nil { 639 b.Fatal(err) 640 } 641 b.ResetTimer() 642 for n := 0; n < b.N; n++ { 643 sl, err := NewL0Sublevels(&v.Levels[0], 644 base.DefaultComparer.Compare, base.DefaultFormatter, 5<<20) 645 require.NoError(b, err) 646 if sl == nil { 647 b.Fatal("expected non-nil L0Sublevels to be generated") 648 } 649 } 650 } 651 652 func BenchmarkL0SublevelsInitAndPick(b *testing.B) { 653 v, err := readManifest("testdata/MANIFEST_import") 654 if err != nil { 655 b.Fatal(err) 656 } 657 b.ResetTimer() 658 for n := 0; n < b.N; n++ { 659 sl, err := NewL0Sublevels(&v.Levels[0], 660 base.DefaultComparer.Compare, base.DefaultFormatter, 5<<20) 661 require.NoError(b, err) 662 if sl == nil { 663 b.Fatal("expected non-nil L0Sublevels to be generated") 664 } 665 c, err := sl.PickBaseCompaction(2, LevelSlice{}) 666 require.NoError(b, err) 667 if c == nil { 668 b.Fatal("expected non-nil compaction to be generated") 669 } 670 } 671 }