Skip to content

Commit 178e34b

Browse files
committed
Reorganize project
1 parent 7622d06 commit 178e34b

22 files changed

+3970
-325
lines changed

README.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
11
# go-sort
22

3-
A collection of Go sort experiments and implementations, including a naive timsort I wrote from scratch. The goal of this project was to write basic versions of **timsort** and **pdqsort** that are still similar to their basic mergesort and quicksort counterparts (minimal magic constants, etc.) and validate their performance improvement.
3+
A collection of Go sort experiments and implementations.
44

5-
### Merge sorts
5+
The goal of this project is to implement basic versions of **timsort** and **pdqsort** with minimal magic constants, etc. and validate their performance improvement over standard mergesort and quicksort.
6+
7+
Also included are:
8+
9+
* RadixSort (using counting sort on decimal digits)
10+
* heapSort (from go/pkg/sort)
11+
* insertionSort (from go/pkg/sort)
12+
13+
### Quicksort
14+
15+
| Function | Source | Notes |
16+
|------------|--------|-------|
17+
| QuickSort1 | CLRS | Naive pivot selection (A[r]) |
18+
| QuickSort2 | [golang pkg/sort](https://golang.org/pkg/sort/#Sort) | Uses median-of-three partitioning, drops to insertionSort and heapSort in some cases |
19+
| QuickSort3 | CLRS | Randomized pivot selection |
20+
| PdqSort | [pdqsort](https://github.com/orlp/pdqsort) | No block quicksort - QuickSort2 with added bad partition detection and elimination |
21+
22+
QuickSort1 vs QuickSort3 shows the importance of picking good pivots in quicksort.
23+
24+
Parts of PdqSort implemented:
25+
26+
1. Check partitions that are bad and do swaps to fix them
27+
1. The Go QuickSort2 implementation is already always descending towards heapSort so leave that unchanged
28+
29+
Additional reading for quicksort:
30+
31+
1. S. Edelkamp, A. Weiß, "BlockQuicksort: Avoiding Branch Mispredictions in Quicksort", [link](https://pdfs.semanticscholar.org/b24e/f8021811cd4ef0fcc96a770657b664ee5b52.pdf)
32+
2. M. D. McIlroy, "A Killer Adversary for Quicksort", [link](https://www.cs.dartmouth.edu/~doug/mdmspe.pdf)
33+
3. The above adversary test in Go's sort pkg [tests](https://github.com/golang/go/blob/master/src/sort/sort_test.go#L455)
34+
35+
### Mergesort
636

737
| Function | Source | Notes |
838
|------------|--------|-------|
939
| MergeSort1 | CLRS | Basic recursive implementation |
1040
| MergeSort2 | [golang pkg/sort](https://golang.org/pkg/sort/#Stable) | Implements in-place symmetric merge ([[1]](https://www.semanticscholar.org/paper/Stable-Minimum-Storage-Merging-by-Symmetric-Kim-Kutzner/d664cee462cb8e6a8ae2a1a7c6bab1b5f81e0618)) and does insertion sort by blocks |
11-
| MergeSort3 | Various timsort sources | No galloping, and uses the same in-place symmetric merge ([1]) as above |
12-
| MergeSort4 | Goodrich & Tamassia | Bottom-up iterative merge sort |
41+
| MergeSort3 | Goodrich & Tamassia | Bottom-up iterative merge sort |
42+
| TimSort | Various timsort sources | No galloping, and uses the same in-place symmetric merge as MergeSort2 |
1343

14-
Parts of TimSort implemented in MergeSort3:
44+
Parts of TimSort implemented:
1545

1646
1. Calculate minimum ascending runs of at least 32 in length (insertion sort to 32 if less)
1747
1. If the descending run is bigger, reverse it in-place and use that as the minrun
@@ -41,7 +71,7 @@ for len(boundaries) > 1 {
4171
Benches for 1 million random ints:
4272

4373
```
44-
sevagh:go-mergesort $ go test -benchmem -run=^a -bench='.*Random1048576$' -v
74+
sevagh:go-mergesort $ go test -benchmem -run=^a -bench='.*Merge.*Random1048576$' -v
4575
goos: linux
4676
goarch: amd64
4777
pkg: github.com/sevagh/go-mergesort
@@ -54,3 +84,8 @@ ok github.com/sevagh/go-mergesort 12.053s
5484
```
5585

5686
I'm surprised by how well my naive implementation of timsort is performing - I'm sure it can be made better.
87+
88+
Additional reading for timsort:
89+
90+
1. Python [implementation](https://github.com/python/cpython/blob/master/Objects/listobject.c) and [description](https://github.com/python/cpython/blob/master/Objects/listsort.txt)
91+
2. High level descriptions of timsort I used to write my implementation: [here](https://medium.com/@rylanbauermeister/understanding-timsort-191c758a42f3?) and [here](https://wiki.c2.com/?TimSort)
File renamed without changes.

mergesort1_benches_test.go renamed to bench_mergesort1_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,9 @@ func BenchmarkMergeSort1PushMiddle1024(b *testing.B) {
475475
}
476476
}
477477

478-
func BenchmarkMergeSort1Random8092(b *testing.B) {
478+
func BenchmarkMergeSort1Random8192(b *testing.B) {
479479
b.StopTimer()
480-
nums := RandomInt(8092)
480+
nums := RandomInt(8192)
481481
b.StartTimer()
482482
for i := 0; i < b.N; i++ {
483483
gosort.MergeSort1(nums)
@@ -488,9 +488,9 @@ func BenchmarkMergeSort1Random8092(b *testing.B) {
488488
}
489489
}
490490

491-
func BenchmarkMergeSort1Shuffled8092(b *testing.B) {
491+
func BenchmarkMergeSort1Shuffled8192(b *testing.B) {
492492
b.StopTimer()
493-
nums := ShuffledInt(8092)
493+
nums := ShuffledInt(8192)
494494
b.StartTimer()
495495
for i := 0; i < b.N; i++ {
496496
gosort.MergeSort1(nums)
@@ -501,9 +501,9 @@ func BenchmarkMergeSort1Shuffled8092(b *testing.B) {
501501
}
502502
}
503503

504-
func BenchmarkMergeSort1Shuffled16Values8092(b *testing.B) {
504+
func BenchmarkMergeSort1Shuffled16Values8192(b *testing.B) {
505505
b.StopTimer()
506-
nums := Shuffled16ValuesInt(8092)
506+
nums := Shuffled16ValuesInt(8192)
507507
b.StartTimer()
508508
for i := 0; i < b.N; i++ {
509509
gosort.MergeSort1(nums)
@@ -514,9 +514,9 @@ func BenchmarkMergeSort1Shuffled16Values8092(b *testing.B) {
514514
}
515515
}
516516

517-
func BenchmarkMergeSort1AllEqual8092(b *testing.B) {
517+
func BenchmarkMergeSort1AllEqual8192(b *testing.B) {
518518
b.StopTimer()
519-
nums := AllEqualInt(8092)
519+
nums := AllEqualInt(8192)
520520
b.StartTimer()
521521
for i := 0; i < b.N; i++ {
522522
gosort.MergeSort1(nums)
@@ -527,9 +527,9 @@ func BenchmarkMergeSort1AllEqual8092(b *testing.B) {
527527
}
528528
}
529529

530-
func BenchmarkMergeSort1Ascending8092(b *testing.B) {
530+
func BenchmarkMergeSort1Ascending8192(b *testing.B) {
531531
b.StopTimer()
532-
nums := AscendingInt(8092)
532+
nums := AscendingInt(8192)
533533
b.StartTimer()
534534
for i := 0; i < b.N; i++ {
535535
gosort.MergeSort1(nums)
@@ -540,9 +540,9 @@ func BenchmarkMergeSort1Ascending8092(b *testing.B) {
540540
}
541541
}
542542

543-
func BenchmarkMergeSort1Descending8092(b *testing.B) {
543+
func BenchmarkMergeSort1Descending8192(b *testing.B) {
544544
b.StopTimer()
545-
nums := DescendingInt(8092)
545+
nums := DescendingInt(8192)
546546
b.StartTimer()
547547
for i := 0; i < b.N; i++ {
548548
gosort.MergeSort1(nums)
@@ -553,9 +553,9 @@ func BenchmarkMergeSort1Descending8092(b *testing.B) {
553553
}
554554
}
555555

556-
func BenchmarkMergeSort1PipeOrgan8092(b *testing.B) {
556+
func BenchmarkMergeSort1PipeOrgan8192(b *testing.B) {
557557
b.StopTimer()
558-
nums := PipeOrganInt(8092)
558+
nums := PipeOrganInt(8192)
559559
b.StartTimer()
560560
for i := 0; i < b.N; i++ {
561561
gosort.MergeSort1(nums)
@@ -566,9 +566,9 @@ func BenchmarkMergeSort1PipeOrgan8092(b *testing.B) {
566566
}
567567
}
568568

569-
func BenchmarkMergeSort1PushFront8092(b *testing.B) {
569+
func BenchmarkMergeSort1PushFront8192(b *testing.B) {
570570
b.StopTimer()
571-
nums := PushFrontInt(8092)
571+
nums := PushFrontInt(8192)
572572
b.StartTimer()
573573
for i := 0; i < b.N; i++ {
574574
gosort.MergeSort1(nums)
@@ -579,9 +579,9 @@ func BenchmarkMergeSort1PushFront8092(b *testing.B) {
579579
}
580580
}
581581

582-
func BenchmarkMergeSort1PushMiddle8092(b *testing.B) {
582+
func BenchmarkMergeSort1PushMiddle8192(b *testing.B) {
583583
b.StopTimer()
584-
nums := PushMiddleInt(8092)
584+
nums := PushMiddleInt(8192)
585585
b.StartTimer()
586586
for i := 0; i < b.N; i++ {
587587
gosort.MergeSort1(nums)

mergesort2_benches_test.go renamed to bench_mergesort2_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,9 @@ func BenchmarkMergeSort2PushMiddle1024(b *testing.B) {
475475
}
476476
}
477477

478-
func BenchmarkMergeSort2Random8092(b *testing.B) {
478+
func BenchmarkMergeSort2Random8192(b *testing.B) {
479479
b.StopTimer()
480-
nums := RandomInt(8092)
480+
nums := RandomInt(8192)
481481
b.StartTimer()
482482
for i := 0; i < b.N; i++ {
483483
gosort.MergeSort2(nums)
@@ -488,9 +488,9 @@ func BenchmarkMergeSort2Random8092(b *testing.B) {
488488
}
489489
}
490490

491-
func BenchmarkMergeSort2Shuffled8092(b *testing.B) {
491+
func BenchmarkMergeSort2Shuffled8192(b *testing.B) {
492492
b.StopTimer()
493-
nums := ShuffledInt(8092)
493+
nums := ShuffledInt(8192)
494494
b.StartTimer()
495495
for i := 0; i < b.N; i++ {
496496
gosort.MergeSort2(nums)
@@ -501,9 +501,9 @@ func BenchmarkMergeSort2Shuffled8092(b *testing.B) {
501501
}
502502
}
503503

504-
func BenchmarkMergeSort2Shuffled16Values8092(b *testing.B) {
504+
func BenchmarkMergeSort2Shuffled16Values8192(b *testing.B) {
505505
b.StopTimer()
506-
nums := Shuffled16ValuesInt(8092)
506+
nums := Shuffled16ValuesInt(8192)
507507
b.StartTimer()
508508
for i := 0; i < b.N; i++ {
509509
gosort.MergeSort2(nums)
@@ -514,9 +514,9 @@ func BenchmarkMergeSort2Shuffled16Values8092(b *testing.B) {
514514
}
515515
}
516516

517-
func BenchmarkMergeSort2AllEqual8092(b *testing.B) {
517+
func BenchmarkMergeSort2AllEqual8192(b *testing.B) {
518518
b.StopTimer()
519-
nums := AllEqualInt(8092)
519+
nums := AllEqualInt(8192)
520520
b.StartTimer()
521521
for i := 0; i < b.N; i++ {
522522
gosort.MergeSort2(nums)
@@ -527,9 +527,9 @@ func BenchmarkMergeSort2AllEqual8092(b *testing.B) {
527527
}
528528
}
529529

530-
func BenchmarkMergeSort2Ascending8092(b *testing.B) {
530+
func BenchmarkMergeSort2Ascending8192(b *testing.B) {
531531
b.StopTimer()
532-
nums := AscendingInt(8092)
532+
nums := AscendingInt(8192)
533533
b.StartTimer()
534534
for i := 0; i < b.N; i++ {
535535
gosort.MergeSort2(nums)
@@ -540,9 +540,9 @@ func BenchmarkMergeSort2Ascending8092(b *testing.B) {
540540
}
541541
}
542542

543-
func BenchmarkMergeSort2Descending8092(b *testing.B) {
543+
func BenchmarkMergeSort2Descending8192(b *testing.B) {
544544
b.StopTimer()
545-
nums := DescendingInt(8092)
545+
nums := DescendingInt(8192)
546546
b.StartTimer()
547547
for i := 0; i < b.N; i++ {
548548
gosort.MergeSort2(nums)
@@ -553,9 +553,9 @@ func BenchmarkMergeSort2Descending8092(b *testing.B) {
553553
}
554554
}
555555

556-
func BenchmarkMergeSort2PipeOrgan8092(b *testing.B) {
556+
func BenchmarkMergeSort2PipeOrgan8192(b *testing.B) {
557557
b.StopTimer()
558-
nums := PipeOrganInt(8092)
558+
nums := PipeOrganInt(8192)
559559
b.StartTimer()
560560
for i := 0; i < b.N; i++ {
561561
gosort.MergeSort2(nums)
@@ -566,9 +566,9 @@ func BenchmarkMergeSort2PipeOrgan8092(b *testing.B) {
566566
}
567567
}
568568

569-
func BenchmarkMergeSort2PushFront8092(b *testing.B) {
569+
func BenchmarkMergeSort2PushFront8192(b *testing.B) {
570570
b.StopTimer()
571-
nums := PushFrontInt(8092)
571+
nums := PushFrontInt(8192)
572572
b.StartTimer()
573573
for i := 0; i < b.N; i++ {
574574
gosort.MergeSort2(nums)
@@ -579,9 +579,9 @@ func BenchmarkMergeSort2PushFront8092(b *testing.B) {
579579
}
580580
}
581581

582-
func BenchmarkMergeSort2PushMiddle8092(b *testing.B) {
582+
func BenchmarkMergeSort2PushMiddle8192(b *testing.B) {
583583
b.StopTimer()
584-
nums := PushMiddleInt(8092)
584+
nums := PushMiddleInt(8192)
585585
b.StartTimer()
586586
for i := 0; i < b.N; i++ {
587587
gosort.MergeSort2(nums)

mergesort3_benches_test.go renamed to bench_mergesort3_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,9 @@ func BenchmarkMergeSort3PushMiddle1024(b *testing.B) {
475475
}
476476
}
477477

478-
func BenchmarkMergeSort3Random8092(b *testing.B) {
478+
func BenchmarkMergeSort3Random8192(b *testing.B) {
479479
b.StopTimer()
480-
nums := RandomInt(8092)
480+
nums := RandomInt(8192)
481481
b.StartTimer()
482482
for i := 0; i < b.N; i++ {
483483
gosort.MergeSort3(nums)
@@ -488,9 +488,9 @@ func BenchmarkMergeSort3Random8092(b *testing.B) {
488488
}
489489
}
490490

491-
func BenchmarkMergeSort3Shuffled8092(b *testing.B) {
491+
func BenchmarkMergeSort3Shuffled8192(b *testing.B) {
492492
b.StopTimer()
493-
nums := ShuffledInt(8092)
493+
nums := ShuffledInt(8192)
494494
b.StartTimer()
495495
for i := 0; i < b.N; i++ {
496496
gosort.MergeSort3(nums)
@@ -501,9 +501,9 @@ func BenchmarkMergeSort3Shuffled8092(b *testing.B) {
501501
}
502502
}
503503

504-
func BenchmarkMergeSort3Shuffled16Values8092(b *testing.B) {
504+
func BenchmarkMergeSort3Shuffled16Values8192(b *testing.B) {
505505
b.StopTimer()
506-
nums := Shuffled16ValuesInt(8092)
506+
nums := Shuffled16ValuesInt(8192)
507507
b.StartTimer()
508508
for i := 0; i < b.N; i++ {
509509
gosort.MergeSort3(nums)
@@ -514,9 +514,9 @@ func BenchmarkMergeSort3Shuffled16Values8092(b *testing.B) {
514514
}
515515
}
516516

517-
func BenchmarkMergeSort3AllEqual8092(b *testing.B) {
517+
func BenchmarkMergeSort3AllEqual8192(b *testing.B) {
518518
b.StopTimer()
519-
nums := AllEqualInt(8092)
519+
nums := AllEqualInt(8192)
520520
b.StartTimer()
521521
for i := 0; i < b.N; i++ {
522522
gosort.MergeSort3(nums)
@@ -527,9 +527,9 @@ func BenchmarkMergeSort3AllEqual8092(b *testing.B) {
527527
}
528528
}
529529

530-
func BenchmarkMergeSort3Ascending8092(b *testing.B) {
530+
func BenchmarkMergeSort3Ascending8192(b *testing.B) {
531531
b.StopTimer()
532-
nums := AscendingInt(8092)
532+
nums := AscendingInt(8192)
533533
b.StartTimer()
534534
for i := 0; i < b.N; i++ {
535535
gosort.MergeSort3(nums)
@@ -540,9 +540,9 @@ func BenchmarkMergeSort3Ascending8092(b *testing.B) {
540540
}
541541
}
542542

543-
func BenchmarkMergeSort3Descending8092(b *testing.B) {
543+
func BenchmarkMergeSort3Descending8192(b *testing.B) {
544544
b.StopTimer()
545-
nums := DescendingInt(8092)
545+
nums := DescendingInt(8192)
546546
b.StartTimer()
547547
for i := 0; i < b.N; i++ {
548548
gosort.MergeSort3(nums)
@@ -553,9 +553,9 @@ func BenchmarkMergeSort3Descending8092(b *testing.B) {
553553
}
554554
}
555555

556-
func BenchmarkMergeSort3PipeOrgan8092(b *testing.B) {
556+
func BenchmarkMergeSort3PipeOrgan8192(b *testing.B) {
557557
b.StopTimer()
558-
nums := PipeOrganInt(8092)
558+
nums := PipeOrganInt(8192)
559559
b.StartTimer()
560560
for i := 0; i < b.N; i++ {
561561
gosort.MergeSort3(nums)
@@ -566,9 +566,9 @@ func BenchmarkMergeSort3PipeOrgan8092(b *testing.B) {
566566
}
567567
}
568568

569-
func BenchmarkMergeSort3PushFront8092(b *testing.B) {
569+
func BenchmarkMergeSort3PushFront8192(b *testing.B) {
570570
b.StopTimer()
571-
nums := PushFrontInt(8092)
571+
nums := PushFrontInt(8192)
572572
b.StartTimer()
573573
for i := 0; i < b.N; i++ {
574574
gosort.MergeSort3(nums)
@@ -579,9 +579,9 @@ func BenchmarkMergeSort3PushFront8092(b *testing.B) {
579579
}
580580
}
581581

582-
func BenchmarkMergeSort3PushMiddle8092(b *testing.B) {
582+
func BenchmarkMergeSort3PushMiddle8192(b *testing.B) {
583583
b.StopTimer()
584-
nums := PushMiddleInt(8092)
584+
nums := PushMiddleInt(8192)
585585
b.StartTimer()
586586
for i := 0; i < b.N; i++ {
587587
gosort.MergeSort3(nums)

0 commit comments

Comments
 (0)