Question

In order to refactor my MATLAB code, I thought I'd pass around functions as arguments (what MATLAB calls anonymous functions), inspired by functional programming.

However, it seems performance is hit quite severely. In the examples below, I compare different approaches. (The code snippet is wrapped in a function in order to be able to use subfunctions)

The result I get is 0 seconds for direct, almost 0 seconds using a subfunction, and 5 seconds using anonymous functions. I'm running MATLAB 7.7 (R2007b) on OS X 10.6, on a C2D 1.8 GHz.

Can anyone run the code and see what they get? I'm especially interested in performance on 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
Was it helpful?

Solution

You're cheating with the nested function. :) The anonymous function is being called inside a loop, so you're measuring the cost of calling it 100,000 times. The nested function only gets called once, so its function call overhead is negligible. To compare the cost of calling anonymous vs named functions, you should have the nested function do the same work as the anonymous function and then call it from inside a loop, too.

I did that and still got similar results. The anonymous function is about 20 times slower.

However, you can still use function handles with non-anonymous functions, and that doesn't have the same performance hit as anonymous functions. This works with either nested functions (as with the foo in your original test) or non-nested subfunctions (which don't act as closures and may have less overhead).

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

I get this on R2009b in Windows.

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

Another way to look at it is to structure your code so it's "vectorized" and operates on arrays, reducing the number of function calls and the cost of the function call doesn't matter so much. That would be more idiomatic Matlab: typical performance advice is to ignore the cost of function calls and loops because you ought to be doing fewer calls on larger arguments anyway.

OTHER TIPS

I can confirm your findings Grav. The speedtest function returns the following on my computer.

>> speedtest()
ans =
         0    0.0313    1.3906

As a sidenote, the function cputime is not the best method for measuring computation time. Use the tic and toc functions instead. see link These functions provides a much higher time resolution, and using them I obtain the following.

>> speedtest()
ans =
         0.0062    0.0162    1.3495

Results from a Windows machine, Matlab 2009a

>> test

ans =

     0    0.0156    1.1094

I faced the same issue as Gary, just thought it would be good to check Andrew's answer on a more recent version of Matlab (2014a) (Mac). The results first:

direct: 0.0722
anonymous: 0.3916
subfunction: 0.2277

And the code I used:

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top