Performance: Sorting Slice vs Sorting Type (of Slice) with Sort implementation












3















I was playing with some code challenges and found that custom sort (implementation of sort interface) work much faster than just for raw structure of slices. Why is that? Does slice conversion to type do some megic (like converting to slice of pointers to structs)?



I made some code to test my hipotesis



package sortingexample

import (
"sort"
"testing"
)

// Example of struct we going to sort.

type Point struct {
X, Y int
}

// --- Struct / Raw Data
var TestCases = Point{
{10, 3},
{10, 4},
{10, 35},
{10, 5},
{10, 51},
{10, 25},
{10, 59},
{10, 15},
{10, 22},
{10, 91},
}

// Example One - Sorting Slice Directly
// somehow - slowest way to sort it.
func SortSlice(points Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlice(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortSlice(tmp)
}
}

// Example Two - Sorting Slice Directly
// much faster performance
type Points Point

// Sort interface implementation
func (p Points) Less(i, j int) bool { return p[i].Y < p[j].Y }
func (p Points) Len() int { return len(p) }
func (p Points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func SortStruct(points Point) {
sort.Sort(Points(points))
}

func BenchmarkStruct(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortStruct(tmp)
}
}

// --- Pointers
var TestCasesPoints = *Point{
&Point{10, 3},
&Point{10, 4},
&Point{10, 35},
&Point{10, 5},
&Point{10, 51},
&Point{10, 25},
&Point{10, 59},
&Point{10, 15},
&Point{10, 22},
&Point{10, 91},
}

// Example Three - Sorting Slice of Pointers

func SortSlicePointers(points *Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))
for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortSlicePointers(tmp)
}
}

// Example Four - Sorting Struct (with Slice of pointers beneath it)
type PointsPointer *Point

func (pp PointsPointer) Less(i, j int) bool { return pp[i].Y < pp[j].Y }
func (pp PointsPointer) Len() int { return len(pp) }
func (pp PointsPointer) Swap(i, j int) { pp[i], pp[j] = pp[j], pp[i] }

func SortStructOfSlicePointers(points *Point) {
sort.Sort(PointsPointer(points))
}

func BenchmarkStructOfSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))

for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortStructOfSlicePointers(tmp)
}
}


And here are results...



> go test -bench=.
goos: darwin
goarch: amd64
BenchmarkSlice-4 3000000 542 ns/op
BenchmarkStruct-4 5000000 318 ns/op
BenchmarkSlicePointers-4 5000000 280 ns/op
BenchmarkStructOfSlicePointers-4 5000000 321 ns/op


It's obvious that sorting a slice of pointers will work faster, but why does custom sort implementation faster? Are there any resources I can read about it?










share|improve this question

























  • The less functions should compare for less, not less than or equal. The fastest of the four tests uses sort.Slice.

    – ThunderCat
    Jan 20 at 16:39











  • Thank you for your answer. 1) OK (it's an accident). 2) Everything faster if you don't need to move big chunks of memory, pointer faster by default, Question was about Structs.

    – Butuzov
    Jan 20 at 17:01
















3















I was playing with some code challenges and found that custom sort (implementation of sort interface) work much faster than just for raw structure of slices. Why is that? Does slice conversion to type do some megic (like converting to slice of pointers to structs)?



I made some code to test my hipotesis



package sortingexample

import (
"sort"
"testing"
)

// Example of struct we going to sort.

type Point struct {
X, Y int
}

// --- Struct / Raw Data
var TestCases = Point{
{10, 3},
{10, 4},
{10, 35},
{10, 5},
{10, 51},
{10, 25},
{10, 59},
{10, 15},
{10, 22},
{10, 91},
}

// Example One - Sorting Slice Directly
// somehow - slowest way to sort it.
func SortSlice(points Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlice(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortSlice(tmp)
}
}

// Example Two - Sorting Slice Directly
// much faster performance
type Points Point

// Sort interface implementation
func (p Points) Less(i, j int) bool { return p[i].Y < p[j].Y }
func (p Points) Len() int { return len(p) }
func (p Points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func SortStruct(points Point) {
sort.Sort(Points(points))
}

func BenchmarkStruct(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortStruct(tmp)
}
}

// --- Pointers
var TestCasesPoints = *Point{
&Point{10, 3},
&Point{10, 4},
&Point{10, 35},
&Point{10, 5},
&Point{10, 51},
&Point{10, 25},
&Point{10, 59},
&Point{10, 15},
&Point{10, 22},
&Point{10, 91},
}

// Example Three - Sorting Slice of Pointers

func SortSlicePointers(points *Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))
for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortSlicePointers(tmp)
}
}

// Example Four - Sorting Struct (with Slice of pointers beneath it)
type PointsPointer *Point

func (pp PointsPointer) Less(i, j int) bool { return pp[i].Y < pp[j].Y }
func (pp PointsPointer) Len() int { return len(pp) }
func (pp PointsPointer) Swap(i, j int) { pp[i], pp[j] = pp[j], pp[i] }

func SortStructOfSlicePointers(points *Point) {
sort.Sort(PointsPointer(points))
}

func BenchmarkStructOfSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))

for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortStructOfSlicePointers(tmp)
}
}


And here are results...



> go test -bench=.
goos: darwin
goarch: amd64
BenchmarkSlice-4 3000000 542 ns/op
BenchmarkStruct-4 5000000 318 ns/op
BenchmarkSlicePointers-4 5000000 280 ns/op
BenchmarkStructOfSlicePointers-4 5000000 321 ns/op


It's obvious that sorting a slice of pointers will work faster, but why does custom sort implementation faster? Are there any resources I can read about it?










share|improve this question

























  • The less functions should compare for less, not less than or equal. The fastest of the four tests uses sort.Slice.

    – ThunderCat
    Jan 20 at 16:39











  • Thank you for your answer. 1) OK (it's an accident). 2) Everything faster if you don't need to move big chunks of memory, pointer faster by default, Question was about Structs.

    – Butuzov
    Jan 20 at 17:01














3












3








3








I was playing with some code challenges and found that custom sort (implementation of sort interface) work much faster than just for raw structure of slices. Why is that? Does slice conversion to type do some megic (like converting to slice of pointers to structs)?



I made some code to test my hipotesis



package sortingexample

import (
"sort"
"testing"
)

// Example of struct we going to sort.

type Point struct {
X, Y int
}

// --- Struct / Raw Data
var TestCases = Point{
{10, 3},
{10, 4},
{10, 35},
{10, 5},
{10, 51},
{10, 25},
{10, 59},
{10, 15},
{10, 22},
{10, 91},
}

// Example One - Sorting Slice Directly
// somehow - slowest way to sort it.
func SortSlice(points Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlice(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortSlice(tmp)
}
}

// Example Two - Sorting Slice Directly
// much faster performance
type Points Point

// Sort interface implementation
func (p Points) Less(i, j int) bool { return p[i].Y < p[j].Y }
func (p Points) Len() int { return len(p) }
func (p Points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func SortStruct(points Point) {
sort.Sort(Points(points))
}

func BenchmarkStruct(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortStruct(tmp)
}
}

// --- Pointers
var TestCasesPoints = *Point{
&Point{10, 3},
&Point{10, 4},
&Point{10, 35},
&Point{10, 5},
&Point{10, 51},
&Point{10, 25},
&Point{10, 59},
&Point{10, 15},
&Point{10, 22},
&Point{10, 91},
}

// Example Three - Sorting Slice of Pointers

func SortSlicePointers(points *Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))
for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortSlicePointers(tmp)
}
}

// Example Four - Sorting Struct (with Slice of pointers beneath it)
type PointsPointer *Point

func (pp PointsPointer) Less(i, j int) bool { return pp[i].Y < pp[j].Y }
func (pp PointsPointer) Len() int { return len(pp) }
func (pp PointsPointer) Swap(i, j int) { pp[i], pp[j] = pp[j], pp[i] }

func SortStructOfSlicePointers(points *Point) {
sort.Sort(PointsPointer(points))
}

func BenchmarkStructOfSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))

for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortStructOfSlicePointers(tmp)
}
}


And here are results...



> go test -bench=.
goos: darwin
goarch: amd64
BenchmarkSlice-4 3000000 542 ns/op
BenchmarkStruct-4 5000000 318 ns/op
BenchmarkSlicePointers-4 5000000 280 ns/op
BenchmarkStructOfSlicePointers-4 5000000 321 ns/op


It's obvious that sorting a slice of pointers will work faster, but why does custom sort implementation faster? Are there any resources I can read about it?










share|improve this question
















I was playing with some code challenges and found that custom sort (implementation of sort interface) work much faster than just for raw structure of slices. Why is that? Does slice conversion to type do some megic (like converting to slice of pointers to structs)?



I made some code to test my hipotesis



package sortingexample

import (
"sort"
"testing"
)

// Example of struct we going to sort.

type Point struct {
X, Y int
}

// --- Struct / Raw Data
var TestCases = Point{
{10, 3},
{10, 4},
{10, 35},
{10, 5},
{10, 51},
{10, 25},
{10, 59},
{10, 15},
{10, 22},
{10, 91},
}

// Example One - Sorting Slice Directly
// somehow - slowest way to sort it.
func SortSlice(points Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlice(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortSlice(tmp)
}
}

// Example Two - Sorting Slice Directly
// much faster performance
type Points Point

// Sort interface implementation
func (p Points) Less(i, j int) bool { return p[i].Y < p[j].Y }
func (p Points) Len() int { return len(p) }
func (p Points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func SortStruct(points Point) {
sort.Sort(Points(points))
}

func BenchmarkStruct(b *testing.B) {
tmp := make(Point, len(TestCases))
for i := 0; i < b.N; i++ {
copy(tmp, TestCases)
SortStruct(tmp)
}
}

// --- Pointers
var TestCasesPoints = *Point{
&Point{10, 3},
&Point{10, 4},
&Point{10, 35},
&Point{10, 5},
&Point{10, 51},
&Point{10, 25},
&Point{10, 59},
&Point{10, 15},
&Point{10, 22},
&Point{10, 91},
}

// Example Three - Sorting Slice of Pointers

func SortSlicePointers(points *Point) {
sort.Slice(points, func(i, j int) bool {
return points[i].Y < points[j].Y
})
}

func BenchmarkSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))
for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortSlicePointers(tmp)
}
}

// Example Four - Sorting Struct (with Slice of pointers beneath it)
type PointsPointer *Point

func (pp PointsPointer) Less(i, j int) bool { return pp[i].Y < pp[j].Y }
func (pp PointsPointer) Len() int { return len(pp) }
func (pp PointsPointer) Swap(i, j int) { pp[i], pp[j] = pp[j], pp[i] }

func SortStructOfSlicePointers(points *Point) {
sort.Sort(PointsPointer(points))
}

func BenchmarkStructOfSlicePointers(b *testing.B) {
tmp := make(*Point, len(TestCasesPoints))

for i := 0; i < b.N; i++ {
copy(tmp, TestCasesPoints)
SortStructOfSlicePointers(tmp)
}
}


And here are results...



> go test -bench=.
goos: darwin
goarch: amd64
BenchmarkSlice-4 3000000 542 ns/op
BenchmarkStruct-4 5000000 318 ns/op
BenchmarkSlicePointers-4 5000000 280 ns/op
BenchmarkStructOfSlicePointers-4 5000000 321 ns/op


It's obvious that sorting a slice of pointers will work faster, but why does custom sort implementation faster? Are there any resources I can read about it?







sorting go slice






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 20 at 16:59







Butuzov

















asked Jan 20 at 12:10









ButuzovButuzov

1,22221116




1,22221116













  • The less functions should compare for less, not less than or equal. The fastest of the four tests uses sort.Slice.

    – ThunderCat
    Jan 20 at 16:39











  • Thank you for your answer. 1) OK (it's an accident). 2) Everything faster if you don't need to move big chunks of memory, pointer faster by default, Question was about Structs.

    – Butuzov
    Jan 20 at 17:01



















  • The less functions should compare for less, not less than or equal. The fastest of the four tests uses sort.Slice.

    – ThunderCat
    Jan 20 at 16:39











  • Thank you for your answer. 1) OK (it's an accident). 2) Everything faster if you don't need to move big chunks of memory, pointer faster by default, Question was about Structs.

    – Butuzov
    Jan 20 at 17:01

















The less functions should compare for less, not less than or equal. The fastest of the four tests uses sort.Slice.

– ThunderCat
Jan 20 at 16:39





The less functions should compare for less, not less than or equal. The fastest of the four tests uses sort.Slice.

– ThunderCat
Jan 20 at 16:39













Thank you for your answer. 1) OK (it's an accident). 2) Everything faster if you don't need to move big chunks of memory, pointer faster by default, Question was about Structs.

– Butuzov
Jan 20 at 17:01





Thank you for your answer. 1) OK (it's an accident). 2) Everything faster if you don't need to move big chunks of memory, pointer faster by default, Question was about Structs.

– Butuzov
Jan 20 at 17:01












1 Answer
1






active

oldest

votes


















5














The general sort.Slice() and sort.SliceStable() functions work on any slices. You have to pass your slice value as an interface{} value, and the implementation has to use reflection (the reflect package) to access its elements and length, and to perform the swaps of elements.



In contrast, when you implement the sort.Interface type yourself, in your implementation you have access to the static type of your slice, and you can provide the implementation of the sort.Interface without relfection, this is what will make it faster.



So if performance is critical / important, always provide the sort.Interface implementation yourself. If the slices are small or performance is not important, you may use the more convenient sort.Slice() function.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54276285%2fperformance-sorting-slice-vs-sorting-type-of-slice-with-sort-implementation%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    5














    The general sort.Slice() and sort.SliceStable() functions work on any slices. You have to pass your slice value as an interface{} value, and the implementation has to use reflection (the reflect package) to access its elements and length, and to perform the swaps of elements.



    In contrast, when you implement the sort.Interface type yourself, in your implementation you have access to the static type of your slice, and you can provide the implementation of the sort.Interface without relfection, this is what will make it faster.



    So if performance is critical / important, always provide the sort.Interface implementation yourself. If the slices are small or performance is not important, you may use the more convenient sort.Slice() function.






    share|improve this answer




























      5














      The general sort.Slice() and sort.SliceStable() functions work on any slices. You have to pass your slice value as an interface{} value, and the implementation has to use reflection (the reflect package) to access its elements and length, and to perform the swaps of elements.



      In contrast, when you implement the sort.Interface type yourself, in your implementation you have access to the static type of your slice, and you can provide the implementation of the sort.Interface without relfection, this is what will make it faster.



      So if performance is critical / important, always provide the sort.Interface implementation yourself. If the slices are small or performance is not important, you may use the more convenient sort.Slice() function.






      share|improve this answer


























        5












        5








        5







        The general sort.Slice() and sort.SliceStable() functions work on any slices. You have to pass your slice value as an interface{} value, and the implementation has to use reflection (the reflect package) to access its elements and length, and to perform the swaps of elements.



        In contrast, when you implement the sort.Interface type yourself, in your implementation you have access to the static type of your slice, and you can provide the implementation of the sort.Interface without relfection, this is what will make it faster.



        So if performance is critical / important, always provide the sort.Interface implementation yourself. If the slices are small or performance is not important, you may use the more convenient sort.Slice() function.






        share|improve this answer













        The general sort.Slice() and sort.SliceStable() functions work on any slices. You have to pass your slice value as an interface{} value, and the implementation has to use reflection (the reflect package) to access its elements and length, and to perform the swaps of elements.



        In contrast, when you implement the sort.Interface type yourself, in your implementation you have access to the static type of your slice, and you can provide the implementation of the sort.Interface without relfection, this is what will make it faster.



        So if performance is critical / important, always provide the sort.Interface implementation yourself. If the slices are small or performance is not important, you may use the more convenient sort.Slice() function.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jan 20 at 12:33









        iczaicza

        168k25336370




        168k25336370
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54276285%2fperformance-sorting-slice-vs-sorting-type-of-slice-with-sort-implementation%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Liquibase includeAll doesn't find base path

            How to use setInterval in EJS file?

            Petrus Granier-Deferre