Низкая производительность при использовании анонимных функций в MATLAB. Заметили ли это другие?

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

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

В результате я получаю 0 секунд для прямого, почти 0 секунд для подфункции и 5 секунд для анонимных функций. Я запускаю MATLAB 7.7 (R2007b) на OS X 10.6 на C2D 1,8 ГГц.

Может ли кто-нибудь запустить код и посмотреть, что получится? Меня особенно интересует производительность в Windows.

function [] = speedtest()


clear all; close all;

function y = foo(x)
    y = zeros(1,length(x));
    for j=1:N
        y(j) = x(j)^2;
    end
end

x = linspace(-100,100,100000);
N = length(x);


%% direct
t = cputime;

y = zeros(1,N);
for i=1:N
    y(i) = x(i)^2;
end

r1 = cputime - t;

%% using subfunction
t = cputime;
y = foo(x);
r2 = cputime - t;

%% using anon function
fn = @(x) x^2;

t = cputime;

y = zeros(1,N);
for i=1:N
    y(i) = fn(x(i));
end

r3 = cputime-t;

[r1 r2 r3]

end

person Grav    schedule 04.11.2009    source источник
comment
В чем именно заключается ваш вопрос ??   -  person Amro    schedule 04.11.2009
comment
Мне интересно, почему вы все равно делаете такие чрезмерно сложные вычисления ... вы можете заменить цикл for, используя векторные операции. Например, y = x.^2; возведет в квадрат каждый элемент x и сохранит полученный вектор в y.   -  person gnovice    schedule 04.11.2009
comment
К вашему сведению: аналогичный вопрос о производительности отправки метода ООП: stackoverflow.com/questions/1693429/   -  person Andrew Janke    schedule 17.11.2009
comment
Как продолжение комментария midtiby, лучший подход к синхронизации кода MATLAB - использовать timeit из File Exchange. Он решает такие проблемы, как стоимость запуска при первом запуске, и вычисляет среднее время выполнения в стационарном состоянии как минимум для 10 запусков. Это намного надежнее, чем простая пара крестиков / током. mathworks.com/matlabcentral/fileexchange/   -  person Arthur Ward    schedule 14.10.2010


Ответы (4)


Вы обманываете вложенную функцию. :) Анонимная функция вызывается внутри цикла, поэтому вы измеряете стоимость ее вызова 100 000 раз. Вложенная функция вызывается только один раз, поэтому накладные расходы на ее вызовы незначительны. Чтобы сравнить стоимость вызова анонимных и именованных функций, вы должны сделать так, чтобы вложенная функция выполняла ту же работу, что и анонимная функция, а затем также вызывала ее из цикла.

Я сделал это и получил аналогичные результаты. Анонимная функция примерно в 20 раз медленнее.

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

function [] = speedtest()

function y = foo(x)
    y = x^2;
end

r = struct;

...

%% using nested function through function handle
fn = @foo;
y = zeros(1,N);
t = cputime;
for i=1:N
    y(i) = fn(x(i));
end
r.nested_handle = cputime - t;

...

%% using subfunction through function handle
fn = @subfunction_foo;
y = zeros(1,N);
t = cputime;
for i=1:N
    y(i) = fn(x(i));
end
r.subfunction_handle = cputime - t;

...

end % end function speedtest

function y = subfunction_foo(x)
y = x^2;
end

Я получаю это на R2009b в Windows.

>> speedtest
                direct: 0
                nested: 0.0469
         nested_handle: 0.0781
           subfunction: 0.0313
    subfunction_handle: 0.0313
             anonymous: 1.2344

Другой способ взглянуть на это - структурировать свой код таким образом, чтобы он «векторизовался» и работал с массивами, уменьшая количество вызовов функций и стоимость вызова функции не имеет большого значения. Это было бы более идиоматическим Matlab: типичный совет по производительности - игнорировать стоимость вызовов функций и циклов, потому что вам в любом случае следует делать меньше вызовов для более крупных аргументов.

person Andrew Janke    schedule 04.11.2009
comment
Незначительная опечатка: в вашем коде то, что вы называете подфункцией, на самом деле является вашей вложенной функцией, а то, что вы называете своей локальной функцией, на самом деле является подфункцией. - person gnovice; 04.11.2009
comment
Отредактировано для использования правильной терминологии, как указано gnovice; подфункция щас. - person Andrew Janke; 04.11.2009
comment
Да, ты прав - я обманываю. Но не специально ;-) В настоящее время я привыкаю к ​​векторизации своего кода, например. избегая циклов for и вместо этого выполняя такие вещи, как A (A ‹0) = 0. Но я не вижу, как использование анонимных функций соотносится с идиомами Matlab - может быть, мне просто следует избегать их использования? - person Grav; 05.11.2009
comment
@Grav: Не идиоматична не анонимность; это небольшой кусок работы. Я просто имел в виду, что векторизованный код, который приводит к гораздо меньшему количеству вызовов функций, каждый из которых работает с большими массивами входных данных, является идиоматическим Matlab. Похоже, вы на правильном пути. Продолжайте и используйте аноны, если они удобны; просто сделайте их векторизованными, как @ (x) x. ^ 2, и тогда снижение производительности не будет такой большой проблемой. - person Andrew Janke; 05.11.2009

Могу подтвердить ваши выводы Grav. На моем компьютере функция speedtest возвращает следующее.

>> speedtest()
ans =
         0    0.0313    1.3906

Кстати, функция cputime - не лучший метод измерения времени вычислений. Вместо этого используйте функции tic и toc. см. ссылку Эти функции обеспечивают гораздо более высокое разрешение по времени, и их использование Получаю следующее.

>> speedtest()
ans =
         0.0062    0.0162    1.3495
person midtiby    schedule 04.11.2009

Результаты с машины Windows, Matlab 2009a

>> test

ans =

     0    0.0156    1.1094
person High Performance Mark    schedule 04.11.2009

Я столкнулся с той же проблемой, что и Гэри, просто подумал, что было бы хорошо проверить ответ Эндрю в более поздней версии Matlab (2014a) (Mac). Сначала результаты:

direct: 0.0722
anonymous: 0.3916
subfunction: 0.2277

И код, который я использовал:

function []=SpeedTest()

fanon = @(x,y) x*x+y*y;

iter=1000000;
x=1:iter;
y=1:iter;
var1=nan(size(x));
var2=nan(size(x));
var3=nan(size(x));
timefor=struct('direct',nan,'anonymous',nan','subfunction',nan);

tic;
for i=1:iter
    var1(i)=x(i)*x(i)+y(i)*y(i);
end
timefor.direct=toc;

tic;
for i=1:iter
    var2(i)=fanon(x(i),y(i));
end
timefor.anonymous=toc;

tic;
for i=1:iter
    var3(i)=fsub(x(i),y(i));
end
timefor.subfunction=toc;

display(timefor);
end

function [z]=fsub(x,y)
z=x*x+y*y;
end
person Rohan    schedule 20.07.2014