Performance: Sorting Slice vs Sorting Type (of Slice) with Sort implementation
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
add a comment |
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
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 aboutStructs
.
– Butuzov
Jan 20 at 17:01
add a comment |
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
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
sorting go slice
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 aboutStructs
.
– Butuzov
Jan 20 at 17:01
add a comment |
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 aboutStructs
.
– 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
add a comment |
1 Answer
1
active
oldest
votes
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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
answered Jan 20 at 12:33
iczaicza
168k25336370
168k25336370
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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