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 }