Głównym powodem jest różnica w kosztach połączenia bytes.HasPrefix()
i strings.HasPrefix()
. Jak zauważył @tomasz w swoim komentarzu, strings.HashPrefix()
jest domyślnie wstawiane, podczas gdy bytes.HasPrefix()
nie jest.
Kolejnym powodem są różne typy parametrów: bytes.HasPrefix()
zajmuje 2 plasterki (deskryptory 2 plasterki). strings.HasPrefix()
zajmuje 2 ciągi (2 nagłówki). Deskryptory plasterków zawierają wskaźnik i s: długość i pojemność, patrz reflect.SliceHeader
. Łańcuchy nagłówków zawierają tylko wskaźnik i int
: długość, patrz reflect.StringHeader
.
Możemy to udowodnić, jeśli ręcznie wliścimy funkcje HasPrefix()
w funkcjach testu porównawczego, więc eliminujemy koszty połączeń (zero zarówno). Przez zaznaczenie ich nie zostanie wywołana żadna funkcja (do nich).
HasPrefix()
implementacje:
// HasPrefix tests whether the byte slice s begins with prefix.
func HasPrefix(s, prefix []byte) bool {
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
}
// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
funkcje Benchmark po ich inline:
func BenchmarkStrHasPrefix(b *testing.B) {
s, prefix := STR, PREFIX
for i := 0; i < b.N; i++ {
_ = len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
}
func BenchmarkBytHasPrefix(b *testing.B) {
s, prefix := STR_B, PREFIX_B
for i := 0; i < b.N; i++ {
_ = len(s) >= len(prefix) && bytes.Equal(s[0:len(prefix)], prefix)
}
}
Running nich można uzyskać bardzo zbliżone wyniki:
BenchmarkStrHasPrefix-2 300000000 5.88 ns/op
BenchmarkBytHasPrefix-2 200000000 6.17 ns/op
Powodem małej różnicy w Inicjatywnymi benchmarkami może być to, że obie funkcje testują prese nce prefiksu przez przecięcie operandu string
i []byte
. A ponieważ string
s są porównywalne, podczas gdy bajtowe plasterki nie są, BenchmarkBytHasPrefix()
wymaga dodatkowego wywołania funkcji do bytes.Equal()
w porównaniu do BenchmarkStrHasPrefix()
(a dodatkowe wywołanie funkcji obejmuje również wykonywanie kopii jej parametrów: 2 nagłówki wycinków).
Inne rzeczy, które mogą nieznacznie przyczynić się do oryginalnych różnych wyników: argumenty użyte w BenchmarkStrHasPrefix()
są stałymi, podczas gdy parametry używane w BenchmarkBytHasPrefix()
są zmiennymi.
Nie powinieneś się martwić o różnicę w wydajności, obie funkcje kończą się w zaledwie kilka nanosekund.
Zauważ, że „realizacja” z bytes.Equal()
:
func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
ten jest prawdopodobnie inlined w niektórych platformach, nie powodując dodatkowych kosztów połączeń.
Jakie są wartości 'STR',' STR_B', 'PREFIX',' PREFIX_B'? –
@IsmailBadawi zaktualizowany przykład) – gobwas
Zwróć uwagę, że na moim Core2Duo P8600 oba testy działają tak samo (~ 20 ns/op), podczas gdy na i5-2400S są podobne do twoich - 5,5 vs 8.5 ns/op – tomasz