2009-02-04 3 views
4

У меня есть несколько больших (~ 100 Мб) файлов, которые я регулярно обрабатываю. Хотя я пытаюсь удалить ненужные структуры данных во время обработки, потребление памяти слишком велико. Мне было интересно, если есть способ, чтобы эффективно управлять большими объемами данных, например .:Использование памяти в больших манипуляциях/обработке данных

def read(self, filename): 
    fc = read_100_mb_file(filename) 
    self.process(fc) 
def process(self, content): 
    # do some processing of file content 

Есть ли дублирование структур данных? Разве не более эффективно использовать память для использования атрибута класса, такого как self.fc?

Когда следует использовать сборку мусора? Я знаю о модуле gc, но могу ли я назвать его после того, как я, например, del fc?

обновление
p.s. 100 Мб не проблема сама по себе. но конвертирование с плавающей точкой, дальнейшая обработка значительно увеличивает как рабочий набор, так и виртуальный размер (я нахожусь в Windows).

+0

Что в файле? Что такое обработка? –

+0

временные ряды, разделенные запятыми, которые я уменьшаю до некоторых понятных суммарных переменных. – SilentGhost

+0

Не могли бы вы быть более конкретными, может быть, представите небольшой пример? – oefe

ответ

7

Я предлагаю посмотреть на presentation by David Beazley на использование генераторов в Python. Этот метод позволяет обрабатывать множество данных и выполнять сложную обработку, быстро и без взрыва использования вашей памяти. ИМО, трюк не содержит огромное количество данных в памяти как можно более эффективно; трюк позволяет одновременно загружать огромное количество данных в память.

+1

Gah, как только я увидел вопрос, в который я вскочил, чтобы ответить со ссылкой на материал Beazley и увидел, что вы даете его как ответ уже. Ну что ж, вам нужно проголосовать за +1 вместо этого! Просто хочу, чтобы я мог дать ему больше, чем +1. –

3

Прежде, чем вы начнете разрывать волосы над сборщиком мусора, вы можете избежать этого 100-мегабайтного удара по загрузке всего файла в память с помощью файла с отображением памяти. См. Модуль mmap.

+0

100 Mb просто отлично , проблема начинается, когда она достигает 1,7 Гбайт виртуальной памяти. – SilentGhost

+0

Yikes! Это похоже на то, что вы навязываете ссылки на многие вещи, чтобы сборщик мусора не мог их очистить. Это может произойти, если вы сохраните ссылку на свои промежуточные данные в классе обработки. – Crashworks

+0

это точно мой вопрос! – SilentGhost

3

Не читайте весь 100-мегабайтный файл за раз. Используйте потоки для обработки немного за раз. Проверьте это сообщение в блоге, в котором говорится о работе с большими файлами csv и xml. http://lethain.com/entry/2009/jan/22/handling-very-large-csv-and-xml-files-in-python/

Вот пример кода из статьи.

from __future__ import with_statement # for python 2.5 

with open('data.in','r') as fin: 
    with open('data.out','w') as fout: 
     for line in fin: 
      fout.write(','.join(line.split(' '))) 
+0

он, похоже, не масштабируется с точки зрения кода, мне не нужно просто переупорядочивать биты, есть больше обработки. – SilentGhost

+0

После того, как вы проанализировали подробную строку и выполнили свои расчеты по сокращению, убедитесь, что вы не висели на каких-либо объектов, созданных при анализе деталей. Python GC основан на ссылках. Пока есть ссылка на объект, он не получит GC'ed. –

+0

Просто добавьте. Если у вас есть два объекта, которые относятся друг к другу, они никогда не будут собираться мусором, если только один из них не перейдет к ссылке на другую.Проверяйте эту циркулярную ссылку, если вы видите, что ваша память использует воздушный шар, и вещь, которую вы должны вынести из области. –

2

Таким образом, из ваших комментариев я предполагаю, что ваш файл выглядит примерно так:

item1,item2,item3,item4,item5,item6,item7,...,itemn 

который вы все сводятся к одному значению при повторном применении некоторой комбинации функции. В качестве решения, только для чтения одно значение в то время:

def read_values(f): 
    buf = [] 
    while True: 
     c = f.read(1) 
     if c == ",": 
      yield parse("".join(buf)) 
      buf = [] 
     elif c == "": 
      yield parse("".join(buf)) 
      return 
     else: 
      buf.append(c) 

with open("some_file", "r") as f: 
    agg = initial 
    for v in read_values(f): 
     agg = combine(agg, v) 

Таким образом, потребление памяти остается постоянным, если agg не растет во времени.

  1. Обеспечить соответствующие реализаций initial, parse и combine
  2. Не читать файл байт в байт, но читать в фиксированном буфере разбора из буфера и далее, как вам это нужно
  3. Это в основном то, что делает встроенная функция reduce, но я для ясности использовал явный цикл для этого цикла. Вот то же самое, используя reduce:

    with open("some_file", "r") as f: 
        agg = reduce(combine, read_values(f), initial) 
    

Я надеюсь, что я истолковал вашу проблему правильно.

+0

Мне жаль, если я поставил его неуклюжим, но по сокращению я имел в виду «сделать 32 Кб от 100 МБ» – SilentGhost

+0

Нет, я не имел в виду это, я имел в виду сокращение встроенного. –

+0

Я добавил пример «уменьшить». – jfs

0

Прежде всего, не прикасайтесь к сборщику мусора. Это не проблема, не решение.

Похоже, что реальная проблема, с которой вы сталкиваетесь, связана не с чтением файла, а с структурами данных, которые вы выделяете при обработке файлов. Сжатие с использованием del для удаления структур, которые вам больше не нужны во время обработки.Кроме того, вы можете использовать маркер , чтобы выгрузить часть обработанных данных на диск во время работы через следующие 100 МБ входных файлов.

Для чтения файлов у вас есть в основном два варианта: файлы в стиле unix как потоки или файлы с отображением памяти. Для потоков на основе файлов, объект питона по умолчанию файл уже буферизация, поэтому простейший код также, вероятно, является наиболее эффективным:

 
    with open("filename", "r") as f: 
    for line in f: 
     # do something with a line of the files 

Кроме того, вы можете использовать f.read ([размер]) для чтения блоков файл. Однако обычно вы делаете это, чтобы получить производительность процессора, многопоточно обрабатывая часть вашего сценария, чтобы вы могли одновременно читать и обрабатывать. Но это не помогает при использовании памяти; на самом деле он использует больше памяти.

Другой вариант ММАП, который выглядит следующим образом:

 
    with open("filename", "r+") as f: 
    map = mmap.mmap(f.fileno(), 0) 
    line = map.readline() 
    while line != '': 
     # process a line 
     line = map.readline() 

Это иногда обгоняет потоки, но это также не улучшит использование памяти.

0

В вашем примере кода данные хранятся в переменной fc. Если вы не сохраните ссылку на fc, все содержимое вашего файла будет удалено из памяти при завершении метода read.

Если это не так, то Вы сохраняете ссылку где-то. Возможно, ссылка создается в read_100_mb_file, может быть, в process. Если ссылок нет, реализация CPython будет освобождать его почти сразу.

Есть некоторые инструменты, чтобы помочь вам найти, где эта ссылка, guppy, dowser, pysizer ...

Смежные вопросы