Верхний предел памяти? - PullRequest
       11

Верхний предел памяти?

25 голосов
/ 26 ноября 2010

Есть ли предел памяти для питона? Я использовал скрипт на python для вычисления средних значений из файла размером не менее 150 МБ.

В зависимости от размера файла я иногда сталкиваюсь с MemoryError.

Можно ли выделить больше памяти для питона, чтобы я не столкнулся с ошибкой?


РЕДАКТИРОВАТЬ: код ниже

ПРИМЕЧАНИЕ. Размер файла может сильно различаться (до 20 ГБ), минимальный размер файла составляет 150 МБ

.
file_A1_B1 = open("A1_B1_100000.txt", "r")
file_A2_B2 = open("A2_B2_100000.txt", "r")
file_A1_B2 = open("A1_B2_100000.txt", "r")
file_A2_B1 = open("A2_B1_100000.txt", "r")
file_write = open ("average_generations.txt", "w")
mutation_average = open("mutation_average", "w")

files = [file_A2_B2,file_A2_B2,file_A1_B2,file_A2_B1]

for u in files:
    line = u.readlines()
    list_of_lines = []
    for i in line:
        values = i.split('\t')
        list_of_lines.append(values)

    count = 0
    for j in list_of_lines:
        count +=1

    for k in range(0,count):
        list_of_lines[k].remove('\n')

    length = len(list_of_lines[0])
    print_counter = 4

    for o in range(0,length):
        total = 0
        for p in range(0,count):
            number = float(list_of_lines[p][o])
            total = total + number
        average = total/count
        print average
        if print_counter == 4:
            file_write.write(str(average)+'\n')
            print_counter = 0
        print_counter +=1
file_write.write('\n')

Ответы [ 5 ]

30 голосов
/ 27 ноября 2010

(Это мой третий ответ, потому что я неправильно понял, что твой код делал в моем оригинале, а затем допустил небольшую, но решающую ошибку в моем втором - надеюсь, три очарования.

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

Как уже отмечали другие, ваша проблема MemoryError наиболее вероятна, потому что вы пытаетесь прочитать все содержимое огромных файлов в память, а затем, наВдобавок к этому, фактически удваивая объем памяти, необходимый путем создания списка списков строковых значений из каждой строки.

Пределы памяти Python определяются тем, сколько физического ОЗУ и дискового пространства виртуальной памяти ваш компьютер и работаетсистема доступна. Даже если вы не используете ее все и ваша программа «работает», ее использование может быть нецелесообразным, посколькуслишком долго.

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

Для этогосохраняется список промежуточных итогов для каждого из полей.Когда это закончено, среднее значение каждого поля может быть вычислено путем деления соответствующего общего значения на количество прочитанных строк.Как только это будет сделано, эти средние значения могут быть распечатаны, а некоторые записаны в один из выходных файлов.Я также приложил сознательные усилия, чтобы использовать очень описательные имена переменных, чтобы сделать их понятными.

try:
    from itertools import izip_longest
except ImportError:    # Python 3
    from itertools import zip_longest as izip_longest

GROUP_SIZE = 4
input_file_names = ["A1_B1_100000.txt", "A2_B2_100000.txt", "A1_B2_100000.txt",
                    "A2_B1_100000.txt"]
file_write = open("average_generations.txt", 'w')
mutation_average = open("mutation_average", 'w')  # left in, but nothing written

for file_name in input_file_names:
    with open(file_name, 'r') as input_file:
        print('processing file: {}'.format(file_name))

        totals = []
        for count, fields in enumerate((line.split('\t') for line in input_file), 1):
            totals = [sum(values) for values in
                        izip_longest(totals, map(float, fields), fillvalue=0)]
        averages = [total/count for total in totals]

        for print_counter, average in enumerate(averages):
            print('  {:9.4f}'.format(average))
            if print_counter % GROUP_SIZE == 0:
                file_write.write(str(average)+'\n')

file_write.write('\n')
file_write.close()
mutation_average.close()
18 голосов
/ 26 ноября 2010

Вы читаете весь файл в память (line = u.readlines()), что, конечно, не удастся, если файл слишком велик (и вы говорите, что некоторые имеют размер до 20 ГБ), так что это ваша проблема прямо сейчас.

Лучше перебирать каждую строку:

for current_line in u:
    do_something_with(current_line)

- рекомендуемый подход.

Позже в вашем скрипте вы делаете очень странные вещи, например, сначала подсчитываете все элементы в списке, затем строите цикл for по диапазону этого количества. Почему бы не выполнить итерацию по списку напрямую? Какова цель вашего сценария? У меня сложилось впечатление, что это можно сделать гораздо проще.

Это одно из преимуществ высокоуровневых языков, таких как Python (в отличие от C, где вам приходится выполнять эти служебные задачи самостоятельно): разрешить Python выполнять итерации за вас и собирать в памяти только то, что вам действительно нужно иметь в памяти в любой момент времени.

Кроме того, так как кажется, что вы обрабатываете файлы TSV (значения, разделенные табуляцией), вы должны взглянуть на csv модуль , который будет обрабатывать все разбиение, удаление \n S и т. д. для вас.

15 голосов
/ 26 ноября 2010

Python может использовать всю доступную память для своего окружения. Мой простой «тест памяти» падает на ActiveState Python 2.6 после использования около

1959167 [MiB]

На jython 2.5 он падает раньше:

 239000 [MiB]

вероятно, я могу настроить Jython для использования большего количества памяти (он использует ограничения из JVM)

Тестовое приложение:

import sys

sl = []
i = 0
# some magic 1024 - overhead of string object
fill_size = 1024
if sys.version.startswith('2.7'):
    fill_size = 1003
if sys.version.startswith('3'):
    fill_size = 497
print(fill_size)
MiB = 0
while True:
    s = str(i).zfill(fill_size)
    sl.append(s)
    if i == 0:
        try:
            sys.stderr.write('size of one string %d\n' % (sys.getsizeof(s)))
        except AttributeError:
            pass
    i += 1
    if i % 1024 == 0:
        MiB += 1
        if MiB % 25 == 0:
            sys.stderr.write('%d [MiB]\n' % (MiB))

В вашем приложении вы читаете весь файл одновременно. Для таких больших файлов вы должны читать построчно.

8 голосов
/ 26 ноября 2010

Нет, для Python нет ограничений на использование памяти приложением Python. Я регулярно работаю с приложениями Python, которые могут использовать несколько гигабайт памяти. Скорее всего, ваш скрипт на самом деле использует больше памяти, чем доступно на машине, на которой вы работаете.

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

Edit:

Ваш скрипт одновременно считывает все содержимое ваших файлов в память (line = u.readlines()). Поскольку вы обрабатываете файлы размером до 20 ГБ, при таком подходе вы будете получать ошибки памяти, если у вас на компьютере не будет большого объема памяти.

Лучше было бы читать файлы по одной строке за раз:

for u in files:
     for line in u: # This will iterate over each line in the file
         # Read values from the line, do necessary calculations
5 голосов
/ 27 ноября 2010

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

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

Вот ваш сценарий, переписанный с удаленным каперсом readlines () и значимыми именами:

file_A1_B1 = open("A1_B1_100000.txt", "r")
file_A2_B2 = open("A2_B2_100000.txt", "r")
file_A1_B2 = open("A1_B2_100000.txt", "r")
file_A2_B1 = open("A2_B1_100000.txt", "r")
file_write = open ("average_generations.txt", "w")
mutation_average = open("mutation_average", "w") # not used
files = [file_A2_B2,file_A2_B2,file_A1_B2,file_A2_B1]
for afile in files:
    table = []
    for aline in afile:
        values = aline.split('\t')
        values.remove('\n') # why?
        table.append(values)
    row_count = len(table)
    row0length = len(table[0])
    print_counter = 4
    for column_index in range(row0length):
        column_total = 0
        for row_index in range(row_count):
            number = float(table[row_index][column_index])
            column_total = column_total + number
        column_average = column_total/row_count
        print column_average
        if print_counter == 4:
            file_write.write(str(column_average)+'\n')
            print_counter = 0
        print_counter +=1
file_write.write('\n')

Быстро становится очевидно, что (1) вы вычисляете средние значения по столбцам (2) запутывание заставило некоторых других думать, что вы вычисляете средние значения по строкам.

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

Вот пересмотренная версия кода внешнего цикла:

for afile in files:
    for row_count, aline in enumerate(afile, start=1):
        values = aline.split('\t')
        values.remove('\n') # why?
        fvalues = map(float, values)
        if row_count == 1:
            row0length = len(fvalues)
            column_index_range = range(row0length)
            column_totals = fvalues
        else:
            assert len(fvalues) == row0length
            for column_index in column_index_range:
                column_totals[column_index] += fvalues[column_index]
    print_counter = 4
    for column_index in column_index_range:
        column_average = column_totals[column_index] / row_count
        print column_average
        if print_counter == 4:
            file_write.write(str(column_average)+'\n')
            print_counter = 0
        print_counter +=1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...