Znaczne oszczędności można uzyskać, unikając pożerania całego pliku wejściowego w pamięci jako list
linii.
W szczególności te wiersze są straszne pod względem wykorzystania pamięci, ponieważ obejmują szczytowe użycie pamięci bytes
określ rozmiar całego pliku plus list
wierszy z pełną zawartością pliku:
file_content = obj['Body'].read().decode('utf-8').splitlines(True)
for line in file_content:
Dla pliku tekstowego ASCII o pojemności 1 GB z 5 milionami wierszy, w 64-bitowym Pythonie 3.3+, szczytowe zapotrzebowanie na pamięć wynosi około 2,3 GB dla tylko bytes
obiekt, list
i indywidualny str
s na list
. Program, który potrzebuje 2,3 razy więcej pamięci RAM niż rozmiar przetwarzanych plików, nie będzie skalowany do dużych plików.
Aby naprawić, zmień oryginalny kod na:
file_content = io.TextIOWrapper(obj['Body'], encoding='utf-8')
for line in file_content:
Biorąc pod uwagę, że obj['Body']
wydaje się, że nadaje się do leniwego przesyłania strumieniowego
to powinno usunąć oba kopie pełnych danych pliku z pamięci. Korzystanie z TextIOWrapper
oznacza obj['Body']
jest leniwie odczytywany i dekodowany w kawałkach (po kilka KB na raz), a wiersze są również leniwie iterowane; zmniejsza to zapotrzebowanie na pamięć do małej, w dużej mierze stałej wartości (szczytowy koszt pamięci zależy od długości najdłuższej linii), niezależnie od rozmiaru pliku.
Aktualizacja:
Wygląda jak StreamingBody
nie implementuje io.BufferedIOBase
ABC. Ma własny udokumentowany interfejs API
jednak można to wykorzystać w podobnym celu. Jeśli nie możesz utworzyć TextIOWrapper
wykonać pracę za Ciebie (jest to o wiele bardziej wydajne i prostsze, jeśli można sprawić, aby działał), alternatywą byłoby:
file_content = (line.decode('utf-8') for line in obj['Body'].iter_lines())
for line in file_content:
W przeciwieństwie do używania TextIOWrapper
, nie korzysta z masowego dekodowania bloków (każda linia jest dekodowana indywidualnie), ale poza tym powinna nadal osiągać te same korzyści pod względem zmniejszonego zużycia pamięci.