why wrapping with anonymous function speeds up?

I try to understand the rational behind this result. When I wrap a function within an anonymmous function, it runs almost 10 times faster (R2022a, Windows, my PC, slightly less on remote server) !
What's going on ?
testfunctioncall()
time with direct 1000000 calls of "add" = 0.289378 [s] time with 1000000 calls of "add" wrapped in anonymous = 0.044015 [s]
function testfunctioncall
nruns = 1e6;
a = 1;
b = 2;
%%
fun = @add;
tic;
for k=1:nruns
c = fun(a, b);
end
t1=toc;
% time with direct 1000000 calls of "add" = 0.729264 [s]
fprintf('time with direct %d calls of "add" = %f [s]\n', nruns, t1);
%%
fun = @(a,b) add(a,b);
tic;
for k=1:nruns
c = fun(a, b);
end
t2=toc;
% time with 1000000 calls of "add" wrapped in anonymous = 0.076203 [s]
fprintf('time with %d calls of "add" wrapped in anonymous = %f [s]\n', nruns, t2);
end % testfunctioncall
%% subfunction
function c = add(a, b)
c = a + b;
end

4 Comments

That is odd.
I added a bit more cases to see if I could explain it, but I was not able to come up with a reason.
One hypothesis was that perhaps the constants being passed in was allowing some optimization, so I feed in different inputs each time, but that did not make any substantial difference.
The "retry" is there because sometimes timing is a bit odd the first time you do something in MATLAB; sometimes you need to run a second time to get the "sustainable" timing. You can see here that the retry was faster, but not by orders of magnitude like the anonymous function is.
I have seen in the past that using anonymous functions is often considerably slower than @ of a function directly by name. I am not sure what is happening in this case.
testfunctioncall()
time with direct 1000000 calls of "add" = 0.296775 [s] time with 1000000 calls of "add" wrapped in anonymous = 0.047990 [s] time with 1000000 calls of "plus" in @ = 0.361643 [s] time with 1000000 calls of "plus" direct = 0.006166 [s] time with 1000000 direct + calls = 0.006163 [s] retry time with 1000000 calls of "add" wrapped in anonymous = 0.040836 [s]
function testfunctioncall
nruns = 1e6;
a = rand(nruns,1);
b = rand(nruns,1);
%%
fun = @add;
start = tic;
for k=1:nruns
c = fun(a(k), b(k));
end
t1=toc(start);
% time with direct 1000000 calls of "add" = 0.729264 [s]
fprintf('time with direct %d calls of "add" = %f [s]\n', nruns, t1);
%%
fun = @(a,b) add(a,b);
start = tic;
for k=1:nruns
c = fun(a(k), b(k));
end
t2=toc(start);
% time with 1000000 calls of "add" wrapped in anonymous = 0.076203 [s]
fprintf('time with %d calls of "add" wrapped in anonymous = %f [s]\n', nruns, t2);
fun = @plus;
start = tic;
for k=1:nruns
c = fun(a(k), b(k));
end
t3=toc(start);
fprintf('time with %d calls of "plus" in @ = %f [s]\n', nruns, t3);
start = tic;
for k=1:nruns
c = plus(a(k), b(k));
end
t4=toc(start);
fprintf('time with %d calls of "plus" direct = %f [s]\n', nruns, t4);
start = tic;
for k=1:nruns
c = a(k) + b(k);
end
t5=toc(start);
fprintf('time with %d direct + calls = %f [s]\n', nruns, t5);
fun = @(a,b) add(a,b);
start = tic;
for k=1:nruns
c = fun(a(k), b(k));
end
t2=toc(start);
% time with 1000000 calls of "add" wrapped in anonymous = 0.076203 [s]
fprintf('retry time with %d calls of "add" wrapped in anonymous = %f [s]\n', nruns, t2);
end % testfunctioncall
%% subfunction
function c = add(a, b)
c = a + b;
end
It looks to me that there is a weakness of MATLAB method/implementation of invoking fun = @add.
In normal use, it doesn't really matter of the function does something intrinsically more costly so the overhead is negligible. But if the some function does something very basic and quick operation it really matter.
A wild stab in the dark: might this have something to do with resolving overloaded functions? Perhaps the anonymous function somehow optimizes selecting the correct function in a way that is not possible with the simple function handle.
Do non-overloaded functions show the same behavior?
change add to myadd, still same behavior.

Sign in to comment.

Answers (1)

Don't know the exact reason, but I guess it shows that "annonymous function handle" is faster than "named function handle". It is like macro vs function, or inline vs function call? I noticed that direct function call in this case is even faster.
function testfunctioncall
nruns = 1e6;
a = 1;
b = 2;
%%
fun = @add;
tic;
for k=1:nruns
c = fun(a, b);
end
t1=toc;
% time with direct 1000000 calls of "add" = 0.729264 [s]
fprintf('time with direct %d function handle calls of "add" = %f [s]\n', nruns, t1);
%%
fun = @(a,b) add(a,b);
tic;
for k=1:nruns
c = fun(a, b);
end
t2=toc;
% time with 1000000 calls of "add" wrapped in anonymous = 0.076203 [s]
fprintf('time with %d calls of "add" wrapped in anonymous = %f [s]\n', nruns, t2);
tic;
for k=1:nruns
c = add(a, b);
end
t3=toc;
% time with 1000000 calls of "add" wrapped in anonymous = 0.076203 [s]
fprintf('time with %d function calls of "add" = %f [s]\n', nruns, t3);
end % testfunctioncall
%% subfunction
function c = add(a, b)
c = a + b;
end

Categories

Products

Release

R2022a

Asked:

on 29 Mar 2022

Commented:

on 30 Mar 2022

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!