2015-07-22 1 views
2

Моя проблема заключается в том, чтобы быстро выполнить много низкоразмерных выступов в Matlab. У меня есть массив z который имеет размеры (n,L,d); эти параметры получаются следующим образом. Я беру входной массив размера N, например N = [200, 200] и n = prod(N) = 200*200 = 40,000 и d = numel(N) = 2; т. е. n - количество точек в моей сетке дискретизации, а d - размер входного массива (например, изображение или плоскость из карты высоты). Затем я дискретирую возможные высоты (что моя программа будет выводить - обратите внимание на карту высоты выше) с L пунктами, скажем L = 32.Быстрый алгоритм для нескольких проекций в Matlab

Для каждого i = 1:n и j = 1:L, я хочу, чтобы спроецировать вектор z(i,j,:) на единичный шар * На данный момент у меня есть следующий наивный код:.

z = reshape(z,[n,L,d]); z_norms = norms(z,2,3); 
for i = 1:n 
for j = 1:L 
    z(i,j,:) = z(i,j,:)/max(1,z_norms(i,j)); 
end 
end 

Функция norms(v,p,dim) принимает р норму матрицы v вдоль размерности dim (в этом случае выводится матрица (n,L)).

У меня есть различные идеи относительно того, как это можно улучшить. Одна из идей заключалась в следующем:

for i = 1:n 
for j = 1:L 
normsquared = sum(z(i,j,:).^2) 
if normsquared > 1 
    z(i,j,:) = z(i,j,:)/sqrt(normsquared) 
end 
end 
end 

Обратите внимание, что normsquared переписывается каждый раз, так что не занимая мое место. Когда я использовал это по другой проблеме, это ускорило процесс довольно много; однако я только что протестировал его по этой проблеме, и это на самом деле значительно хуже - примерно в три раза медленнее; на самом деле, требуется примерно в два с половиной раза вычислить normsquared, как это делается для проекции в первом случае!

Странно, если я изменю sum(z(i,j,:).^2) на z(i,j,1)^2 + z(i,j,2)^2 (в случае с d = 2), то это на самом деле немного быстрее, чем первый (наивный) метод ... если кто-то может объяснить это мне тоже, тогда это будь здорово!

Если у кого-то есть совет, как это ускорить, тогда я был бы очень благодарен! В настоящее время на это потрачено около 90% времени моей программы.


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

ответ

2

Там нет необходимости использовать parfor, когда вы можете переписать двойную for петли с помощью bsxfun к «векторизовать» операция:

z = bsxfun(@rdivide,z,max(1,z_norms)) 

max функция векторизация на пороговой из в n- by-L z_norms, так что все значения меньше или равны единице. z представляет собой трехмерный массив n-by-L-by-d.bsxfun фактически реплицирует нижний размер z_norms матрицу d раз по третьему размеру z, так что может выполняться элементное разделение (rdivide). Результатом является массив n-by-L-by-d.

После profiling ваш код, переписывающие циклы, чтобы использовать возможности Matlab vectorization, должны быть одной из первых вещей, которые вы пытаетесь сделать improve performance.

+0

Спасибо. Да, это то, что я обычно делаю. Я предпочитаю писать код в наивном формате, прежде всего, только чтобы убедиться, что все это работает, а затем верните vectorize for-loops. Например, я имел «для j = 1: L, sum = sum + Gamma (j) * u (:, j), end", поэтому я изменил это на u * Gamma '(Gamma was (1, L)) , –

+0

Можете ли вы объяснить немного больше, что делает функция «bsxfun»? Я видел, что раньше это использовалось для нескольких других вещей - я думаю, что это было векторизация умножения столбцов матрицы на вектор покомпонентно или что-то в этом роде. –

+0

Кроме того, я думаю, вы имеете в виду «@rdivide», а не «@ldivide». Документация Matlab для 'bsxfun' наиболее неясна в этом вопросе, но я попробовал ее с' l', и это не сработало, но с 'r'. Как ни странно, использование 'bsxfun' не дает * точно * того же результата, что и использование циклов, только очень * похожего результата ... –

1

Вы можете попробовать заменить внутренний цикл for петлей parfor. parfor позволяет запускать несколько итераций одновременно на многоядерных процессорах.

http://nl.mathworks.com/help/distcomp/parfor.html

+0

Хорошо, спасибо. У меня четырехъядерный процессор (поэтому, если я могу ускорить его восемь раз, то это будет потрясающе!). Ваша поговорка, которая дала мне идею, хотя: поскольку я делаю то же самое, довольно просто, обрабатывать каждый раз, будет ли это хорошей операцией для работы на моем графическом процессоре? Я только на ноутбуке (GeForece 750M посвященный) на данный момент, но у меня есть 5-летняя игровая карта дома (ATi 4900, 2 ГБ GDDR5 RAM). Считаете ли вы, что это будет иметь большое значение? –

+0

Кроме того, нет необходимости в том, чтобы циклы были в этом порядке. Было бы лучше, если бы я поменял порядок, чтобы я мог параллельно использовать n-length (n >> L) или лучше запустить параллельную L-длину параллельно? –

+1

@SmileySam 'parfor' - это процессор. Для вычисления GPU требуется другая структура алгоритма и немного больше обучения, чтобы заставить его работать. Если вы запустите вещь в «parpool», для петель обычно не произойдет. В общем, вы бы хотели иметь только 1 цикл, поэтому, если вы можете сжать, что вложенный в 1, лучше (подсказка: вы можете). –