Верхний предел памяти?
Есть ли предел памяти для python? Я использовал скрипт 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 ответов
(Это мой третий ответ, потому что я неправильно понял, что твой код делал в моем оригинале, а затем сделал небольшую, но важную ошибку в моем втором - надеюсь, три очарование.
Редактирование: Поскольку это, кажется, популярный ответ, я сделал несколько модификаций, чтобы улучшить его реализацию на протяжении многих лет - большинство не слишком много. Это так, если люди используют его в качестве шаблона, это обеспечит еще лучшую основу.
Как уже отмечалось, ваши 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()
Вы читаете весь файл в память (line = u.readlines()
), который, конечно, потерпит неудачу, если файл слишком большой (а вы говорите, что некоторые занимают до 20 ГБ), так что это ваша проблема.
Лучше повторить каждую строку:
for current_line in u:
do_something_with(current_line)
это рекомендуемый подход.
Позже в вашем скрипте вы делаете очень странные вещи, например сначала подсчитываете все элементы в списке, а затем создаете for
цикл по диапазону этого количества. Почему бы не выполнить итерацию по списку напрямую? Какова цель вашего сценария? У меня сложилось впечатление, что это можно сделать гораздо проще.
Это одно из преимуществ высокоуровневых языков, таких как Python (в отличие от C, где вам приходится выполнять эти служебные задачи самостоятельно): разрешите Python обрабатывать итерации для себя и собирать только в памяти то, что вам действительно нужно иметь в память в любой момент времени.
Кроме того, поскольку кажется, что вы обрабатываете файлы TSV (значения, разделенные табуляцией), вы должны взглянуть на csv
модуль, который будет обрабатывать все расщепление, удаление \n
и т. д. для вас.
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))
В вашем приложении вы читаете весь файл сразу. Для таких больших файлов вы должны читать построчно.
Нет, для Python нет ограничений на использование памяти приложением Python. Я регулярно работаю с приложениями Python, которые могут использовать несколько гигабайт памяти. Скорее всего, ваш скрипт на самом деле использует больше памяти, чем доступно на машине, на которой вы работаете.
В этом случае решение состоит в том, чтобы переписать сценарий для более эффективного использования памяти или добавить больше физической памяти, если сценарий уже оптимизирован для минимизации использования памяти.
Редактировать:
Ваш скрипт считывает все содержимое ваших файлов в память одновременно (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
Вы не только читаете весь файл в память, но и кропотливо копируете информацию в таблице 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