Der Hauptgrund ist der Unterschied in den Anrufkosten von bytes.HasPrefix()
und strings.HasPrefix()
. Wie @tomasz in seinem Kommentar darauf hingewiesen hat, ist strings.HashPrefix()
standardmäßig inline, bytes.HasPrefix()
dagegen nicht.
Weitere Gründe sind die unterschiedlichen Parametertypen: bytes.HasPrefix()
benötigt 2 Slices (2 Slice Deskriptoren). dauert 2 Zeichenfolgen (2 Zeichenfolgenheader). Slice-Deskriptoren enthalten einen Zeiger und 2 int
s: Länge und Kapazität, siehe reflect.SliceHeader
. String-Header enthalten nur einen Zeiger und eine int
: die Länge, siehe reflect.StringHeader
.
Wir können dies beweisen, wenn wir manuell die HasPrefix()
Funktionen in den Benchmark-Funktionen inline, so dass wir die Anrufkosten (Null beide) beseitigen. Durch Inlinen wird kein Funktionsaufruf (an sie) gemacht.
HasPrefix()
Implementierungen:
// 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
}
Benchmark-Funktionen nach ihnen inlining:
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)
}
}
diese Laufen Sie ganz in der Nähe Ergebnisse erhalten:
BenchmarkStrHasPrefix-2 300000000 5.88 ns/op
BenchmarkBytHasPrefix-2 200000000 6.17 ns/op
Der Grund für den kleinen Unterschied in der Inline-Benchmarks können sein, dass beide Funktionen die Prese testen nce des Präfixes durch Schneiden des Operanden string
und []byte
. Und da string
s vergleichbar sind, während Byte-Slices nicht vergleichbar sind, erfordert BenchmarkBytHasPrefix()
einen zusätzlichen Funktionsaufruf an bytes.Equal()
im Vergleich zu BenchmarkStrHasPrefix()
(und der zusätzliche Funktionsaufruf umfasst auch das Erstellen von Kopien seiner Parameter: 2 Slice-Header).
Andere Dinge, die leicht zu den ursprünglichen unterschiedlichen Ergebnissen beitragen können: Die in BenchmarkStrHasPrefix()
verwendeten Argumente sind Konstanten, während die in BenchmarkBytHasPrefix()
verwendeten Parameter Variablen sind.
Sie sollten sich nicht viel Gedanken über den Leistungsunterschied machen, beide Funktionen sind in wenigen Nanosekunden erledigt.
Beachten Sie, dass die "Umsetzung" von bytes.Equal()
:
func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
Dies kann ohne zusätzliche Gesprächskosten führt in einigen Plattformen inlined werden.
Was sind die Werte von 'STR',' STR_B', 'PREFIX',' PREFIX_B'? –
@IsmailBadawi aktualisiert Beispiel) – gobwas
beachten Sie, dass auf meinem Core2Duo P8600 beide Benchmarks die gleiche Leistung (~ 20 ns/op), während auf i5-2400S sind sie ähnlich zu Ihnen - 5,5 vs 8.5 ns/op – tomasz