github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/list/x_com_skl_test.go (about) 1 package list 2 3 import ( 4 "cmp" 5 "errors" 6 "hash/fnv" 7 randv2 "math/rand/v2" 8 "sort" 9 "strconv" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 type HashObject interface { 19 comparable 20 Hash() uint64 21 } 22 23 type emptyHashObject struct{} 24 25 func (o *emptyHashObject) Hash() uint64 { return 0 } 26 27 type SkipListWeight interface { 28 cmp.Ordered 29 // ~uint8 == byte 30 } 31 32 type xSklObject struct { 33 id string 34 } 35 36 func (o *xSklObject) Hash() uint64 { 37 if o == nil { 38 return 0 39 } 40 hash := fnv.New64() 41 _, _ = hash.Write([]byte(o.id)) 42 val := hash.Sum64() 43 return val 44 } 45 46 func TestStringHash_FNV(t *testing.T) { 47 s1, s2 := "1", "2" 48 hash := fnv.New64() 49 _, _ = hash.Write([]byte(s1)) 50 v1 := hash.Sum64() 51 assert.Equal(t, uint64(12638153115695167470), v1) 52 hash = fnv.New64() 53 _, _ = hash.Write([]byte(s2)) 54 v2 := hash.Sum64() 55 assert.Equal(t, uint64(12638153115695167469), v2) 56 res := int64(v2 - v1) 57 assert.Equal(t, res, int64(-1)) 58 59 s1, s2 = "100", "200" 60 hash = fnv.New64() 61 _, _ = hash.Write([]byte(s1)) 62 v1 = hash.Sum64() 63 hash = fnv.New64() 64 _, _ = hash.Write([]byte(s2)) 65 v2 = hash.Sum64() 66 assert.Greater(t, v1, v2) 67 } 68 69 func TestXComSkl_SimpleCRUD(t *testing.T) { 70 type element struct { 71 w int 72 id int 73 } 74 orders := []element{ 75 {1, 9}, 76 {1, 8}, 77 {1, 7}, 78 {1, 6}, 79 {1, 5}, 80 {1, 4}, 81 {1, 3}, 82 {1, 2}, 83 {1, 1}, 84 {2, 19}, 85 {2, 18}, 86 {2, 17}, 87 {2, 16}, 88 {2, 15}, 89 {2, 14}, 90 {2, 13}, 91 {2, 12}, 92 {2, 11}, 93 {4, 29}, 94 {4, 28}, 95 {4, 27}, 96 {4, 26}, 97 {4, 25}, 98 {4, 24}, 99 {4, 23}, 100 {4, 22}, 101 {4, 21}, 102 {8, 39}, 103 {8, 38}, 104 {8, 37}, 105 {8, 36}, 106 {8, 35}, 107 {8, 34}, 108 {8, 33}, 109 {8, 32}, 110 {8, 31}, 111 {16, 49}, 112 {16, 48}, 113 {16, 47}, 114 {16, 46}, 115 {16, 45}, 116 {16, 44}, 117 {16, 43}, 118 {16, 42}, 119 {16, 41}, 120 {32, 59}, 121 {32, 58}, 122 {32, 57}, 123 {32, 56}, 124 {32, 55}, 125 {32, 54}, 126 {32, 53}, 127 {32, 52}, 128 {32, 51}, 129 {64, 69}, 130 {64, 68}, 131 {64, 67}, 132 {64, 66}, 133 {64, 65}, 134 {64, 64}, 135 {64, 63}, 136 {64, 62}, 137 {64, 61}, 138 {128, 79}, 139 {128, 78}, 140 {128, 77}, 141 {128, 76}, 142 {128, 75}, 143 {128, 74}, 144 {128, 73}, 145 {128, 72}, 146 {128, 71}, 147 } 148 149 skl, err := NewXSkl[int, int]( 150 XComSkl, 151 WithXComSklValComparator[int, int]( 152 func(i, j int) int64 { 153 if i == j { 154 return 0 155 } else if i > j { 156 return -1 157 } 158 return 1 159 }), 160 WithSklRandLevelGen[int, int](randomLevelV2), 161 ) 162 require.NoError(t, err) 163 164 _, err = skl.RemoveAll(1) 165 require.True(t, errors.Is(err, ErrXSklIsEmpty)) 166 167 for _, o := range orders { 168 _ = skl.Insert(o.w, o.id) 169 } 170 171 err = skl.Insert(1, 2) 172 require.NoError(t, err) 173 skl.Foreach(func(i int64, item SklIterationItem[int, int]) bool { 174 require.Equal(t, orders[i].w, item.Key()) 175 require.Equal(t, orders[i].id, item.Val()) 176 t.Logf("key: %d, value: %v, levels: %d\n", item.Key(), item.Val(), item.NodeLevel()) 177 return true 178 }) 179 assert.Equal(t, int64(len(orders)), skl.Len()) 180 181 expectedFirstList := []element{ 182 {1, 9}, 183 {2, 19}, 184 {4, 29}, 185 {8, 39}, 186 {16, 49}, 187 {32, 59}, 188 {64, 69}, 189 {128, 79}, 190 } 191 for _, first := range expectedFirstList { 192 ele, err := skl.LoadFirst(first.w) 193 require.NoError(t, err) 194 assert.NotNil(t, ele) 195 assert.Equal(t, first.w, ele.Key()) 196 assert.Equal(t, first.id, ele.Val()) 197 } 198 199 var ele SklElement[int, int] 200 ele, err = skl.RemoveFirst(4) 201 require.NoError(t, err) 202 assert.NotNil(t, ele) 203 assert.Equal(t, 4, ele.Key()) 204 assert.Equal(t, 29, ele.Val()) 205 206 var eleList []SklElement[int, int] 207 eleList, err = skl.RemoveAll(4) 208 assert.NotNil(t, eleList) 209 expectedRemoveList := []element{ 210 {4, 28}, {4, 27}, {4, 26}, {4, 25}, {4, 24}, {4, 23}, {4, 22}, {4, 21}, 211 } 212 assert.Equal(t, len(expectedRemoveList), len(eleList)) 213 for i, e := range expectedRemoveList { 214 assert.Equal(t, e.w, eleList[i].Key()) 215 assert.Equal(t, e.id, eleList[i].Val()) 216 } 217 218 orders = []element{ 219 {1, 9}, 220 {1, 8}, 221 {1, 7}, 222 {1, 6}, 223 {1, 5}, 224 {1, 4}, 225 {1, 3}, 226 {1, 2}, 227 {1, 1}, 228 {2, 19}, 229 {2, 18}, 230 {2, 17}, 231 {2, 16}, 232 {2, 15}, 233 {2, 14}, 234 {2, 13}, 235 {2, 12}, 236 {2, 11}, 237 {8, 39}, 238 {8, 38}, 239 {8, 37}, 240 {8, 36}, 241 {8, 35}, 242 {8, 34}, 243 {8, 33}, 244 {8, 32}, 245 {8, 31}, 246 {16, 49}, 247 {16, 48}, 248 {16, 47}, 249 {16, 46}, 250 {16, 45}, 251 {16, 44}, 252 {16, 43}, 253 {16, 42}, 254 {16, 41}, 255 {32, 59}, 256 {32, 58}, 257 {32, 57}, 258 {32, 56}, 259 {32, 55}, 260 {32, 54}, 261 {32, 53}, 262 {32, 52}, 263 {32, 51}, 264 {64, 69}, 265 {64, 68}, 266 {64, 67}, 267 {64, 66}, 268 {64, 65}, 269 {64, 64}, 270 {64, 63}, 271 {64, 62}, 272 {64, 61}, 273 {128, 79}, 274 {128, 78}, 275 {128, 77}, 276 {128, 76}, 277 {128, 75}, 278 {128, 74}, 279 {128, 73}, 280 {128, 72}, 281 {128, 71}, 282 } 283 skl.Foreach(func(i int64, item SklIterationItem[int, int]) bool { 284 assert.Equal(t, orders[i].w, item.Key()) 285 assert.Equal(t, orders[i].id, item.Val()) 286 return true 287 }) 288 assert.Equal(t, int64(len(orders)), skl.Len()) 289 290 expectedFirstList = []element{ 291 {1, 9}, 292 {2, 19}, 293 {8, 39}, 294 {16, 49}, 295 {32, 59}, 296 {64, 69}, 297 {128, 79}, 298 } 299 for _, first := range expectedFirstList { 300 var err error 301 ele, err = skl.LoadFirst(first.w) 302 require.NoError(t, err) 303 assert.NotNil(t, ele) 304 assert.Equal(t, first.w, ele.Key()) 305 assert.Equal(t, first.id, ele.Val()) 306 } 307 308 expectedRemoveList = []element{ 309 {16, 47}, {8, 35}, {128, 71}, 310 } 311 for _, e := range expectedRemoveList { 312 eleList, err = skl.RemoveIfMatch(e.w, func(that int) bool { 313 return that == e.id 314 }) 315 assert.NotNil(t, eleList) 316 assert.Equal(t, 1, len(eleList)) 317 assert.Equal(t, e.w, eleList[0].Key()) 318 assert.Equal(t, e.id, eleList[0].Val()) 319 } 320 321 orders = []element{ 322 {1, 9}, 323 {1, 8}, 324 {1, 7}, 325 {1, 6}, 326 {1, 5}, 327 {1, 4}, 328 {1, 3}, 329 {1, 2}, 330 {1, 1}, 331 {2, 19}, 332 {2, 18}, 333 {2, 17}, 334 {2, 16}, 335 {2, 15}, 336 {2, 14}, 337 {2, 13}, 338 {2, 12}, 339 {2, 11}, 340 {8, 39}, 341 {8, 38}, 342 {8, 37}, 343 {8, 36}, 344 {8, 34}, 345 {8, 33}, 346 {8, 32}, 347 {8, 31}, 348 {16, 49}, 349 {16, 48}, 350 {16, 46}, 351 {16, 45}, 352 {16, 44}, 353 {16, 43}, 354 {16, 42}, 355 {16, 41}, 356 {32, 59}, 357 {32, 58}, 358 {32, 57}, 359 {32, 56}, 360 {32, 55}, 361 {32, 54}, 362 {32, 53}, 363 {32, 52}, 364 {32, 51}, 365 {64, 69}, 366 {64, 68}, 367 {64, 67}, 368 {64, 66}, 369 {64, 65}, 370 {64, 64}, 371 {64, 63}, 372 {64, 62}, 373 {64, 61}, 374 {128, 79}, 375 {128, 78}, 376 {128, 77}, 377 {128, 76}, 378 {128, 75}, 379 {128, 74}, 380 {128, 73}, 381 {128, 72}, 382 } 383 skl.Foreach(func(i int64, item SklIterationItem[int, int]) bool { 384 assert.Equal(t, orders[i].w, item.Key()) 385 assert.Equal(t, orders[i].id, item.Val()) 386 return true 387 }) 388 assert.Equal(t, int64(len(orders)), skl.Len()) 389 390 expectedFindList := []element{ 391 {1, 9}, 392 {2, 19}, 393 {8, 39}, 394 {16, 49}, 395 {32, 59}, 396 {64, 69}, 397 {128, 79}, 398 } 399 for _, e := range expectedFindList { 400 eleList, err = skl.LoadIfMatch(e.w, func(obj int) bool { 401 return obj == e.id 402 }) 403 require.NoError(t, err) 404 assert.NotNil(t, eleList) 405 assert.Equal(t, 1, len(eleList)) 406 assert.Equal(t, e.w, eleList[0].Key()) 407 assert.Equal(t, e.id, eleList[0].Val()) 408 } 409 410 expectedFindList = []element{ 411 {4, 20}, 412 {100, 100}, 413 {129, 77}, 414 } 415 for _, e := range expectedFindList { 416 eleList, err = skl.LoadIfMatch(e.w, func(obj int) bool { 417 return obj == e.id 418 }) 419 require.Error(t, err) 420 assert.Zero(t, len(eleList)) 421 } 422 423 expectedFindList = []element{ 424 {64, 69}, {64, 68}, {64, 67}, {64, 66}, {64, 65}, {64, 64}, {64, 63}, {64, 62}, {64, 61}, 425 } 426 eleList, err = skl.LoadAll(64) 427 require.NoError(t, err) 428 assert.NotZero(t, len(eleList)) 429 for i, e := range eleList { 430 assert.Equal(t, expectedFindList[i].w, e.Key()) 431 assert.Equal(t, expectedFindList[i].id, e.Val()) 432 } 433 } 434 435 func TestXComSkl_PopHead(t *testing.T) { 436 type element struct { 437 w int 438 id string 439 } 440 441 count := 1000 442 elements := make([]element, 0, count) 443 for i := 0; i < count; i++ { 444 w := int(cryptoRandUint32()) 445 elements = append(elements, element{w: w, id: strconv.Itoa(w)}) 446 } 447 448 skl, err := NewSkl[int, *xSklObject]( 449 XComSkl, 450 WithSklRandLevelGen[int, *xSklObject](randomLevelV2), 451 ) 452 require.NoError(t, err) 453 454 for _, o := range elements { 455 skl.Insert(o.w, &xSklObject{id: o.id}) 456 } 457 458 sort.Slice(elements, func(i, j int) bool { 459 return elements[i].w < elements[j].w 460 }) 461 462 for i := 0; i < len(elements); i++ { 463 ele, err := skl.PopHead() 464 require.NoError(t, err) 465 assert.Equal(t, elements[i].w, ele.Key()) 466 assert.Equal(t, elements[i].id, ele.Val().id) 467 restOrders := elements[i+1:] 468 ii := 0 469 skl.Foreach(func(i int64, item SklIterationItem[int, *xSklObject]) bool { 470 assert.Equal(t, restOrders[ii].w, item.Key()) 471 assert.Equal(t, restOrders[ii].id, item.Val().id) 472 ii++ 473 return true 474 }) 475 } 476 } 477 478 func TestXComSkl_Duplicate_PopHead(t *testing.T) { 479 type element struct { 480 w int 481 id string 482 } 483 orders := []element{ 484 {1, "3"}, 485 {1, "2"}, 486 {1, "1"}, 487 {2, "4"}, 488 {2, "2"}, 489 {3, "9"}, 490 {3, "8"}, 491 {3, "7"}, 492 {3, "1"}, 493 {4, "9"}, 494 {4, "6"}, 495 {4, "3"}, 496 {5, "7"}, 497 {5, "6"}, 498 {5, "2"}, 499 {6, "8"}, 500 {6, "100"}, 501 {7, "8"}, 502 {7, "7"}, 503 {7, "2"}, 504 {7, "1"}, 505 } 506 507 skl, err := NewXSkl[int, *xSklObject]( 508 XComSkl, 509 WithXComSklValComparator[int, *xSklObject]( 510 func(i, j *xSklObject) int64 { 511 _i, _j := i.Hash(), j.Hash() 512 if _i == _j { 513 return 0 514 } else if _i < _j { 515 return -1 516 } 517 return 1 518 }), 519 WithSklRandLevelGen[int, *xSklObject](randomLevelV3), 520 ) 521 require.NoError(t, err) 522 523 for _, o := range orders { 524 skl.Insert(o.w, &xSklObject{id: o.id}) 525 } 526 for i := 0; i < len(orders); i++ { 527 ele, err := skl.PopHead() 528 require.NoError(t, err) 529 assert.Equal(t, orders[i].w, ele.Key()) 530 assert.Equal(t, orders[i].id, ele.Val().id) 531 restOrders := orders[i+1:] 532 ii := 0 533 skl.Foreach(func(i int64, item SklIterationItem[int, *xSklObject]) bool { 534 assert.Equal(t, restOrders[ii].w, item.Key()) 535 assert.Equal(t, restOrders[ii].id, item.Val().id) 536 ii++ 537 return true 538 }) 539 } 540 } 541 542 func TestXComSklDuplicateDataRace(t *testing.T) { 543 opts := []SklOption[uint64, int64]{ 544 WithSklRandLevelGen[uint64, int64](randomLevelV2), 545 WithXComSklEnableConc[uint64, int64](), 546 WithXComSklValComparator[uint64, int64]( 547 func(i, j int64) int64 { 548 // avoid calculation overflow 549 if i == j { 550 return 0 551 } else if i > j { 552 return 1 553 } 554 return -1 555 }, 556 ), 557 } 558 skl, err := NewXSkl[uint64, int64]( 559 XComSkl, 560 opts..., 561 ) 562 require.NoError(t, err) 563 564 ele, err := skl.PopHead() 565 require.Nil(t, ele) 566 require.True(t, errors.Is(err, ErrXSklIsEmpty)) 567 568 size := 10 569 size2 := 10 570 unorderedWeights := make([]int64, 0, size2) 571 for i := 0; i < size2; i++ { 572 unorderedWeights = append(unorderedWeights, int64(cryptoRandUint64())) 573 } 574 orderedWeights := make([]int64, 0, size2) 575 orderedWeights = append(orderedWeights, unorderedWeights...) 576 sort.Slice(orderedWeights, func(i, j int) bool { 577 return orderedWeights[i] < orderedWeights[j] 578 }) 579 580 var wg sync.WaitGroup 581 wg.Add(size * size2) 582 583 type answer struct { 584 w uint64 585 id int64 586 } 587 expected := make([]*answer, 0, size*size2) 588 589 for i := uint64(0); i < uint64(size); i++ { 590 for j := uint64(0); j < uint64(size2); j++ { 591 go func(_i, _j uint64) { 592 w := (_i + 1) * 100 593 time.Sleep(time.Duration(cryptoRandUint32()%5) * time.Millisecond) 594 _ = skl.Insert(w, unorderedWeights[_j]) 595 wg.Done() 596 }(i, j) 597 expected = append(expected, &answer{w: (i + 1) * 100, id: orderedWeights[j]}) 598 } 599 } 600 wg.Wait() 601 t.Logf("nodeLen: %d, indexCount: %d\n", skl.Len(), skl.IndexCount()) 602 603 skl.Foreach(func(idx int64, item SklIterationItem[uint64, int64]) bool { 604 require.Equalf(t, expected[idx].w, item.Key(), "exp: %d; actual: %d\n", expected[idx].w, item.Key()) 605 require.Equalf(t, expected[idx].id, item.Val(), "exp: %d; actual: %d\n", expected[idx].id, item.Val()) 606 return true 607 }) 608 609 wg.Add(size * size2) 610 for i := uint64(0); i < uint64(size); i++ { 611 for j := uint64(0); j < uint64(size2); j++ { 612 go func(_i, _j uint64) { 613 w := (_i + 1) * 100 614 time.Sleep(time.Duration(cryptoRandUint32()%5) * time.Millisecond) 615 if _, err := skl.RemoveFirst(w); err != nil { 616 t.Logf("remove failed, key: %d, err: %v\n", w, err) 617 } 618 wg.Done() 619 }(i, j) 620 } 621 } 622 wg.Wait() 623 require.Equal(t, int64(0), skl.Len()) 624 require.Equal(t, uint64(0), skl.IndexCount()) 625 } 626 627 func BenchmarkXComSklUnique_Random(b *testing.B) { 628 testByBytes := []byte(`abc`) 629 630 b.StopTimer() 631 opts := make([]SklOption[int, []byte], 0, 2) 632 skl, err := NewSkl[int, []byte]( 633 XComSkl, 634 opts..., 635 ) 636 if err != nil { 637 panic(err) 638 } 639 640 rngArr := make([]int, 0, b.N) 641 for i := 0; i < b.N; i++ { 642 rngArr = append(rngArr, randv2.Int()) 643 } 644 645 b.StartTimer() 646 for i := 0; i < b.N; i++ { 647 err := skl.Insert(rngArr[i], testByBytes) 648 if err != nil { 649 panic(err) 650 } 651 } 652 } 653 654 func BenchmarkXComSklUnique_Serial(b *testing.B) { 655 testByBytes := []byte(`abc`) 656 657 b.StopTimer() 658 opts := make([]SklOption[int, []byte], 0, 2) 659 skl, err := NewSkl[int, []byte]( 660 XComSkl, 661 opts..., 662 ) 663 if err != nil { 664 panic(err) 665 } 666 667 b.StartTimer() 668 for i := 0; i < b.N; i++ { 669 skl.Insert(i, testByBytes) 670 } 671 } 672 673 func TestXComSklNodeGen(t *testing.T) { 674 for lvl := int32(1); lvl <= sklMaxLevel; lvl++ { 675 genXComSklNode[string, int]("123", 1, lvl) 676 } 677 require.Panics(t, func() { 678 genXComSklNode[string, int]("123", 1, 0) 679 }) 680 }