2016-02-17 9 views
8

Ich bin mit einem Speicherfehler in meinem Code konfrontiert. Mein Parser kann so zusammenfassen:Freier Speicher während Schleife

# 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 :(

Ziel (große) Datei zu analysieren ist, jede Zeile in ein JSON-Objekt umgewandelt, das wieder in eine Datei geschrieben werden.

Mein Ziel ist es, den Footprint zu reduzieren, da dieser Code in einigen Fällen einen Speicherfehler auslöst. Nach jeder fout.write möchte ich löschen (freier Speicher) obj Referenz.

Ich habe versucht, obj auf Keine der Methode obj.free_all() aufrufen, aber keiner von ihnen den Speicher freigeben. Ich habe auch simplejson anstelle von json verwendet, die den Footprint reduziert haben, aber in einigen Fällen immer noch zu groß.

test.in sucht wie:

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

Haben Sie gc.collect() bereits versucht? Siehe: http://stackoverflow.com/questions/1316767/how-can-i-explicitly-free-memory-in-python – JonnyTieM

+0

Wie groß ist Ihr test.in? – YOU

+0

Für den echten Parser ist die Eingabedatei etwa 300 MB. –

Antwort

1

Damit obj Frei der Lage sein, alle Verweise auf sie müssen beseitigt werden. Ihre Schleife hat das nicht getan, weil die Referenz in list_obj noch existierte. Im Folgenden wird beheben, dass:

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() 

Alternativ Sie destruktiv das Element von der Vorderseite der list_obj Pop könnten, obwohl das möglicherweise Leistungsprobleme verursachen könnte, wenn es list_obj zu oft neu zu verteilen hat. Ich habe nicht damit experimentiert, also bin ich mir nicht wirklich sicher. Diese Version sieht so aus:

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

Danke. Die Tests mit diesem Code geben keinen Speicher gemäß sys.getsizeof frei. –

+0

Haben Sie nach dem Entfernen der Referenzen versucht, 'gc.collect()' aufzurufen? –

+0

Ja, ich tat und die Zeit des Prozesses ist zusammengebrochen –

4

nicht viele Instanz der Klasse in Array speichern Sie es inline stattdessen tun. Beispiel.

% 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"]} 

Wenn es langsam ist, schreiben Sie nicht Zeile in Datei Zeile, aber Laden abgeladen JSON-String in Array und tun dst.write("\n".join(array))

+0

Danke aber, es wird bedeuten, meine Klasse zu refaktorieren und die Logik zu mischen (Ausgabe in Datei und Parsing der Eingabe). Der Parser sollte in der Lage sein, entweder als Datei oder als Konsole auszugeben, warum ich das analysierte Ergebnis speichern muss. in der Lage zu sein oder einen Schlüssel zu bekommen. Endlich für die Unittests passt dieser Ansatz nicht zu mir. –

+0

@AliSAIDOMAR Das ist einfach. Schreiben Sie einfach einen Generator, der den Wert liefert (setzen Sie den Code aus dieser Antwort in ein "def" und ersetzen Sie "dst.write" durch "yield"). Dann können Sie einfach auf das Ergebnis iterieren und schreiben, was Sie wollen. – Bakuriu

+0

Der Hauptpunkt hier ist, nicht das gesamte Ergebnis (in welcher Form) zu speichern, sondern Zeile für Zeile zu lesen/zu analysieren/zu schreiben. Wie Ihr ursprüngliches Programm funktioniert, ist der Speicherverbrauch O (file_length), während bei YOUs Ansatz (zeilenweise lesen/parsen/schreiben) der Speicherverbrauch O (max_line_length) ist. – hvb

Verwandte Themen