github.com/jfcg/sorty@v1.2.0/sorty_test.go (about)

     1  /*	Copyright (c) 2019, Serhat Şevki Dinçer.
     2  	This Source Code Form is subject to the terms of the Mozilla Public
     3  	License, v. 2.0. If a copy of the MPL was not distributed with this
     4  	file, You can obtain one at http://mozilla.org/MPL/2.0/.
     5  */
     6  
     7  package sorty
     8  
     9  import (
    10  	"fmt"
    11  	"math/rand"
    12  	"testing"
    13  	"time"
    14  	"unsafe"
    15  
    16  	"github.com/jfcg/sixb"
    17  )
    18  
    19  const N = 1 << 26
    20  
    21  var (
    22  	// a & b buffers will hold all slices to sort
    23  	bufaf = make([]float32, N)
    24  	bufbf = make([]float32, N)
    25  
    26  	// different type views of the same buffers
    27  	bufau  = F4toU4(&bufaf) // uint32
    28  	bufbu  = F4toU4(&bufbf)
    29  	bufai  = F4toI4(&bufaf)     // int32
    30  	bufau2 = sixb.U4toU8(bufau) // uint64
    31  	bufbu2 = sixb.U4toU8(bufbu)
    32  	bufaf2 = U8toF8(&bufau2) // float64
    33  	bufbi2 = U8toI8(&bufbu2) // int64
    34  
    35  	tsPtr *testing.T
    36  )
    37  
    38  // fill sort test for uint32
    39  func fstU4(sd int64, ar []uint32, srt func([]uint32)) time.Duration {
    40  	rn := rand.New(rand.NewSource(sd))
    41  	for i := len(ar) - 1; i >= 0; i-- {
    42  		ar[i] = rn.Uint32()
    43  	}
    44  
    45  	now := time.Now()
    46  	srt(ar)
    47  	dur := time.Since(now)
    48  
    49  	if IsSortedU4(ar) != 0 {
    50  		tsPtr.Fatal("not sorted")
    51  	}
    52  	return dur
    53  }
    54  
    55  // fill sort test for uint64
    56  func fstU8(sd int64, ar []uint64, srt func([]uint64)) time.Duration {
    57  	rn := rand.New(rand.NewSource(sd))
    58  	for i := len(ar) - 1; i >= 0; i-- {
    59  		ar[i] = rn.Uint64()
    60  	}
    61  
    62  	now := time.Now()
    63  	srt(ar)
    64  	dur := time.Since(now)
    65  
    66  	if IsSortedU8(ar) != 0 {
    67  		tsPtr.Fatal("not sorted")
    68  	}
    69  	return dur
    70  }
    71  
    72  // fill sort test for int32
    73  func fstI4(sd int64, ar []int32, srt func([]int32)) time.Duration {
    74  	rn := rand.New(rand.NewSource(sd))
    75  	for i := len(ar) - 1; i >= 0; i-- {
    76  		ar[i] = int32(rn.Uint32())
    77  	}
    78  
    79  	now := time.Now()
    80  	srt(ar)
    81  	dur := time.Since(now)
    82  
    83  	if IsSortedI4(ar) != 0 {
    84  		tsPtr.Fatal("not sorted")
    85  	}
    86  	return dur
    87  }
    88  
    89  // fill sort test for int64
    90  func fstI8(sd int64, ar []int64, srt func([]int64)) time.Duration {
    91  	rn := rand.New(rand.NewSource(sd))
    92  	for i := len(ar) - 1; i >= 0; i-- {
    93  		ar[i] = int64(rn.Uint64())
    94  	}
    95  
    96  	now := time.Now()
    97  	srt(ar)
    98  	dur := time.Since(now)
    99  
   100  	if IsSortedI8(ar) != 0 {
   101  		tsPtr.Fatal("not sorted")
   102  	}
   103  	return dur
   104  }
   105  
   106  // fill sort test for float32
   107  func fstF4(sd int64, ar []float32, srt func([]float32)) time.Duration {
   108  	rn := rand.New(rand.NewSource(sd))
   109  	for i := len(ar) - 1; i >= 0; i-- {
   110  		ar[i] = float32(rn.NormFloat64())
   111  	}
   112  
   113  	now := time.Now()
   114  	srt(ar)
   115  	dur := time.Since(now)
   116  
   117  	if IsSortedF4(ar) != 0 {
   118  		tsPtr.Fatal("not sorted")
   119  	}
   120  	return dur
   121  }
   122  
   123  // fill sort test for float64
   124  func fstF8(sd int64, ar []float64, srt func([]float64)) time.Duration {
   125  	rn := rand.New(rand.NewSource(sd))
   126  	for i := len(ar) - 1; i >= 0; i-- {
   127  		ar[i] = rn.NormFloat64()
   128  	}
   129  
   130  	now := time.Now()
   131  	srt(ar)
   132  	dur := time.Since(now)
   133  
   134  	if IsSortedF8(ar) != 0 {
   135  		tsPtr.Fatal("not sorted")
   136  	}
   137  	return dur
   138  }
   139  
   140  // implant strings into ar
   141  func implantS(ar []uint32, fill bool) ([]string, []uint32) {
   142  	// string size is 4*t bytes
   143  	t := sixb.StrSize >> 2
   144  
   145  	// ar will hold n strings (headers followed by 4-byte bodies)
   146  	n := len(ar) / (t + 1)
   147  
   148  	t *= n // total string headers space
   149  	ss := sixb.U4toStrs(ar[:t:t])
   150  
   151  	if fill {
   152  		for k := len(ar); n > 0; {
   153  			n--
   154  			k--
   155  			ss[n].Data = unsafe.Pointer(&ar[k])
   156  			ss[n].Len = 4
   157  		}
   158  	}
   159  	return *(*[]string)(unsafe.Pointer(&ss)), ar[t:]
   160  }
   161  
   162  // fill sort test for string
   163  func fstS(sd int64, ar []uint32, srt func([]string)) time.Duration {
   164  	as, ar := implantS(ar, true)
   165  
   166  	rn := rand.New(rand.NewSource(sd))
   167  	for i := len(ar) - 1; i >= 0; i-- {
   168  		ar[i] = rn.Uint32()
   169  	}
   170  
   171  	now := time.Now()
   172  	srt(as)
   173  	dur := time.Since(now)
   174  
   175  	if IsSortedS(as) != 0 {
   176  		tsPtr.Fatal("not sorted")
   177  	}
   178  	return dur
   179  }
   180  
   181  // implant []byte's into ar
   182  func implantB(ar []uint32, fill bool) ([][]byte, []uint32) {
   183  	// []byte size is 4*t bytes
   184  	t := sixb.SliceSize >> 2
   185  
   186  	// ar will hold n []byte's (headers followed by 4-byte bodies)
   187  	n := len(ar) / (t + 1)
   188  
   189  	t *= n // total []byte headers space
   190  	bs := sixb.U4toSlcs(ar[:t:t])
   191  
   192  	if fill {
   193  		for k := len(ar); n > 0; {
   194  			n--
   195  			k--
   196  			bs[n].Data = unsafe.Pointer(&ar[k])
   197  			bs[n].Len = 4
   198  			bs[n].Cap = 4
   199  		}
   200  	}
   201  	return *(*[][]byte)(unsafe.Pointer(&bs)), ar[t:]
   202  }
   203  
   204  // fill sort test for []byte
   205  func fstB(sd int64, ar []uint32, srt func([][]byte)) time.Duration {
   206  	ab, ar := implantB(ar, true)
   207  
   208  	rn := rand.New(rand.NewSource(sd))
   209  	for i := len(ar) - 1; i >= 0; i-- {
   210  		ar[i] = rn.Uint32()
   211  	}
   212  
   213  	now := time.Now()
   214  	srt(ab)
   215  	dur := time.Since(now)
   216  
   217  	if IsSortedB(ab) != 0 {
   218  		tsPtr.Fatal("not sorted")
   219  	}
   220  	return dur
   221  }
   222  
   223  // implant strings into ar (SortLen)
   224  func implantLenS(sd int64, ar []uint32, fill bool) []string {
   225  	// string size is 4*t bytes
   226  	t := sixb.StrSize >> 2
   227  
   228  	// ar will hold n string headers
   229  	n := len(ar) / t
   230  
   231  	t *= n // total string headers space
   232  	ss := sixb.U4toStrs(ar[:t:t])
   233  
   234  	if fill {
   235  		rn := rand.New(rand.NewSource(sd))
   236  
   237  		for L := 4*len(ar) + 1; n > 0; {
   238  			n--
   239  			// string bodies start at &ar[0] with random lengths up to 4*len(ar) bytes
   240  			ss[n].Data = unsafe.Pointer(&ar[0])
   241  			ss[n].Len = rn.Intn(L)
   242  		}
   243  	}
   244  	return *(*[]string)(unsafe.Pointer(&ss))
   245  }
   246  
   247  // fill sort test for string (SortLen)
   248  func fstLenS(sd int64, ar []uint32, srt func([]string)) time.Duration {
   249  	as := implantLenS(sd, ar, true)
   250  
   251  	now := time.Now()
   252  	srt(as)
   253  	dur := time.Since(now)
   254  
   255  	if IsSortedLen(as) != 0 {
   256  		tsPtr.Fatal("not sorted")
   257  	}
   258  	return dur
   259  }
   260  
   261  // implant []byte's into ar (SortLen)
   262  func implantLenB(sd int64, ar []uint32, fill bool) [][]byte {
   263  	// []byte size is 4*t bytes
   264  	t := sixb.SliceSize >> 2
   265  
   266  	// ar will hold n []byte headers
   267  	n := len(ar) / t
   268  
   269  	t *= n // total []byte headers space
   270  	bs := sixb.U4toSlcs(ar[:t:t])
   271  
   272  	if fill {
   273  		rn := rand.New(rand.NewSource(sd))
   274  
   275  		for L := 4*len(ar) + 1; n > 0; {
   276  			n--
   277  			// []byte bodies start at &ar[0] with random lengths up to 4*len(ar) bytes
   278  			bs[n].Data = unsafe.Pointer(&ar[0])
   279  			bs[n].Len = rn.Intn(L)
   280  			bs[n].Cap = bs[n].Len
   281  		}
   282  	}
   283  	return *(*[][]byte)(unsafe.Pointer(&bs))
   284  }
   285  
   286  // fill sort test for []byte (SortLen)
   287  func fstLenB(sd int64, ar []uint32, srt func([][]byte)) time.Duration {
   288  	ab := implantLenB(sd, ar, true)
   289  
   290  	now := time.Now()
   291  	srt(ab)
   292  	dur := time.Since(now)
   293  
   294  	if IsSortedLen(ab) != 0 {
   295  		tsPtr.Fatal("not sorted")
   296  	}
   297  	return dur
   298  }
   299  
   300  func compareU4(ar, ap []uint32) {
   301  	l := len(ap)
   302  	if l <= 0 {
   303  		return
   304  	}
   305  	if len(ar) != l {
   306  		tsPtr.Fatal("length mismatch:", len(ar), l)
   307  	}
   308  
   309  	for i := l - 1; i >= 0; i-- {
   310  		if ar[i] != ap[i] {
   311  			tsPtr.Fatal("values mismatch:", i, ar[i], ap[i])
   312  		}
   313  	}
   314  }
   315  
   316  func compareS(ar, ap []string) {
   317  	l := len(ap)
   318  	if len(ar) != l {
   319  		tsPtr.Fatal("length mismatch:", len(ar), l)
   320  	}
   321  
   322  	for i := l - 1; i >= 0; i-- {
   323  		if ar[i] != ap[i] {
   324  			tsPtr.Fatal("values mismatch:", i, ar[i], ap[i])
   325  		}
   326  	}
   327  }
   328  
   329  func compareB(ar, ap [][]byte) {
   330  	l := len(ap)
   331  	if len(ar) != l {
   332  		tsPtr.Fatal("length mismatch:", len(ar), l)
   333  	}
   334  
   335  	for i := l - 1; i >= 0; i-- {
   336  		if sixb.BtoS(ar[i]) != sixb.BtoS(ap[i]) {
   337  			tsPtr.Fatal("values mismatch:", i, ar[i], ap[i])
   338  		}
   339  	}
   340  }
   341  
   342  func compareLenS(ar, ap []string) {
   343  	l := len(ap)
   344  	if len(ar) != l {
   345  		tsPtr.Fatal("length mismatch:", len(ar), l)
   346  	}
   347  
   348  	for i := l - 1; i >= 0; i-- {
   349  		if len(ar[i]) != len(ap[i]) {
   350  			tsPtr.Fatal("lengths mismatch:", i, len(ar[i]), len(ap[i]))
   351  		}
   352  	}
   353  }
   354  
   355  func compareLenB(ar, ap [][]byte) {
   356  	l := len(ap)
   357  	if len(ar) != l {
   358  		tsPtr.Fatal("length mismatch:", len(ar), l)
   359  	}
   360  
   361  	for i := l - 1; i >= 0; i-- {
   362  		if len(ar[i]) != len(ap[i]) {
   363  			tsPtr.Fatal("lengths mismatch:", i, len(ar[i]), len(ap[i]))
   364  		}
   365  	}
   366  }
   367  
   368  // median of four durations
   369  func medur(a, b, c, d time.Duration) time.Duration {
   370  	if d < b {
   371  		d, b = b, d
   372  	}
   373  	if c < a {
   374  		c, a = a, c
   375  	}
   376  	if d < c {
   377  		c = d
   378  	}
   379  	if b < a {
   380  		b = a
   381  	}
   382  	return (b + c) >> 1
   383  }
   384  
   385  // median fst & compare for uint32
   386  func mfcU4(tn string, srt func([]uint32), ar, ap []uint32) float64 {
   387  	d1 := fstU4(1, ar, srt) // median of four different sorts
   388  	d2 := fstU4(2, ar, srt)
   389  	d3 := fstU4(3, ar, srt)
   390  	d1 = medur(fstU4(4, ar, srt), d1, d2, d3)
   391  
   392  	compareU4(ar, ap)
   393  	return printSec(tn, d1)
   394  }
   395  
   396  // slice conversions
   397  func F4toU4(p *[]float32) []uint32 {
   398  	return *(*[]uint32)(unsafe.Pointer(p))
   399  }
   400  
   401  func F4toI4(p *[]float32) []int32 {
   402  	return *(*[]int32)(unsafe.Pointer(p))
   403  }
   404  
   405  func U8toF8(p *[]uint64) []float64 {
   406  	return *(*[]float64)(unsafe.Pointer(p))
   407  }
   408  
   409  func U8toI8(p *[]uint64) []int64 {
   410  	return *(*[]int64)(unsafe.Pointer(p))
   411  }
   412  
   413  // median fst & compare for float32
   414  func mfcF4(tn string, srt func([]float32), ar, ap []float32) float64 {
   415  	d1 := fstF4(5, ar, srt) // median of four different sorts
   416  	d2 := fstF4(6, ar, srt)
   417  	d3 := fstF4(7, ar, srt)
   418  	d1 = medur(fstF4(8, ar, srt), d1, d2, d3)
   419  
   420  	compareU4(F4toU4(&ar), F4toU4(&ap))
   421  	return printSec(tn, d1)
   422  }
   423  
   424  // median fst & compare for string
   425  func mfcS(tn string, srt func([]string), ar, ap []uint32) float64 {
   426  	d1 := fstS(9, ar, srt) // median of four different sorts
   427  	d2 := fstS(10, ar, srt)
   428  	d3 := fstS(11, ar, srt)
   429  	d1 = medur(fstS(12, ar, srt), d1, d2, d3)
   430  
   431  	if len(ap) > 0 {
   432  		as, ar := implantS(ar, false)
   433  		aq, ap := implantS(ap, false)
   434  		compareS(as, aq)
   435  		compareU4(ar, ap)
   436  	}
   437  	return printSec(tn, d1)
   438  }
   439  
   440  // median fst & compare for []byte
   441  func mfcB(tn string, srt func([][]byte), ar, ap []uint32) float64 {
   442  	d1 := fstB(13, ar, srt) // median of four different sorts
   443  	d2 := fstB(14, ar, srt)
   444  	d3 := fstB(15, ar, srt)
   445  	d1 = medur(fstB(16, ar, srt), d1, d2, d3)
   446  
   447  	if len(ap) > 0 {
   448  		as, ar := implantB(ar, false)
   449  		aq, ap := implantB(ap, false)
   450  		compareB(as, aq)
   451  		compareU4(ar, ap)
   452  	}
   453  	return printSec(tn, d1)
   454  }
   455  
   456  // median fst & compare for string (SortLen)
   457  func mfcLenS(tn string, srt func([]string), ar, ap []uint32) float64 {
   458  	d1 := fstLenS(17, ar, srt) // median of four different sorts
   459  	d2 := fstLenS(18, ar, srt)
   460  	d3 := fstLenS(19, ar, srt)
   461  	d1 = medur(fstLenS(20, ar, srt), d1, d2, d3)
   462  
   463  	if len(ap) > 0 {
   464  		as := implantLenS(0, ar, false)
   465  		aq := implantLenS(0, ap, false)
   466  		compareLenS(as, aq)
   467  	}
   468  	return printSec(tn, d1)
   469  }
   470  
   471  // median fst & compare for []byte (SortLen)
   472  func mfcLenB(tn string, srt func([][]byte), ar, ap []uint32) float64 {
   473  	d1 := fstLenB(21, ar, srt) // median of four different sorts
   474  	d2 := fstLenB(22, ar, srt)
   475  	d3 := fstLenB(23, ar, srt)
   476  	d1 = medur(fstLenB(24, ar, srt), d1, d2, d3)
   477  
   478  	if len(ap) > 0 {
   479  		as := implantLenB(0, ar, false)
   480  		aq := implantLenB(0, ap, false)
   481  		compareLenB(as, aq)
   482  	}
   483  	return printSec(tn, d1)
   484  }
   485  
   486  // return sum of SortU4() times for 1..4 goroutines
   487  // compare with ap and among themselves
   488  func sumtU4(ar, ap []uint32) float64 {
   489  	s := .0
   490  	for Mxg = 1; Mxg < 5; Mxg++ {
   491  		s += mfcU4(fmt.Sprintf("sorty-%d", Mxg), SortU4, ar, ap)
   492  		ap, ar = ar, ap[:cap(ap)]
   493  	}
   494  	return s
   495  }
   496  
   497  // return sum of SortF4() times for 1..4 goroutines
   498  // compare with ap and among themselves
   499  func sumtF4(ar, ap []float32) float64 {
   500  	s := .0
   501  	for Mxg = 1; Mxg < 5; Mxg++ {
   502  		s += mfcF4(fmt.Sprintf("sorty-%d", Mxg), SortF4, ar, ap)
   503  		ap, ar = ar, ap[:cap(ap)]
   504  	}
   505  	return s
   506  }
   507  
   508  // return sum of SortS() times for 1..4 goroutines
   509  // compare with ap and among themselves
   510  func sumtS(ar, ap []uint32) float64 {
   511  	s := .0
   512  	for Mxg = 1; Mxg < 5; Mxg++ {
   513  		s += mfcS(fmt.Sprintf("sorty-%d", Mxg), SortS, ar, ap)
   514  		ap, ar = ar, ap[:cap(ap)]
   515  	}
   516  	return s
   517  }
   518  
   519  // return sum of SortB() times for 1..4 goroutines
   520  // compare with ap and among themselves
   521  func sumtB(ar, ap []uint32) float64 {
   522  	s := .0
   523  	for Mxg = 1; Mxg < 5; Mxg++ {
   524  		s += mfcB(fmt.Sprintf("sorty-%d", Mxg), SortB, ar, ap)
   525  		ap, ar = ar, ap[:cap(ap)]
   526  	}
   527  	return s
   528  }
   529  
   530  // return sum of SortLen([]string) times for 1..4 goroutines
   531  // compare with ap and among themselves
   532  func sumtLenS(ar, ap []uint32) float64 {
   533  	s := .0
   534  	for Mxg = 1; Mxg < 5; Mxg++ {
   535  		s += mfcLenS(fmt.Sprintf("sorty-%d", Mxg), func(al []string) { SortLen(al) }, ar, ap)
   536  		ap, ar = ar, ap[:cap(ap)]
   537  	}
   538  	return s
   539  }
   540  
   541  // return sum of SortLen([][]byte) times for 1..4 goroutines
   542  // compare with ap and among themselves
   543  func sumtLenB(ar, ap []uint32) float64 {
   544  	s := .0
   545  	for Mxg = 1; Mxg < 5; Mxg++ {
   546  		s += mfcLenB(fmt.Sprintf("sorty-%d", Mxg), func(al [][]byte) { SortLen(al) }, ar, ap)
   547  		ap, ar = ar, ap[:cap(ap)]
   548  	}
   549  	return s
   550  }
   551  
   552  // sort uint32 slice with Sort()
   553  func sort3i(aq []uint32) {
   554  	lsw := func(i, k, r, s int) bool {
   555  		if aq[i] < aq[k] {
   556  			if r != s {
   557  				aq[r], aq[s] = aq[s], aq[r]
   558  			}
   559  			return true
   560  		}
   561  		return false
   562  	}
   563  	Sort(len(aq), lsw)
   564  }
   565  
   566  // return sum of sort3i() times for 1..4 goroutines
   567  // compare with ap and among themselves
   568  func sumtLswU4(ar, ap []uint32) float64 {
   569  	s := .0
   570  	for Mxg = 1; Mxg < 5; Mxg++ {
   571  		s += mfcU4(fmt.Sprintf("sortyLsw-%d", Mxg), sort3i, ar, ap)
   572  		ap, ar = ar, ap[:cap(ap)]
   573  	}
   574  	return s
   575  }
   576  
   577  // sort float32 slice with Sort()
   578  func sort3f(aq []float32) {
   579  	lsw := func(i, k, r, s int) bool {
   580  		if aq[i] < aq[k] {
   581  			if r != s {
   582  				aq[r], aq[s] = aq[s], aq[r]
   583  			}
   584  			return true
   585  		}
   586  		return false
   587  	}
   588  	Sort(len(aq), lsw)
   589  }
   590  
   591  // return sum of sort3f() times for 1..4 goroutines
   592  // compare with ap and among themselves
   593  func sumtLswF4(ar, ap []float32) float64 {
   594  	s := .0
   595  	for Mxg = 1; Mxg < 5; Mxg++ {
   596  		s += mfcF4(fmt.Sprintf("sortyLsw-%d", Mxg), sort3f, ar, ap)
   597  		ap, ar = ar, ap[:cap(ap)]
   598  	}
   599  	return s
   600  }
   601  
   602  // sort string slice with Sort()
   603  func sort3s(aq []string) {
   604  	lsw := func(i, k, r, s int) bool {
   605  		if aq[i] < aq[k] {
   606  			if r != s {
   607  				aq[r], aq[s] = aq[s], aq[r]
   608  			}
   609  			return true
   610  		}
   611  		return false
   612  	}
   613  	Sort(len(aq), lsw)
   614  }
   615  
   616  // return sum of sort3s() times for 1..4 goroutines
   617  // compare with ap and among themselves
   618  func sumtLswS(ar, ap []uint32) float64 {
   619  	s := .0
   620  	for Mxg = 1; Mxg < 5; Mxg++ {
   621  		s += mfcS(fmt.Sprintf("sortyLsw-%d", Mxg), sort3s, ar, ap)
   622  		ap, ar = ar, ap[:cap(ap)]
   623  	}
   624  	return s
   625  }