Распределение памяти MatLab, когда максимальный размер неизвестен

Я пытаюсь ускорить скрипт, который я написал в Matlab, который динамически выделяет память матрице (в основном читает строку данных из файла и записывает ее в матрицу, затем читает другую строку и выделяет больше памяти для большей матрицы для сохранения следующей строки). Причина, по которой я сделал это вместо предварительного выделения памяти с использованием нулей () или чего-то еще, заключалась в том, что я не знаю точного размера матрицы, которая должна быть для хранения всех данных. Я также не знаю максимальный размер матрицы, поэтому я не могу просто предварительно выделить максимальный размер, а затем избавиться от памяти, которую я не использовал. Это было нормально для небольших объемов данных, но теперь мне нужно масштабировать мой скрипт, чтобы считывать многие миллионы точек данных, а эта реализация динамического распределения слишком медленная.

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

data = [];   
count = 0;

for ii = 1:num_filelines    
   if mod(count, 1000) == 0  
       data = [data; zeroes(1000)];  %after 1000 lines are read, allocate another 1000 line
   end  
   data(ii, :) = line_read(file);  %line_read reads a line of data from 'file'
end

К сожалению, это не работает, когда я запускаю его, я получаю сообщение об ошибке: «Ошибка при использовании vertcat. Размеры объединяемых матриц не согласованы».

Итак, вот мой вопрос: действительно ли этот метод выделения памяти в больших блоках быстрее, чем инкрементное динамическое выделение, а также почему приведенный выше код не работает? Спасибо за помощь.


person dynamo    schedule 05.08.2013    source источник
comment
когда вы делаете нули (1000), вы выделяете матрицу нулей 1000x1000. Возможно, поэтому ваш код не работает. Что касается скорости выделения, всегда хорошо предварительно выделять в Matlab, но вместо использования массивов при чтении в строках используйте ячейки для хранения данных. Таким образом, у вас не будет столько проблем. Либо так, либо используйте разреженную матрицу.   -  person MZimmerman6    schedule 05.08.2013
comment
Это будет несколько зависеть от версии Matlab, которую вы используете. Было показано, что последние версии очень быстро распределяют динамическую память. Подробнее.   -  person horchler    schedule 05.08.2013


Ответы (3)


Что я рекомендую сделать, если вы знаете количество строк и можете просто угадать достаточно большое количество допустимых столбцов, используйте sparse matrix.

% create a sparse matrix
mat = sparse(numRows,numCols)

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

Если нет, вы можете просто сделать все как cell. Предварительно выделите cell array столько элементов, сколько строк в вашем файле.

data = cell(1,numLines);
% get matrix from line
for i = 1:numLines
    % get matrix from line
    data{i} = lineData;
end
data = cell2mat(data);

Этот метод помещает все в массив ячеек, который может храниться «динамически», а затем преобразовываться в обычную матрицу.

Дополнение

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

[val,~] = max(sum(mat ~= 0,2));
mat(:,val:size(mat,2)) = [];
mat = full(mat); % use this only if you really need the full matrix

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

ОБНОВЛЕНИЕ

Чтобы легко получить количество строк в файле, используйте интерпретатор Perl MATLAB.

создайте файл с именем countlines.pl и вставьте две строки ниже

while (<>) {};
print $.,"\n";

Затем вы можете запустить этот скрипт в своем файле следующим образом

numLines = str2double(perl('countlines.pl','data.csv'));

Проблема решена.

Из ветки форума MATLAB здесь

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

person MZimmerman6    schedule 05.08.2013
comment
вы можете захотеть зарезервировать место для ненулевых значений в разреженной матрице, угадывая/оценивая количество ненулевых значений - person Shai; 05.08.2013
comment
@Shai Я не уверен, что вы имеете в виду, разреженная матрица будет обрабатывать распределение и все остальное для вас. - person MZimmerman6; 05.08.2013
comment
Я недостаточно ясно выразился в своем вопросе, но на самом деле я не знаю, сколько строк мне нужно. Исправление Shai в коде, которое я опубликовал, решает проблему, которая у меня была, и значительно ускоряет мой скрипт. Спасибо за предложение использовать sparse() и указатели, но я об этом не подумал. Теперь, когда моя проблема решена, мне любопытно, масштабируется ли мой метод линейно с количеством строк данных или это намного лучше, чем динамическое добавление новых строк? Наверное, я не понимаю, почему выделение памяти в блоках более эффективно - person dynamo; 05.08.2013
comment
Я разместил метод, чтобы получить количество строк в файле, это быстро и легко. и будет работать и для больших чисел - person MZimmerman6; 05.08.2013
comment
Я не знал, что у Matlab есть интерпретатор perl, я думаю, этот метод будет лучше масштабироваться с количеством строк данных, которые мне нужно прочитать, чем мой метод. На самом деле я не очень понимаю, почему мой метод быстрее, чем просто перераспределение каждого шага (хотя это, конечно, так). - person dynamo; 05.08.2013
comment
@MZimmerman6 см. второй пример использования здесь аргумента nzmax. - person Shai; 05.08.2013
comment
@ Шай, да, ты мог бы это сделать. Вероятно, это ускорит работу, но это станет очевидным после загрузки данных. Вы не обязательно хотите перегружать память, хотя это может и не понадобиться, особенно если вы работаете в системе с небольшим объемом оперативной памяти. - person MZimmerman6; 05.08.2013
comment
Даже если вы не знаете количество строк, гораздо быстрее использовать ячейку и перебирать все строки в конце, записывая их в матрицу (правильного размера), чем использовать разреженную матрицу в первую очередь. - person Thilo; 04.10.2018

Чтобы решить вашу ошибку, просто используйте этот синтаксис при распределении

data = [data; zeroes(1000, size(data,2))];

Возможно, вы захотите прочитать первую строку вне цикла, чтобы узнать количество столбцов и сделать первое выделение для data.

person Shai    schedule 05.08.2013
comment
Да, это была ошибка, не введено правильное количество столбцов для матрицы нулей, упс. Я знаю, сколько столбцов мне нужно, в то время как количество строк неизвестно, но чтение первой строки отдельно также является отличной идеей, чтобы сделать сценарий более обобщенным, спасибо. - person dynamo; 05.08.2013

Если вы хотите придерживаться своего кода, как написано, я бы заменил вашу инициализацию данных, data = [] на

data = zeros(1,1000); 

Имейте в виду, что предупреждение от @MZimmerman6: zeros(1000) генерирует массив 1000 x 1000. Вы можете изменить все свои операторы zeros на zeros( ... ,Nc), где Nc = длина строки в символах.

person Buck Thorn    schedule 06.08.2013