append()
jest średnia (amortyzowane) Koszt już O (1), ponieważ rośnie tablicę o procent za każdym razem. Gdy tablica staje się większa, staje się coraz droższa, ale proporcjonalnie rzadsza. Fragment o wielkości 10M będzie 10-krotnie droższy w rozwoju niż plasterek o wielkości 1M, ale ponieważ dodatkowa pojemność, którą przydzielamy, jest proporcjonalna do rozmiaru, będzie to również 10-krotna liczba wywołań append(slice, item)
, aż do następnego wzrostu . Rosnący koszt i malejąca częstość ponownych przydziałów znoszą się, pozostawiając średni koszt stały, tj. O (1).
Ta sama zasada dotyczy również tablic dynamicznych o innych językach: implementacja Microsoft std::vector
najwyraźniej powiększa tablicę o 50% za każdym razem, na przykład. Amortyzacja O (1) nie oznacza, że nie płacisz nic za alokacje, tylko że nadal płacisz z tą samą średnią stawką co twoja tablica zwiększa się.
Na moim laptopie mogłem uruchomić milion slice = append(slice, someStaticString)
s w 77ms. Jednym z powodów, dla którego jest to szybkie, które siritinga odnotowano poniżej, jest to, że "kopiowanie" ciągu w celu powiększenia tablicy to po prostu kopiowanie nagłówka (pary wskaźnika/długości), a nie kopiowanie zawartości. 100 000 nagłówków łańcuchów wciąż nie może zawierać więcej niż 2 MB danych, co nie jest wielkim problemem w porównaniu z innymi ilościami danych, z którymi pracujesz.
container/list
okazało się ~ 3x wolniej dla mnie w microbenchmark; Dołączona lista-dołączeń to oczywiście również czas stały, ale wyobrażam sobie, że append
ma niższą stałą, ponieważ zazwyczaj wystarczy napisać kilka słów pamięci i nie można przydzielić elementu listy itp. Kod czasu nie zadziała w plac zabaw dla dzieci, ale można skopiować ten lokalnie i uruchomić go zobaczyć siebie: http://play.golang.org/p/uYyMScmOjX
ale pytasz bardziej konkretne pytanie tutaj o aplikacji grep
-jak (i dzięki za zadając szczegółowe pytania z kontekstu). W tym celu zaleca się, że jeśli szukasz przebojów dzienników, najlepiej jest unikać buforowania całego wyjścia w pamięci RAM.
Możesz napisać coś, aby przesyłać strumieniowo wyniki w postaci pojedynczej funkcji: logparser.Grep(in io.Reader, out io.Writer, patterns []regexp.Regexp)
; możesz też alternatywnie zrobić out
a chan []byte
lub func(match []byte) (err error)
, jeśli nie chcesz, aby kod, który wysyła wyniki, był zbyt mocno zazębiony z kodem grep.
(Na []byte
vs. string
: a []byte
wydaje się do pracy tutaj i unika []byte
< =>string
konwersje kiedy to zrobić I/O, więc wolałbym, że nie wiem, co wszyscy ci”. re robi, choć i jeśli trzeba string
jest w porządku).
Jeśli zrobić zachować całą listę mecz w pamięci RAM, należy pamiętać, że utrzymywanie wokół odniesieniu do części wielkiego łańcucha lub bajtów plaster utrzymuje całość ciąg źródłowy/kawałek z bycia zebranym. Więc jeśli idziesz tą trasą, to wbrew intuicji możesz chcieć kopiować mecze, aby uniknąć przechowywania wszystkich danych logu źródłowego w pamięci RAM.
Podane objętości TB danych obsługiwane muszę ograniczyć liczbę wierszy do skanowania w jednym przebiegu (powody powinny być oczywiste: średnio z I/obciążenie O, obciążenie procesora i zestaw rezydent wielkość, czyli zapobieganie duże obciążenia szczytów), więc nie muszę naprawdę streamować. Ale co jest dla mnie ważniejsze, to że tak naprawdę nie rozumiem, co masz na myśli, że jeśli przepełnię 1M wierszy, następne op nie będzie mieć 1.25M alokacji wierszy + kopiowanie tych 1M wierszy? Zobacz następny komentarz do obliczeń. – LetMeSOThat4U
W języku Python: a = 1; cumul = 0. Następnie 'for i in range (10): print 'rows% .2f cumul% .2f'% (a, cumul) ,; cumul + = a; a = a * 1,25'. Wynik: wiersze 1,00 kumulacji 0,00 wierszy 1,25 kumulacji 1,00 wierszy 1,56 kumulacji 2,25 wierszy 1,95 kumulacji 3,81 wierszy 2,44 kumulacji 5,77 wierszy 3,05 kumulacji 8,21 rzędów 3,81 kumulacji 11,26 rzędów 4,77 kumulacji 15,07 wierszy 5,96 kumulacji 19,84 wierszy 7,45 kumulacji 25,80.Więc jeśli zaczynam od 1M wycinka wierszy i mam 8 milionów dopasowań, które wymagają 10-krotnego kopiowania przekroju i łącznie 25 milionów wierszy skopiowanych. To nie jest zbyt dobry koszt w porównaniu do listy połączonej. – LetMeSOThat4U
Myślę, że kawałek łańcuchów jest wewnętrznie wycinkiem wskaźników do łańcuchów, w tym sensie, że rozszerzenie fragmentu nie będzie faktycznie kopiować łańcuchów, tylko wskaźniki do ciągi, więc każdy wpis będzie miał 4 lub 8 bajtów (w zależności od architektury systemu), więc powinien być szybki. – siritinga