Переменная, похоже, меняет размер на каждой итерации цикла - что?

при написании следующего кода Matlab:

for ii=1:n
    x(ii) = foo( ii ); % foo is some function of ii that cannot be vectorized.
end

Я получаю следующее м-Линт предупреждение:

переменная x Кажется, что изменяет размер на каждой итерации цикла

мой вопрос:

  1. что означает это предупреждение?
  2. почему изменение размера переменной на каждой итерации-это плохо?
  3. как эта проблема может быть решено?

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

4 ответов


Ну, первое, что первый.

1. Что означает это предупреждение?

этот код является правильным с точки зрения синтаксиса, и он будет выполняться правильно, возвращая ожидаемый результат:ii-й элемент x будет содержать значение foo( ii ).
Однако перед запуском этого небольшого фрагмента кода переменная x не определен. Теперь, когда цикл начинается, x(1) присваивается значение foo( 1 ), и поэтому Matlab создает x как массив длины-1. В вторая итерация x(2) присваивается значение foo( 2 ) и поэтому Matlab необходимо изменить x быть длины 2, и так далее: x изменяет свою длину / размер на каждой итерации.

2. Почему изменение размера переменной на каждой итерации-это плохо?

рассмотрим, что происходит в фоновом режиме (с точки зрения выделения памяти), когда x изменяет свой размер на каждой итерации: на каждой итерации Matlab должен найти свободное пространство памяти для размещения нового размера x. Если вам повезло, есть достаточно свободного места сразу после x таким образом, все, что происходит, это изменение объема памяти, выделенной x и запись нового значения в нужном месте.
Однако, если не хватает свободного места сразу после x, Matlab должен найти новое место для все the ii-1 элементы уже в x, выделить это новое пространство для x, скопировать все ii-1 значения уже в x к новому месту, и освободите старое пятно!--3--> использовать. Эти операции без выделения копий, происходящие в фоновом режиме, могут быть чрезвычайно трудоемкими, особенно когда x большой.

3. Как решить эту проблему?

самое простое решение-предварительно выделить все пространство x должен до петли:

x = zeros(1,n); 
for ii=1:n
    x( ii ) = foo( ii );
end

предварительно выделив, мы удостоверяемся, что x выделяется вся память, которая требуется спереди, поэтому нет дорогостоящего выделения/копирования памяти необходимо при выполнении цикла.

альтернативное классное решение проблемы

если вы слишком ленивы (как я) и не хотите предварительно выделить, Вы можете просто:

for ii=n:-1:1
    x( ii ) = foo( ii );
end

сюда, в первый раз x присваивается значение, которое присваивается его n - й элемент (последний) и, следовательно, Matlab тут выделяет место для всех n элементов x.
Круто!


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

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

Предупреждение Объяснил

по состоянию на R2014a, подробное объяснение предупреждения гласит следующее:

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

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

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

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

Автоматическая Оптимизация Роста Массива

Если вы хотите использовать динамическое изменение размера массива путем роста вдоль измерения (без предварительного распределения), есть способы сделать это правильно. Увидел эту MathWorks блог сообщение Стива Эддинса. Самое главное отметить, что вы должны растут вдоль последнего измерения для лучшей производительности. Это не проблема в вашем случае, так как массив 1D. Итак, если вы решите позволить ему ездить, положите %#ok<SAGROW> в той же строке, что и предупреждение, после кода виновника, чтобы отключить предупреждение.

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

предварительное выделение

рекомендуется предварительное выделение. Войти в привычку, научитесь любить zeros. Если вы намерены выжать каждый бит производительности из MATLAB, у Яира Альтмана есть пара отличных статей по теме предварительного выделения памяти:


есть много материала по этому вопросу. Вот некоторые ссылки на дополнительную информацию:

официальная документация и технические решения:

MathWorks Блоги:

Сообщество Блогов:

связанные вопросы переполнения стека / ответы:


этот ответ упрощен, щадя некоторые детали.

  1. M-lint предупреждает программиста, когда он замечает возможность предварительного выделения памяти. Предварительное выделение памяти улучшает время обработки, поскольку упрощает задачи вне прямого контроля программиста MATLAB, связанные с управлением оборудованием памяти.

  2. изменение размера переменной не всегда плохо; однако, это хорошая практика программирования для кода для более быстрой скорости обработки. Более быстрая скорость обработки более заметна, когда код использует много итераций или гораздо больше памяти.

  3. другие рекомендуемые методы предварительного распределения MATLAB будут работать; однако следующие методы могут быть более интуитивными.

    • присвоение значения предварительно выделенному массиву.

      x(1:n) = NaN; 
      for ii=1:n
          x(ii) = foo(ii); 
      end
      

      Примечания:

      • назначение NaN при предварительном выделении может быть выгодным, потому что NaN находится в массиве означает, что значение не было выделено.
      • при предварительном выделении в одном измерении единицы (n), нули(n) или NaN(n) выделят матрицу N x N. При работе с чрезвычайно большим набором данных это может замедлить скорость вычислений.
    • присвоение значения предварительно выделенному многомерному массиву:

      x(1:n_i,1:n_j) = NaN;  % or NaN(n_i, N_j);
      for ii=1:n_i
          for jj=1:n_j
              x(ii,jj) = foo(ii,jj); 
          end
      end
      
    • ленивый способ предварительного выделения-назначить ноль только последнему элементу в массиве.

      x(n)= 0;
      for ii=1:n
          x(ii) = foo(ii); 
      end