использование памяти при манипулировании / обработке больших структур данных - PullRequest
4 голосов
/ 04 февраля 2009

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

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, но я называю его после I del fc, например?

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

Ответы [ 6 ]

7 голосов
/ 05 февраля 2009

Я бы предложил посмотреть на презентацию Дэвида Бизли по использованию генераторов в Python. Этот метод позволяет обрабатывать большое количество данных и выполнять сложную обработку быстро и без лишних затрат памяти. IMO, уловка не удерживает огромное количество данных в памяти настолько эффективно, насколько это возможно; Хитрость заключается в том, чтобы избежать одновременной загрузки большого объема данных в память.

3 голосов
/ 04 февраля 2009

Не читайте весь файл размером 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(' ')))
3 голосов
/ 04 февраля 2009

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

2 голосов
/ 04 февраля 2009

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

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, но я использовал здесь явный цикл for для ясности. Вот то же самое, используя reduce:

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

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

0 голосов
/ 13 февраля 2009

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

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

Есть несколько инструментов, которые помогут вам найти эту ссылку: гуппи , пуховая , pysizer ...

0 голосов
/ 05 февраля 2009

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

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

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

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

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

Другая опция - это mmap, которая выглядит следующим образом:

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

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

...