2016-02-17 17 views
8

Mam do czynienia z błędem pamięci w moim kodzie. Mój parser można podsumować tak:Wolna pamięć podczas pętli

# coding=utf-8 
#! /usr/bin/env python 
import sys 
import json 
from collections import defaultdict 


class MyParserIter(object): 

    def _parse_line(self, line): 
     for couple in line.split(","): 
      key, value = couple.split(':')[0], couple.split(':')[1] 
      self.__hash[key].append(value) 

    def __init__(self, line): 
     # not the real parsing just a example to parse each 
     # line to a dict-like obj 
     self.__hash = defaultdict(list) 
     self._parse_line(line) 

    def __iter__(self): 
     return iter(self.__hash.values()) 

    def to_dict(self): 
     return self.__hash 

    def __getitem__(self, item): 
     return self.__hash[item] 

    def free(self, item): 
     self.__hash[item] = None 

    def free_all(self): 
     for k in self.__hash: 
      self.free(k) 

    def to_json(self): 
     return json.dumps(self.to_dict()) 


def parse_file(file_path): 
    list_result = [] 
    with open(file_path) as fin: 
     for line in fin: 
      parsed_line_obj = MyParserIter(line) 
      list_result.append(parsed_line_obj) 
    return list_result 


def write_to_file(list_obj): 
    with open("out.out", "w") as fout: 
     for obj in list_obj: 
      json_out = obj.to_json() 
      fout.write(json_out + "\n") 
      obj.free_all() 
      obj = None 

if __name__ == '__main__': 
     result_list = parse_file('test.in') 
     print(sys.getsizeof(result_list)) 
     write_to_file(result_list) 
     print(sys.getsizeof(result_list)) 
     # the same result for memory usage result_list 
     print(sys.getsizeof([None] * len(result_list))) 
     # the result is not the same :(

Celem jest, aby przeanalizować pliku (duży) każda linia przekształcony w obiekt json, które będą zapisywane do pliku.

Moim celem jest zmniejszenie śladu, ponieważ w niektórych przypadkach ten kod powoduje błąd pamięci. Po każdym fout.write chciałbym odniesienia (usunąć wolną pamięć) obj.

Próbowałem ustawić obj na Brak wywołania metody obj.free_all(), ale żadna z nich nie zwalnia pamięci. Użyłem także simplejson zamiast json, które zmniejszyły ślad, ale nadal są zbyt duże w niektórych przypadkach.

test.in patrzy jak:

test1:OK,test3:OK,... 
test1:OK,test3:OK,... 
test1:OK,test3:OK,test4:test_again... 
.... 
+0

Czy próbowałeś już gc.collect()? Zobacz: http://stackoverflow.com/questions/1316767/how-can-i-explicitly-free-memory-in-python – JonnyTieM

+0

jak duży jest twój test.in? – YOU

+0

Dla prawdziwego analizatora składni plik wejściowy wynosi około 300 MB. –

Odpowiedz

1

Aby obj być wolnym stanie, wszystkie odniesienia do niej muszą zostać wyeliminowane. Twoja pętla tego nie zrobiła, ponieważ odwołanie w postaci list_obj nadal istniało. Dodaje się ustali, że:

def write_to_file(list_obj): 
    with open("out.out", "w") as fout: 
     for ix in range(list_obj): 
      obj = list_obj[ix] 
      list_obj[ix] = None 
      json_out = obj.to_json() 
      fout.write(json_out + "\n") 
      obj.free_all() 

Alternatywnie, można destrukcyjnie pop element z przodu list_obj, mimo że potencjalnie może powodować problemy z wydajnością, jeśli ma do realokacji list_obj zbyt wiele razy. Nie eksperymentowałem z tym, więc nie jestem pewien. Ta wersja wygląda następująco:

def write_to_file(list_obj): 
    with open("out.out", "w") as fout: 
     while len(list_obj) > 0: 
      obj = list_obj.pop(0) 
      json_out = obj.to_json() 
      fout.write(json_out + "\n") 
      obj.free_all() 
+0

Dzięki. Testy z tym kodem nie zwalniają pamięci według sys.getsizeof. –

+0

Czy próbowałeś wywołać 'gc.collect()' po usunięciu odniesień? –

+0

Tak, zrobiłem i czas procesu się zawalił –

4

Nie przechowywać wiele instancji klasy w tablicy, zamiast zrobić inline. Przykład.

% cat test.in 
test1:OK,test3:OK 
test1:OK,test3:OK 
test1:OK,test3:OK,test4:test_again 

% cat test.py 
import json 

with open("test.in", "rb") as src: 
    with open("out.out", "wb") as dst: 
     for line in src: 
      pairs, obj = [x.split(":",1) for x in line.rstrip().split(",")], {} 
      for k,v in pairs: 
       if k not in obj: obj[k] = [] 
       obj[k].append(v) 
      dst.write(json.dumps(obj)+"\n") 

% cat out.out 
{"test1": ["OK"], "test3": ["OK"]} 
{"test1": ["OK"], "test3": ["OK"]} 
{"test1": ["OK"], "test3": ["OK"], "test4": ["test_again"]} 

Jeśli jest powolny, nie pisz do pliku linia po linii, ale sklep dumpingowych json ciąg w tablicy i zrobić dst.write("\n".join(array))

+0

Dzięki, ale będzie to oznaczać, aby zmienić moją klasę i wymieszać logikę (wyjście do pliku i przeanalizować dane wejściowe). Analizator składni powinien mieć możliwość wyprowadzenia, albo do pliku, albo konsoli, tego, dlaczego muszę przechowywać przeanalizowany wynik. aby móc to zrobić lub zdobyć klucz. Nareszcie, jeśli chodzi o niepowodzenie, to podejście nie pasuje do mnie. –

+0

@AliSAIDOMAR To proste. Po prostu napisz generator, który "wydaj" wartość (w zasadzie umieść kod z tej odpowiedzi na 'def' i zamień' dst.write' na 'yield'). Następnie możesz po prostu powtórzyć wynik i napisać, co chcesz. – Bakuriu

+0

Najważniejsze jest tutaj, aby nie przechowywać całego wyniku (w dowolnej formie), ale czytać/parsować/pisać wiersz po linii. Sposób działania oryginalnego programu, zużycie pamięci to O (długość_pliku), natomiast w podejściu YOU (odczyt/zapis/zapisanie linia po linii) zużycie pamięci wynosi O (długość maksymalna_linii). – hvb

Powiązane problemy