2012-08-28 8 views
15

यह अंततः मेरी सभी उपलब्ध स्मृति का उपभोग करता है और फिर प्रक्रिया मारे जाती है। मैंने schedule से टैग को 'छोटे' टैग में बदलने की कोशिश की है, लेकिन इससे कोई फर्क नहीं पड़ता।lxml.etree.iterparse() मेरी सारी मेमोरी क्यों खा रहा है?

मैं क्या गलत कर रहा/मैं iterparse() के साथ इस बड़ी फ़ाइल को कैसे संसाधित कर सकता हूं?

import lxml.etree 

for schedule in lxml.etree.iterparse('really-big-file.xml', tag='schedule'): 
    print "why does this consume all my memory?" 

मैं इसे आसानी से काट सकता हूं और इसे छोटे हिस्सों में संसाधित कर सकता हूं लेकिन यह मुझे पसंद है।

उत्तर

18

iterparse पूरे फ़ाइल पर एक पेड़ बनाया गया है और कोई तत्व मुक्त नहीं किया गया है। ऐसा करने का लाभ यह है कि तत्व याद करते हैं कि उनके माता-पिता कौन हैं, और आप XPaths बना सकते हैं जो पूर्वजों के तत्वों को संदर्भित करते हैं। नुकसान यह है कि यह बहुत सारी स्मृति का उपभोग कर सकता है।

कुछ स्मृति मुक्त करने के लिए के रूप में आप को पार्स, का उपयोग लिज़ा डैली के fast_iter:

def fast_iter(context, func, *args, **kwargs): 
    """ 
    http://lxml.de/parsing.html#modifying-the-tree 
    Based on Liza Daly's fast_iter 
    http://www.ibm.com/developerworks/xml/library/x-hiperfparse/ 
    See also http://effbot.org/zone/element-iterparse.htm 
    """ 
    for event, elem in context: 
     func(elem, *args, **kwargs) 
     # It's safe to call clear() here because no descendants will be 
     # accessed 
     elem.clear() 
     # Also eliminate now-empty references from the root node to elem 
     for ancestor in elem.xpath('ancestor-or-self::*'): 
      while ancestor.getprevious() is not None: 
       del ancestor.getparent()[0] 
    del context 

है जिसे आप इस तरह इस्तेमाल कर सकते हैं:

def process_element(elem): 
    print "why does this consume all my memory?" 
context = lxml.etree.iterparse('really-big-file.xml', tag='schedule', events = ('end',)) 
fast_iter(context, process_element) 

मैं अत्यधिक the article जो ऊपर fast_iter पर की सिफारिश आधारित है; यदि आप बड़ी एक्सएमएल फाइलों से निपट रहे हैं तो यह आपके लिए विशेष रूप से दिलचस्प होना चाहिए।

उपरोक्त प्रस्तुत fast_iter लेख में दिखाए गए एक का थोड़ा संशोधित संस्करण है। यह पिछले पूर्वजों को हटाने के बारे में अधिक आक्रामक है, इस प्रकार अधिक स्मृति बचाता है। Here you'll find a script जो अंतर प्रदर्शित करता है।

+0

धन्यवाद! आपके समाधान और जो मैंने अभी जोड़ा है, वह चाल चल रहा है, मुझे उत्सुकता है कि आप और अन्य लोगों को लगता है कि एक बेहतर समाधान है। क्या आपके पास कोई विचार है? –

+3

आपके समाधान कार्यों को चालू करता है और http://effbot.org/zone/element-iterparse.htm समाधान नहीं हुआ (यह अभी भी मेरी सभी मेमोरी खा चुका है) –

+0

धन्यवाद! यह वह संस्करण है जो वास्तव में काम करता है। लिज़ा डेली, इफबॉट, और एलएक्सएमएल आधिकारिक दस्तावेज़ों के संस्करणों ने मेरे लिए बहुत मेमोरी नहीं बचाई। – fjsj

3

सीधे से http://effbot.org/zone/element-iterparse.htm

ध्यान दें कि iterparse अभी भी सिर्फ पार्स की तरह एक पेड़ बनाता है, की नकल की है, लेकिन आप सुरक्षित रूप से पुन: व्यवस्थित या पेड़ के कुछ हिस्सों को हटा सकते हैं पार्स करने के दौरान। उदाहरण के लिए, बड़ी फ़ाइलों को पार्स करने के लिए, आप जितनी जल्दी आप उन्हें संसाधित होते तत्वों से छुटकारा पाने के कर सकते हैं:

for event, elem in iterparse(source): 
    if elem.tag == "record": 
     ... process record elements ... 
     elem.clear() 

ऊपर पैटर्न एक दोष यह है; यह रूट तत्व को साफ़ नहीं करता है, इसलिए आप एक ही तत्व के साथ समाप्त हो जाएंगे जिसमें बहुत से खाली बच्चे तत्व होंगे। यदि आपकी फाइलें बड़ी हैं, बल्कि बड़ी हैं, तो यह एक समस्या हो सकती है। इसके आसपास काम करने के लिए, आपको रूट तत्व पर अपने हाथों की आवश्यकता है। यह करने के लिए सबसे आसान तरीका शुरू घटनाओं सक्षम करें, और एक चर में पहला तत्व के लिए एक संदर्भ को बचाने के लिए है:

# get an iterable 
context = iterparse(source, events=("start", "end")) 

# turn it into an iterator 
context = iter(context) 

# get the root element 
event, root = context.next() 

for event, elem in context: 
    if event == "end" and elem.tag == "record": 
     ... process record elements ... 
     root.clear() 
0

यह मेरे लिए वास्तव में अच्छी तरह से काम:

def destroy_tree(tree): 
    root = tree.getroot() 

    node_tracker = {root: [0, None]} 

    for node in root.iterdescendants(): 
     parent = node.getparent() 
     node_tracker[node] = [node_tracker[parent][0] + 1, parent] 

    node_tracker = sorted([(depth, parent, child) for child, (depth, parent) 
          in node_tracker.items()], key=lambda x: x[0], reverse=True) 

    for _, parent, child in node_tracker: 
     if parent is None: 
      break 
     parent.remove(child) 

    del tree 
संबंधित मुद्दे