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  }