В MATLAB очень медленно вкладывается в циклы - PullRequest
4 голосов
/ 18 октября 2011

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

Я загрузил мои изображения RGB в 4-мерном массиве со следующими размерами:

uint8 [ num_images, width, height, RGB ]

Вот функция, которую я написал, которая включает 4 вложенных цикла. Я использую preallocation , но все же, он очень медленный. Я полагаю, что в C ++ эта функция может работать как минимум в 10–20 раз быстрее, и я думаю, что в CUDA она может выполняться в реальном времени. В MATLAB это занимает около 20 секунд с 4 вложенными циклами. Мой стек составляет 100 изображений с размерами 640x480x3.

function background = calc_background(stack)
tic;

si = size(stack,1);
sy = size(stack,2);
sx = size(stack,3);
sc = size(stack,4);

background = zeros(sy,sx,sc);
A = zeros(si,1);

for x = 1:sx
    for y = 1:sy
        for c = 1:sc
            for i = 1:si
                A(i) = stack(i,y,x,c);
            end
            background(y,x,c) = median(A);
        end
    end
end

background = uint8(background);

disp(toc);
end

Не могли бы вы сказать, как сделать этот код намного быстрее? Я попытался поэкспериментировать с каким-либо способом получения данных непосредственно из массива, используя только индексы, и это кажется НАМНОГО быстрее. Он завершается за 3 секунды против 20 секунд , так что это разница в производительности в 7 раз, просто написав меньшую функцию.

function background = calc_background2(stack)
    tic;

    % bad code, confusing
    % background = uint8(squeeze(median(stack(:, 1:size(stack,2), 1:size(stack,3), 1:3 ))));

    % good code (credits: Laurent)
    background=uint8((squeeze(median(stack,1)));

    disp(toc);
end

Так что теперь я не понимаю , если MATLAB может быть таким быстрым, тогда почему версия с вложенным циклом такая медленная? Я не делаю динамического изменения размера, и MATLAB должен запускать те же 4 вложенных цикла внутри .

Почему это происходит?

Есть ли способ ускорить выполнение вложенных циклов, как это было бы естественно в C ++?

Или я должен привыкнуть к идее программирования MATLAB в этом сумасшедшем однострочном способе получения оптимальной производительности?

Обновление

Спасибо за все великолепные ответы, теперь я понимаю намного больше. Мой исходный код с stack(:, 1:size(stack,2), 1:size(stack,3), 1:3 )) не имеет никакого смысла, он точно такой же, как stack, мне просто повезло с медианной опцией по умолчанию, использующей 1-е измерение для своего рабочего диапазона.

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

Как писать векторизованные функции в MATLAB

Ответы [ 3 ]

4 голосов
/ 18 октября 2011

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

2 голосов
/ 18 октября 2011

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

Оценка - это длительный процесс, поскольку она должна анализировать, компилировать и интерпретировать каждую строку *. Использование циклов for с простыми операциями означает, что для синтаксического анализа / компиляции matlab требуется гораздо больше времени, чем для выполнения кода.

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

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

* Чтобы быть полным и воздать должное тому, кому причитается честь: последние версии Matlab фактически пытаются ускорить циклы, анализируя повторяющиеся операции для компиляции кусков повторяющихся операций в собственный исполняемый файл. Это называется компиляцией Just In Time (JIT) и было отмечено Jonas в следующих комментариях.


Оригинальный ответ:

Если я хорошо понял (и вам нужна медиана первого измерения), вы можете попробовать:

background=uint8((squeeze(median(stack,1)));
1 голос
/ 18 октября 2011

Ну, разница между ними заключается в их методе выполнения кода. Чтобы набросать это очень грубо: в C вы передаете свой код компилятору, который попытается оптимизировать ваш код или, во всяком случае, преобразовать его в машинный код. Это занимает некоторое время, но когда вы фактически выполняете свою программу, она уже находится в машинном коде и, следовательно, выполняется очень быстро. Ваш компилятор может потратить много времени, пытаясь оптимизировать код для вас, в общем, вам все равно, потребуется ли 1 или 10 минут для компиляции готовой к распространению программы.

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

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

За кулисами MATLAB использует скомпилированный код (Fortan / C / C ++, если я не ошибаюсь) для выполнения больших операций: таким образом, умножение матриц действительно выполняется фрагментом машинного кода, который был скомпилирован из другого языка. Для небольших операций это также верно, но вы не заметите скорость этих вычислений, так как большая часть вашего времени уходит на код управления (передача переменных, выделение памяти, ...).

Подводя итог всему: да, вы должны привыкнуть к таким компактным утверждениям. Если вы видите строку кода, подобную примеру Лорана, вы сразу же видите, что она вычисляет медиану стека. Ваш код требует 11 строк кода, чтобы выразить то же самое, поэтому, когда вы смотрите на код, похожий на ваш (который может быть встроен в сотни строк другого кода), вам будет сложнее понять, что происходит, и точно определить, где находится определенный код. операция выполнена.

Если рассуждать еще дальше: вы не должны программировать на MATLAB так же, как программируете на C / C ++; и вы не должны делать наоборот. У каждого языка есть свои сильные и слабые стороны, учитесь их знать и используйте каждый язык для того, для чего он создан. Например. Вы можете написать целый компилятор или веб-сервер в MATLAB, но в целом это будет очень медленно, поскольку MATLAB не предназначен для обработки или объединения строк (это возможно, но это может быть очень медленно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...