append()
‚s durchschnittlich (abgeschrieben) Kosten ist bereits O (1), weil es wächst das Array von einem jedes Mal Prozentsatz. Wenn das Array größer wird, wird es teurer, aber proportional seltener. Ein 10M-Stück Slice ist 10x teurer zu wachsen als ein 1M-Stück Slice, aber da die zusätzliche Kapazität, die wir zuweisen proportional zur Größe ist, wird es auch 10x so viele append(slice, item)
Aufrufe bis zum nächsten Mal wächst . Die steigenden Kosten und die abnehmende Häufigkeit von Neuzuordnungen heben sich auf, wodurch die Durchschnittskosten konstant bleiben, d. H. O (1).
Die gleiche Idee gilt auch für dynamisch skalierte Arrays anderer Sprachen: Microsofts std::vector
Implementierung vergrößert das Array zum Beispiel jedes Mal um 50%. Amortisiertes O (1) bedeutet nicht, dass Sie nichts für Allokationen zahlen, sondern nur, dass Sie weiterhin mit der gleichen durchschnittlichen Rate zahlen, wie Ihr Array größer wird.
Auf meinem Laptop konnte ich eine Million slice = append(slice, someStaticString)
s in 77ms laufen. Ein Grund, warum es schnell ist, was Siritinga unten bemerkt, ist, dass das "Kopieren" der Zeichenfolge, um das Array zu vergrößern, nur den String-Header (ein Pointer/Längen-Paar) kopiert und den Inhalt nicht kopiert. 100.000 Zeichenfolgen-Header sind immer noch weniger als 2 MB zum Kopieren, keine große Sache im Vergleich zu den anderen Datenmengen, mit denen Sie arbeiten.
container/list
stellte sich heraus ~ 3x langsamer für mich in einem microbenchmark; linked-list append ist natürlich auch konstante Zeit, aber ich stelle mir vor append
hat eine niedrigere Konstante, weil es in der Regel nur ein paar Worte des Gedächtnisses schreiben und nicht ein Listenelement usw. zuweisen kann. Der Timing-Code wird nicht funktionieren in der Spielplatz, aber Sie können diese lokal kopieren und führen Sie es selbst sehen: http://play.golang.org/p/uYyMScmOjX
aber Sie fragen eine spezifischere Frage hier über eine grep
-ähnlichen Anwendung (und vielen dank für eine detaillierte Frage mit Kontext zu fragen). Aus diesem Grund ist die beste Empfehlung, wenn Sie Gigs von Logs suchen, ist es wahrscheinlich am besten zu vermeiden, die gesamte Ausgabe im RAM zu puffern.
Sie könnten etwas schreiben, um die Ergebnisse als eine einzige Funktion zu streamen: logparser.Grep(in io.Reader, out io.Writer, patterns []regexp.Regexp)
; Sie könnten alternativ out
eine chan []byte
oder func(match []byte) (err error)
machen, wenn Sie nicht möchten, dass der Code, der Ergebnisse sendet, zu eng mit dem Grep-Code verknüpft ist.
(On []byte
vs. string
: a []byte
scheint die Arbeit hier zu tun und vermeidet []byte
< =>string
Conversions, wenn Sie mir/O, also würde ich, dass ich lieber nicht wissen, was alles, was Ihnen‘. re tun, aber, und wenn Sie string
brauchen es ist in Ordnung.)
wenn Sie haltet die ganze Spiel Liste im RAM tun , beachten Sie, dass auf einen Teil einer großen String oder Byte-Scheibe um eine Referenz hält die ganze hält Source String/Slice von Garbage Collection gesammelt. Also, wenn Sie diese Route gehen, dann können Sie tatsächlich Counterintuitiv wollen, um Übereinstimmungen zu kopieren, um zu vermeiden, alle Quellprotokolldaten im RAM zu behalten.
Angesichts der Datenmengen tb behandelt Ich habe die Anzahl der Zeilen zu begrenzen, in einem Durchlauf scannen (Gründe sollte klar sein: durchschnittlich I/O-Last, CPU-Last und resident Satzgröße, dh zu verhindern große Last peaks), also muss ich nicht wirklich streamen. Aber was ist hier wichtiger für mich ist, dass ich nicht wirklich verstehe, was Sie meinen, dass, wenn ich 1M Zeilen überlaufen die nächste Operation wird nicht 1,25M Zeilenzuweisung + kopieren, dass 1M Zeilen? Siehe nächsten Kommentar für die Berechnung. – LetMeSOThat4U
In Python: a = 1; cumul = 0. Dann 'für i im Bereich (10): print 'rows% .2f cumul% .2f'% (a, cumul); cumul + = a; a = a * 1,25'. Ergebnis: Zeilen 1,00 Cumul 0,00 Zeilen 1,25 Cumul 1,00 Zeilen 1,56 Cumul 2,25 Zeilen 1,95 Cumul 3,81 Zeilen 2,44 Cumul 5,77 Zeilen 3,05 Cumul 8,21 Zeilen 3,81 Cumul 11,26 Zeilen 4,77 Cumul 15,07 Zeilen 5,96 Cumul 19,84 Zeilen 7,45 Cumul 25,80.Also, wenn ich mit 1M Zeilen Slice anfange und 8 Millionen Übereinstimmungen habe, müsste ich Slice 10 Mal kopieren und insgesamt 25M Zeilen kopieren. Das ist nicht eine sehr gute Kosten im Vergleich zu Linked List sagen. – LetMeSOThat4U
Ich denke, dass eine Scheibe von Strings ist intern eine Scheibe von Zeigern auf Strings, in dem Sinne, dass die Verlängerung der Scheibe wird nicht wirklich die Zeichenfolgen, nur die Zeiger auf die Strings, also wird jeder Eintrag 4 oder 8 Bytes (abhängig von der Systemarchitektur) sein, also sollte es schnell sein. – siritinga