Perl: ошибка нехватки памяти при построении двумерного массива во время выполнения

Я новичок в перле. Я пытаюсь построить 2D-массив во время выполнения из двоичного файла. Я получаю сообщение об ошибке "недостаточно памяти". Я использую Perl 5.16.3 в Windows7. Размер моего входного файла составляет ~ 4,2 МБ. В моей системе физическая память составляет 4 ГБ, и я использую 90%, а затем при запуске этого кода появляется ошибка нехватки памяти.

Я пробовал много способов отладить это. Только если я уменьшу b32 до b16 или меньше, я смогу успешно работать. Даже при этом, если размер файла превышает 4 МБ, ошибка появляется снова. Я попытался посмотреть на использование физической памяти в диспетчере задач во время выполнения кода, оно продолжает увеличиваться.

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

#!/usr/bin/perl
use strict;
use warnings;

open( DATA, 'debug.bin' ) or die "Unable to open:$!";
binmode DATA;
my ( $data, $n, $i );
my @2dmatrix;
while ( $n = read DATA, $data, 4 ) {
    push @2dmatrix, [ split( '', unpack( 'b32', $data ) ) ];
}
print scalar(@2dmatrix);
print "completed reading";
close(DATA);

Просто, чтобы очистить требование. Из сборки 2d-массива мне нужно извлечь содержимое из столбца A, соответствующего определенному шаблону (11111111000000001111111100000000) в столбце B. Это нужно сделать для 4 наборов столбцов с размером файла 500 МБ.


person tirumalesh    schedule 02.11.2014    source источник
comment
Попробуйте разбить данные на блоки меньшего размера, что может помочь вам устранить ошибку нехватки памяти.   -  person Praveen    schedule 02.11.2014
comment
английские требования намного более неоднозначны, чем код; вам нужно только 40 строк за раз?   -  person ysth    schedule 03.11.2014
comment
@ysth Да. но какие 40 рядов нельзя решить напрямую. Мне приходится игнорировать множество промежуточных строк, в зависимости от варианта использования. Итак, я выбрал сборку всего массива и процесс.   -  person tirumalesh    schedule 03.11.2014


Ответы (3)


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

Для каждых 4 байтов, которые вы читаете, вы делаете unpack 'b32', что создает 32-символьную строку; split //, который превращает его в 32 односимвольные строки, создайте ссылку на массив из полученного списка и поместите ссылку на массив в @2dmatrix. Это приводит к:

  • 32 тела строки, каждое размером не менее 2 байт (для "0\0" или "1\0"), хотя Perl может решить использовать больше, чтобы избежать перераспределения, если строки вырастут: 64 байта.
  • 32 SVPV (скалярные переменные, содержащие строки, по 28 байтов для 32-разрядных систем, по 40 байтов для 64-разрядных систем): 896 или 1280 байтов.
  • 1 тело массива с 32 элементами: 128 байт для 32-разрядной версии, 256 байт для 64-разрядной версии.
  • 1 AV (переменная массива): 28 байт для 32-разрядной версии, 40 байт для 64-разрядной версии.
  • 1 SVRV (скаляр, содержащий ссылку): 16 байтов для 32-разрядных, 24 байта для 64-разрядных.
  • 1 запись в теле массива @2dmatrix: 4 байта для 32-битных, 8 байтов для 64-битных.

С результатом 1136 байтов на 4 байта (284-кратное умножение) для 32-разрядных и 1672 байта на 4 байта (418-кратное умножение) на 64-разрядных без учета постоянных факторов и того факта, что Perl может предпочесть использовать более крупные тела строк ( на двух версиях Perl, которые я тестировал здесь, я получил либо 10, либо 16 байт, а не 2.) Таким образом, ваша программа будет использовать более 1,1 ГБ памяти для ввода 4,2 МБ в 32-разрядной системе и более 1,7 ГБ памяти для ввода 4,2 МБ в 64-разрядной системе.

Решение здесь состоит в том, чтобы хранить и получать доступ к данным более эффективным способом, но я не могу дать никаких конкретных советов, потому что вы не сказали, что вы на самом деле пытаетесь сделать с @2dmatrix после того, как вы иметь это.

person hobbs    schedule 02.11.2014
comment
хранение чисел вместо строк — легко достижимая эффективность ([ map $_?1:0, split...] вместо [split...]), экономия 576 байтов на 64-битном perl. - person ysth; 02.11.2014
comment
@ysth да, на самом деле я бы предложил хранить 32-битные числа и использовать комбинацию индексации массива и смещения битов для доступа к членам в качестве достойного компромисса между пространством и удобством (приводит вас к массиву записей в ~ 1 миллион без косвенности), но что-нибудь от vec до фактического чтения файла вообще может работать, поэтому я хотел бы услышать больше требований :) - person hobbs; 02.11.2014
comment
если они собираются внести изменения в код того, что на самом деле использует массив массивов, просто прочитать весь файл в строку и использовать vec, вероятно, проще всего - person ysth; 02.11.2014
comment
Спасибо за все ваши ответы. Я хотел сохранить данные в виде двоичной матрицы. @ysth: я попробую твою идею. - person tirumalesh; 03.11.2014
comment
@tirumalesh, пожалуйста, покажите, как вы используете данные по запросу hobbs; это поможет нам дать вам лучший совет - person ysth; 03.11.2014
comment
@ysth уже обновил вопрос, добавив подробности о том, как я хочу использовать данные. дайте мне знать, если вам нужны какие-либо другие детали. - person tirumalesh; 03.11.2014
comment
@hobbs Я хочу обработать столбец 2d-матрицы. Так что строится как 32-битные числа мне не поможет. насчет век не знаю. пытаюсь понять это онлайн. - person tirumalesh; 03.11.2014

Забудьте о чтении в памяти всего содержимого файла. Создайте функцию для доступа к данным (x, y), которая впоследствии будет обращаться к значению в файле. Map.pm#Advantages_of_memory_mapping" rel="nofollow">http://search.cpan.org/~leont/File-Map-0.63/lib/File/Map.pm#Advantages_of_memory_mapping

person Boris Ivanov    schedule 02.11.2014
comment
4 МБ данных могут отлично поместиться в памяти без сопоставления памяти с файлом. Проблема в том, что текущий подход ОП крайне неэффективен. - person nobody; 02.11.2014
comment
Извините, недоразумение. Я думал о файле 4Gb. - person Boris Ivanov; 03.11.2014

Чтение всего файла в строку и использование vec выглядит так:

my $data = do { local $/; <DATA> };

Затем, чтобы получить конкретную строку/столбец, используйте:

$value = vec( $data, $row*32+$col, 1 );
person ysth    schedule 03.11.2014
comment
Я пытаюсь понять ваш пример, значение $ выше читает элемент [$ row, $ col] в массиве 2d. Могу ли я прочитать массив строк/столбцов из vec? - person tirumalesh; 03.11.2014
comment
используйте vec($data, $row*32+$col, 1) вместо $twodarray[$row][$col]; так понятнее? - person ysth; 03.11.2014