Tema
/
Algoritmai ir duomenų struktūros GO
Pamoka 4 / 15 Stekai ir eilės ~50 min
Teorija

Ko skaitiklis nemato

Ta pati komanda spausdina ir antrą dalį:

keeping only the LAST item of a 100000-item queue:
q = q[1:] (header points into array)       7034 KB still on the heap
copied the survivor out                       0 KB still on the heap

Iš 100 000 įrašų eilės liko vienas. Su q = q[1:] atmintyje tebėra 7 megabaitai.

Kodėl

q[1:] nesukuria naujo masyvo. Jis grąžina antraštę, kuri rodo į tą patį masyvą, tik viena vieta toliau. Kai išimi 99 999 įrašus, lieka vieno elemento slice — bet jo rodyklė vis dar nurodo į vidurį šimtatūkstantinio masyvo.

Šiukšlių surinkėjas negali laisvinti masyvo dalimis. Masyvas arba pasiekiamas, arba ne. Vienas gyvas elementas išlaiko visą masyvą.

Todėl copied the survivor out rodo nulį: nukopijavus tą vieną įrašą į naują slice, senas masyvas tampa nepasiekiamas ir dingsta.

Ką iš tikrųjų reiškia šis rezultatas

Pirmoje pamokoje pasakėm: skaičiuok veiksmus, o ne sekundes, nes skaičius tikslus, determinuotas ir nepriklauso nuo technikos. Visa tai tiesa.

Bet skaitiklis matuoja laiką, ne atmintį, ir čia jis parodė nulį ties variantu, kuris nutekina septynis megabaitus.

Veiksmų skaičius — teisingas įrankis laikui. Atminčiai jis aklas.

Tai ne skaitiklio klaida. Tai jo ribos, ir dabar tu jas žinai. Nuo šiol, lygindamas struktūras, klausk abiejų klausimų.

Kodėl žiedinis buferis

Ring juda nulį elementų ir nieko neišlaiko: r.buf[r.head] = Item{} išleidžia įrašą iš masyvo iškart, kai jis atiduotas. Vienas masyvas, fiksuoto dydžio, niekada nejuda ir niekada neauga.

Kaina — reikia iš anksto žinoti dydį. Tai mainai, o ne pergalė.

Spąstai

Šitas nutekėjimas ateina ne tik iš eilių. Bet kada, kai iš didelio slice pasilieki mažą gabalėlį — first := data[:10], tail := log[len(log)-5:] — laikai visą masyvą. Jei gabalėlis gyvena ilgai, kopijuok jį: out := append([]Item(nil), data[:10]...).