2016-07-24 31 views
4
var testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
//var testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" 
func BenchmarkHashing900000000(b *testing.B){ 
    var bufByte = bytes.Buffer{} 
    for i := 0; i < b.N ; i++{ 
     bufByte.WriteString(testString) 
     Sum32(bufByte.Bytes()) 
     bufByte.Reset() 
    } 
} 

func BenchmarkHashingWithNew900000000(b *testing.B){ 
    for i := 0; i < b.N ; i++{ 
     bytStr := []byte(testString) 
     Sum32(bytStr) 
    } 
} 

Testergebnis:Wenn golang tut Zuweisung für String-Byte Umwandlung

With testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
BenchmarkHashing900000000-4   50000000   35.2 ns/op   0 B/op   0 allocs/op 
BenchmarkHashingWithNew900000000-4 50000000   30.9 ns/op   0 B/op   0 allocs/op 

With testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" 
BenchmarkHashing900000000-4   30000000   46.6 ns/op   0 B/op   0 allocs/op 
BenchmarkHashingWithNew900000000-4 20000000   73.0 ns/op  64 B/op   1 allocs/op 

Warum es Zuteilung bei BenchmarkHashingWithNew900000000 ist, wenn String lang, aber keine Zuordnung, wenn Zeichenfolge klein ist.
Sum32: https://gowalker.org/github.com/spaolacci/murmur3
I go1.6

Antwort

-1

bin mit Wenn Sie etwas in eine byte.Buffer schreiben, ordnet es Speicher nach Bedarf. Wenn Sie byte.Buffer.Reset aufrufen, wird dieser Speicher nicht freigegeben aber stattdessen für spätere Wiederverwendung gespeichert. Und dein Code tut genau das. Er markiert den Puffer als leer und füllt ihn dann wieder.

Eigentlich ist einige Zuteilung gehen, aber wenn 50000000 iterieren, ist es vernachlässigbar. Aber wenn Sie die Deklaration für bufByte in die for Schleife verschieben, werden Sie einige Zuordnungen erhalten.

+0

Das ist, was ich erwarten würde. Abgesehen von Benchmark-Shows für kleinere Strings gibt es in beiden Fällen keine Zuweisung. Aber es verhält sich wie erwartet für größere Saiten. –

+0

Sie sollten sich den kompilierten Code ansehen. 'go test -c' und dann' go tool objdump -s 'BenchmarkHashing' main.test.exe'. –

+0

@VishalKumar Für die 'bytes.Buffer'-Version sollte eigentlich keine Zuweisung in _either_ case angezeigt werden. Bytes.Buffer verfügt in seiner Struktur über ein eingebautes 64-Byte-Array zur Verarbeitung kleiner Datensätze ohne (zusätzliche) Zuweisung, und keiner dieser Tests überschreitet 64 Byte pro Schreiboperation. Wenn Sie 'bufByte.Reset()' außerhalb der for-Schleife bewegen, habe ich das Gefühl, dass Sie gerechtere Ergebnisse sehen. – Kaedys

0

Ihre Benchmarks beobachten eine merkwürdige Optimierung durch den Golang-Compiler (Version 1.8).

Sie können die PR von Dmitry Dvyukov sehen hier

https://go-review.googlesource.com/c/go/+/3120

Leider ist das von vor langer Zeit, wenn der Compiler in C geschrieben wurde, bin ich nicht sicher, wo die Optimierung finden in der aktueller Compiler. Aber ich kann bestätigen, dass es noch existiert, und Dmitrys PR-Beschreibung ist korrekt.

Wenn Sie eine klarere in sich geschlossene Reihe von Benchmarks benötigen, um dies zu demonstrieren, habe ich hier einen Sinn.

https://gist.github.com/fmstephe/f0eb393c4ec41940741376ab08cbdf7e

Wenn wir nur an der zweiten Benchmark BenchmarkHashingWithNew900000000 aussehen können wir einen hellen Fleck sehen, wo es ‚sollte‘ zuordnen.

bytStr := []byte(testString) 

Diese Zeile muss den Inhalt des testString in eine neue []byte kopieren. In diesem Fall kann der Compiler jedoch sehen, dass bytStr nie wieder verwendet wird, nachdem Sum32 zurückgibt. Daher kann es auf dem Stapel zugeordnet werden. Da Strings jedoch beliebig groß sein können, wird ein Limit auf 32 Bytes für einen Stack festgelegt, der string oder []byte zugeordnet ist.

Es lohnt sich, diesen kleinen Trick zu beachten, weil es leicht sein kann, sich selbst zu glauben, dass ein Code nicht alloziert, wenn Ihre Benchmark-Strings alle kurz sind.