Open5
スライス操作
package benchmark
import (
"testing"
)
func BenchmarkA(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
ss := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
s := "5"
A(ss, s)
}
}
func BenchmarkB(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
ss := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
s := "5"
B(ss, s)
}
}
// 要素がユニークであることが前提
func A(ss []string, s string) []string {
r := make([]string, 0, len(ss)-1)
for i := range ss {
if ss[i] == s {
r = append([]string{ss[i]}, r...)
continue
}
r = append(r, ss[i])
}
return r
}
// 要素がユニークであることが前提
func B(ss []string, s string) []string {
for i := range ss {
if ss[i] == s {
v := ss[i]
ss = append([]string{v}, append(ss[:i], ss[i+1:]...)...)
}
}
return ss
}
BenchmarkA-4 1881720 861.2 ns/op 400 B/op 4 allocs/op
BenchmarkB-4 4549692 238.9 ns/op 176 B/op 2 allocs/op
func BenchmarkC(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
ss := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
s := "5"
C(ss, s)
}
}
func C(ss []string, s string) []string {
for i := range ss {
if ss[i] == s {
v := ss[i]
ss = append(ss[:i], ss[i+1:]...)
ss = append([]string{v}, ss...)
}
}
return ss
}
BenchmarkC-4 5156166 231.0 ns/op 176 B/op 2 allocs/op
func BenchmarkD(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
ss := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
s := "5"
D(ss, s)
}
}
func D(ss []string, s string) []string {
for i, v := range ss {
if ss[i] == s {
ss = append([]string{v}, append(ss[:i], ss[i+1:]...)...)
}
}
return ss
}
改めて全て計測
BenchmarkA-4 2718588 424.5 ns/op
BenchmarkB-4 5561485 231.6 ns/op
BenchmarkC-4 5404134 224.3 ns/op
BenchmarkD-4 4500566 235.8 ns/op
// moveToFront moves needle to the front of haystack, in place if possible.
func moveToFront(needle string, haystack []string) []string {
if len(haystack) != 0 && haystack[0] == needle {
return haystack
}
prev := needle
for i, elem := range haystack {
switch {
case i == 0:
haystack[0] = needle
prev = elem
case elem == needle:
haystack[i] = prev
return haystack
default:
haystack[i] = prev
prev = elem
}
}
return append(haystack, prev)
}
haystack := []string{"a", "b", "c", "d", "e"} // [a b c d e]
haystack = moveToFront("c", haystack) // [c a b d e]
haystack = moveToFront("f", haystack) // [f c a b d e]
出典:https://github.com/golang/go/wiki/SliceTricks
先頭にneedleを詰めた後は、needleが元々いた要素に遭遇するまでは、要素を一つずつ後ろにずらし、
needleが元々いた要素に遭遇したら一つ前の要素をneedleがneedleが元々いた要素に詰め替えreturnする。(needleが元々いた要素以降は不変)
https://github.com/thoas/go-funk を使ってみる。
func BenchmarkE(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
ss := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
s := "5"
E(ss, s)
}
}
func E(ss []string, s string) []string {
ss = funk.SubtractString(ss, []string{s})
return append([]string{s}, ss...)
}
BenchmarkE-4 770540 1511 ns/op 947 B/op 7 allocs/op
予想はしていたがアロケートが多いしパフォーマンスは落ちる。