2012-09-06 2 views
1

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

sum.

Рассчитать эту сумму 2500 раз с новыми значениями C и z.

Аргумент z может быть вектором. Я написал простым для цикла и векторизованного версии кода следующим образом:

K = 200; 
n_z = 40000; 
C = ones(K,1); % an example, in real life the arey some coefficients (at next call will be new) 
k = 0:K-1; 
z = linspace(0, 2*pi, n_z); % at next call will be new 

tic; 
    my_sum_for = zeros(1, K); 
    for i=1:n_z 
     my_sum_for(i) = C' * tan(k' * z(i)); 
    end 
toc; % Elapsed time is 1.820485 seconds. 

tic; 
    my_sum = C' * tan(k' * z); 
toc; % Elapsed time is 0.160924 seconds. 

Векторизованной версия работает быстрее, но не достаточно. Можно ли улучшить векторную версию?

После ответа Доминик Jacquel, я есть эта векторизованную версия, это быстрее:

K = 200; 
    n_z = 40000; 
    C = ones(K,1)'; % an example, in real life they are some coefficients (at next call will be new) 
    k = (0:K-1)'; 
    z = linspace(0, 2*pi, n_z); % at next call will be new 

    tic; 
     my_sum_for = zeros(1, K); 
     for i=1:n_z 
      my_sum_for(i) = C * tan(k * z(i)); 
     end 
    toc; % Elapsed time is 1.521587 seconds. 

    tic; 
     my_sum = C * tan(k * z); 
    toc; % Elapsed time is 0.125468 seconds. 

Можно ли улучшить векторизованную версию еще больше (bsxfun, arrayfun или что-то)? Время 250 секунд для меня все еще медленное (это 75% всех вычислений).

+0

'all (my_sum_for == my_sum) -> ans = 0' ... так что вы этого не сделали? Мне кажется, что вы делаете какую-то странную перестановку, которая не нужна .., –

+1

Я проверил это, вычислив норму (my_sum_for, my_sum) = 1.7861e-10. Для меня не ново, что векторизованные и зацикленные версии одного и того же кода дают несколько разные результаты. – N0rbert

+0

Является ли фактическое содержание 'C' действительно только одними? И в матрице 'k * z' есть несколько повторяющихся элементов, которые вы могли бы пропустить вычисление' tan' of, но с учетом значений 'K' и' n_Z' там не так много денег. –

ответ

6

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

AFAIK, касательная функция имеет фактическое значение dedicated hardware для вычисления его значения. Кроме того, Matlab автоматически распределяет триггерные функции больших матриц по нескольким ядрам, поэтому там практически нечего улучшать.

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

Теперь, если вы сравните эти:

tic; 
for ii = 1:10 
    my_sum = C * tan(k * z); 
end 
toc 

tic; 
for ii = 1:10 
    my_sum_notan = C * k * z; 
end 
toc 

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

Во всех вы должны задать себе следующие вопросы:

  1. вы готовы отказаться от полной двойной точности, или, скажем, 6 цифр «достаточно близко»?

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

  3. Учитывая все вышесказанное, требуется ли количество усилий, необходимых для оптимизации этого, в большей степени перевешивать более продолжительное время работы? 250 секунд звучит не так уж плохо по сравнению с написанием пользовательских, едва переносимых функций MEX, которые реализуют триггерные функции с искаженным временем.

+0

Хороший способ показать преступника здесь (загар)! Вы получаете от меня один голос :). Вы уверены, что MATLAB автоматически выполнит многопоточные операции? Даже без параллельного набора инструментов? – zeFrenchy

+0

@ DominiqueJacquel Это была новая функция в R2007a (см. Здесь [здесь] (http://www.mathworks.nl/support/solutions/en/data/1-4PG4AN/?solution=1-4PG4AN)). Вы можете увидеть это в диспетчере задач/сверху, если вы выполняете вычисления в бесконечном цикле; вы увидите, что MATLAB использует большинство/всех ядер. –

+0

@Rody Oldenhuis Спасибо за ваш полный ответ. Иногда я думаю о вычислении задач в чистом Fortran/C или использовании GotoBLAS/OpenBLAS, они быстрее (во время выполнения), но разработка такого кода происходит медленнее. – N0rbert

3

Делай, как много матричных операций, как это возможно авансовый (транспозиции в данном случае), чтобы сэкономить время в цикле

K = 200; 
n_z = 40000; 
C = ones(K,1)'; 
k = (0:K-1)'; 
z = linspace(0, 2*pi, n_z); 

tic; 
    my_sum_for = zeros(1, K); 
    for i=1:n_z 
     my_sum_for(i) = C * tan(k * z(i)); 
    end 
toc 

tic; 
    my_sum = C * tan(k * z); 
toc; 

мои времена выполнения, прежде чем

Elapsed time is 1.266158 seconds. 
Elapsed time is 0.531173 seconds. 

и после

Elapsed time is 0.496803 seconds. 
Elapsed time is 0.185396 seconds. 
+0

спасибо. Это действительно быстрее. – N0rbert

3

Вот версия, которая работает немного быстрее на моем компьютере:

k = repmat((0:K-1)', 1, n_z); 
z = repmat(linspace(0, 2*pi, n_z), K, 1); 
C = ones(1, K); 
tic 
my_sum = C*tan(k.*z); 
toc 

По существу, вместо того, чтобы внешний продукт к и г я работать непосредственно на матрицах.

Первая версия

Elapsed time is 0.652923 seconds. 
Elapsed time is 0.240300 seconds. 

После ответа Доминик Jacquel в

Elapsed time is 0.376218 seconds. 
Elapsed time is 0.214047 seconds. 

Моя версия

Elapsed time is 0.168535 seconds. 

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

Я полностью согласен с Rody Oldenhuis. Большая часть работы лежит в касательной функции. Я могу рассказать вам больше. k. * z очень эффективен и не может быть улучшен. Если вы подсчитаете пропускную способность памяти, он будет иметь около 10 ГБ/с на моем компьютере. Пик, который я могу получить, составляет около 16 ГБ/с, поэтому он близок. Здесь не так много возможностей. То же самое с C * T. Это простое преобразование матриц-векторов BLAS2, ограниченное памятью. Для системных размеров вы показываете накладные расходы MATLAB не слишком велики.

Редактировать: как упоминал Родий, новые версии MATLAB уже распараллеливают tan(). Так что здесь не так много.

Вы можете только надеяться улучшить tan() - возможно, запустив его параллельно. В конце концов, это тривиально параллелизуемая задача ... Рассмотрите возможность экспорта именно этого файла MEX, который будет использовать OpenMP. Очень простая работа, много ускорений, если у вас есть несколько запасных ядер.

+0

Спасибо. Что интересно, новые версии MATLAB (проверенные на 2008b) не поддерживают флаг 'mcc -x':' Ошибка: -x больше не поддерживается. Компилятор MATLAB больше не генерирует файлы MEX, потому что для этого больше нет преимуществ по производительности: MATLAB JIT по умолчанию ускоряет M-файлы. Чтобы скрыть запатентованные алгоритмы, используйте функцию PCODE. ' Может быть, я попытаюсь вызвать внешнюю самодельную __tan __- функцию. – N0rbert

+1

Я хотел написать собственную функцию MEX и скомпилировать ее с помощью mex. Таким образом, вы можете пропустить весь бизнес repmat/k * z. Это только поглощает полосу пропускания памяти и влияет на время выполнения. k * z - операция BLAS2. Вам не нужно явно создавать матрицу. Вы можете вычислить его записи на лету. – angainor

+0

Спасибо, я попробую. – N0rbert