2015-12-01 10 views
7

W moim kodu, mam takiego odniesienia:Dlaczego string.HasPrefix jest szybszy niż bytes.HasPrefix?

const STR = "abcd" 
const PREFIX = "ab" 
var STR_B = []byte(STR) 
var PREFIX_B = []byte(PREFIX) 

func BenchmarkStrHasPrefix(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     strings.HasPrefix(STR, PREFIX) 
    } 
} 

func BenchmarkBytHasPrefix(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     bytes.HasPrefix(STR_B, PREFIX_B) 
    } 
} 

I jestem trochę zagubiony wyników:

BenchmarkStrHasPrefix-4 300000000 4.67 ns/op 
BenchmarkBytHasPrefix-4 200000000 8.05 ns/op 

Dlaczego jest do 2x różnicę?

Dzięki.

+1

Jakie są wartości 'STR',' STR_B', 'PREFIX',' PREFIX_B'? –

+0

@IsmailBadawi zaktualizowany przykład) – gobwas

+0

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

Odpowiedz

13

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ń.

+0

To niewystarczające wyjaśnienie: Jeśli wstawisz dodatkowe nieliniowe wywołanie funkcji, będzie ono wolniejsze. Niestety nie rozumiem, co faktycznie robi funkcja Equal w zestawie. Czy ktoś może wyjaśnić? – 0x434D53

+1

@ 0x434D53 +1, na przykład w asm_amd64.s (Linia 1312 (środowisko wykonawcze eqstring) i linia 1652 (bajty · Równa)) - jaka jest różnica w tym kodzie? – gobwas

+0

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ =) – gobwas

Powiązane problemy