使用go1.18版本进行对比。
首先创建 compare_test.go 文件:
package main
import (
"bytes"
"strings"
"testing"
)
var result interface{}
func BenchmarkBytesToString(b *testing.B) {
s1 := []byte("string bytes convert")
b.ResetTimer()
b.ReportAllocs()
var r string
for n := 0; n < b.N; n++ {
r = string(s1)
}
result = r
}
func BenchmarkStringToBytes(b *testing.B) {
s1 := "string bytes convert"
b.ResetTimer()
b.ReportAllocs()
var r []byte
for n := 0; n < b.N; n++ {
r = []byte(s1)
}
result = r
}
func BenchmarkBytesCompareEq(b *testing.B) {
s1 := []byte("string bytes compare a")
s2 := []byte("string bytes compare a")
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = bytes.Compare(s1, s2)
}
result = r
}
func BenchmarkBytesCompareNeq(b *testing.B) {
s1 := []byte("string bytes compare a")
s2 := []byte("string bytes compare b")
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = bytes.Compare(s1, s2)
}
result = r
}
func Benchmark10KBytesCompareEq(b *testing.B) {
var s1, s2 []byte
for i:=0; i<1024; i++ {
s1 = append(s1, []byte("abcdefghij")...)
s2 = append(s2, []byte("abcdefghij")...)
}
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = bytes.Compare(s1, s2)
}
result = r
}
func Benchmark10KBytesCompareNeq(b *testing.B) {
var s1, s2 []byte
for i:=0; i<1024; i++ {
s1 = append(s1, []byte("bacdefghij")...)
s2 = append(s2, []byte("badcfehgji")...)
}
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = bytes.Compare(s1, s2)
}
result = r
}
func Benchmark10KBytesCompareNeqTail(b *testing.B) {
var s1, s2 []byte
for i:=0; i<1024; i++ {
s1 = append(s1, []byte("abcdefghij")...)
s2 = append(s2, []byte("abcdefghij")...)
}
s2 = append(s2[0:1023], 'k')
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = bytes.Compare(s1, s2)
}
result = r
}
func BenchmarkStringCompareEq(b *testing.B) {
s1 := "string bytes compare a"
s2 := "string bytes compare a"
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = strings.Compare(s1, s2)
}
result = r
}
func BenchmarkStringCompareNeq(b *testing.B) {
s1 := "string bytes compare a"
s2 := "string bytes compare b"
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = strings.Compare(s1, s2)
}
result = r
}
func Benchmark10KStringCompareEq(b *testing.B) {
var s1, s2 string
for i:=0; i<1024; i++ {
s1 += "abcdefghij"
s2 += "abcdefghij"
}
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = strings.Compare(s1, s2)
}
result = r
}
func Benchmark10KStringCompareNeq(b *testing.B) {
var s1, s2 string
for i:=0; i<1024; i++ {
s1 += "bacdefghij"
s2 += "badcfehgji"
}
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = strings.Compare(s1, s2)
}
result = r
}
func Benchmark10KStringCompareNeqTail(b *testing.B) {
var s1, s2 string
for i:=0; i<1024; i++ {
s1 += "abcdefghij"
s2 += "abcdefghij"
}
s2 = s2[0:1023] + "k"
b.ResetTimer()
b.ReportAllocs()
var r int
for n := 0; n < b.N; n++ {
r = strings.Compare(s1, s2)
}
result = r
}
然后用原生go进行测试:
[xxxx tmp]$ go test -bench=. compare_test.go
goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i7-5820K CPU @ 3.30GHz
BenchmarkBytesToString-4 50214578 24.18 ns/op 24 B/op 1 allocs/op
BenchmarkStringToBytes-4 38366035 30.74 ns/op 24 B/op 1 allocs/op
BenchmarkBytesCompareEq-4 236118513 5.013 ns/op 0 B/op 0 allocs/op
BenchmarkBytesCompareNeq-4 246910944 4.841 ns/op 0 B/op 0 allocs/op
Benchmark10KBytesCompareEq-4 5201443 229.9 ns/op 0 B/op 0 allocs/op
Benchmark10KBytesCompareNeq-4 258311394 4.555 ns/op 0 B/op 0 allocs/op
Benchmark10KBytesCompareNeqTail-4 47393660 25.12 ns/op 0 B/op 0 allocs/op
BenchmarkStringCompareEq-4 651114916 1.831 ns/op 0 B/op 0 allocs/op
BenchmarkStringCompareNeq-4 100000000 10.06 ns/op 0 B/op 0 allocs/op
Benchmark10KStringCompareEq-4 6436839 186.3 ns/op 0 B/op 0 allocs/op
Benchmark10KStringCompareNeq-4 146472643 8.295 ns/op 0 B/op 0 allocs/op
Benchmark10KStringCompareNeqTail-4 47167117 25.12 ns/op 0 B/op 0 allocs/op
PASS
ok command-line-arguments 17.252s
[xxxx tmp]$
再用gccgo进行测试:
[xxxx tmp]$ /opt/gcc-14.0.0-20230621-el7-x86_64-go1.18-output/bin/go test -bench=. compare_test.go
goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i7-5820K CPU @ 3.30GHz
BenchmarkBytesToString-4 19341158 62.55 ns/op 24 B/op 1 allocs/op
BenchmarkStringToBytes-4 14889639 81.64 ns/op 24 B/op 1 allocs/op
BenchmarkBytesCompareEq-4 29715772 39.86 ns/op 0 B/op 0 allocs/op
BenchmarkBytesCompareNeq-4 29307763 38.69 ns/op 0 B/op 0 allocs/op
Benchmark10KBytesCompareEq-4 187540 6304 ns/op 0 B/op 0 allocs/op
Benchmark10KBytesCompareNeq-4 44679166 27.13 ns/op 0 B/op 0 allocs/op
Benchmark10KBytesCompareNeqTail-4 1838538 649.7 ns/op 0 B/op 0 allocs/op
BenchmarkStringCompareEq-4 1000000000 0.0000017 ns/op 0 B/op 0 allocs/op
BenchmarkStringCompareNeq-4 37796344 31.55 ns/op 0 B/op 0 allocs/op
Benchmark10KStringCompareEq-4 4962115 242.0 ns/op 0 B/op 0 allocs/op
Benchmark10KStringCompareNeq-4 49536991 24.06 ns/op 0 B/op 0 allocs/op
Benchmark10KStringCompareNeqTail-4 1842632 643.9 ns/op 0 B/op 0 allocs/op
PASS
ok command-line-arguments 15.254s
[xxxx tmp]$
结论:
除了对相等的短字符串进行比较gccgo明显快出指数级(怀疑gccgo进行了编译阶段的优化),其他的操作原生go都快于gccgo几倍。
两种go对于byte切片转字符串都快于字符串转byte切片。
对于比较长的内存序列都是不相等的比较快于相等的比较,不相等的位置越靠前越快。
原生go不相等的比较:byte切片快于字符串,gccgo相反:不相等的比较字符串快于byte切片。
综上,如果预判两个内存序列相等就用字符串,如果预判不相等gccgo用字符串、原生go用byte切片。