Performance of `eval` compared to `str2func` to evalulate a function from a string
Asked Answered
C

2

4

eval and str2func are both able to evaluate a function represented by a string, f.e. f='a^x+exp(b)+sin(c*x)+d':

  • Using eval:

    y = eval(f)
    

    or (suggested by rahnema1)

    fHandle = eval(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
  • Using str2func:

    fHandle = str2func(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    

Which of both methods has the best performance?

Remarks

Note that this benchmark is inspired on this question.

Note that I'm aware that using eval and str2func is often bad practice[1][2] (as mentioned in the comments).

Cowlick answered 12/9, 2017 at 15:9 Comment(8)
Good idea, but please mention in your answer how eval is undesirable for reasons more fundamental than performanceFrias
In which way is eval undesirable compared to str2func except for performance?Cowlick
@Cowlick In my answer to the question which inspired your benchmark, I linked to the documentation on why eval is undesirable. Namely, it's hard to debug, it can unexpectedly overwrite workspace data, it isn't compiled for future runs (that's the performance aspect) and strings which are created by concatenation as inputs to eval can be hard to read. Can usually be avoided, so should beDinesen
@Dinesen I think those arguments are also - to some extent - applicable to str2func, especially the last two items.Cowlick
@Cowlick See link in my first commentFrias
@Luis Mendo Loren writes there that str2func is an alternative to eval, but I didn't understand why, what cause it be different? Why in that function Matlab can process and access variables rather than in eval?Eckardt
@Eckardt As I see it, eval and str2func do different thingsFrias
I asked a new question as I'm unsure which fundamental reasons do only apply to eval and not to str2func.Cowlick
C
6

Short answer: use str2func.

Benchmark

The benchmark will evaluate the function for N different values of x.

f='a^x+exp(b)+sin(c*x)+d';

Ns = linspace(1, 1000, 20);
timeEval = zeros(size(Ns));
timeEvalHandle = zeros(size(Ns));
timeStr2func = zeros(size(Ns));
for i=1:length(Ns)
  N = Ns(i);
  timeEval(i) = timeit(@() useEval(f, N));
  timeEvalHandle(i) = timeit(@() useEvalHandle(f, N));
  timeStr2func(i) = timeit(@() useStr2func(f, N));
end

figure
plot(Ns, timeEval, 'DisplayName', 'time eval');
hold on
plot(Ns, timeEvalHandle, 'DisplayName', 'time eval');
hold on
plot(Ns, timeStr2func, 'DisplayName', 'time str2func');
legend show
xlabel('N');

figure
plot(Ns, timeEval./timeStr2func, 'DisplayName', 'time_{eval}/time_{str2func}');
hold on
plot(Ns, timeEvalHandle./timeStr2func, 'DisplayName', 'time_{eval handle}/time_{str2func}');
legend show
xlabel('N');

figure
plot(Ns, timeEvalHandle./timeStr2func);
ylabel('time_{eval handle}/time_{str2func}')
xlabel('N');

function y = useEval(f, N)
  a = 1; b = 2; c = 3; d = 4;
  for x=1:N
    y = eval(f);
  end
end

function y = useEvalHandle(f, N)
  a = 1; b = 2; c = 3; d = 4;
  fHandle = eval(['@(x, a, b, c, d) ' f]);
  for x=1:N
    y = fHandle(x, a, b, c, d);
  end
end

function y = useStr2func(f, N)
  a = 1; b = 2; c = 3; d = 4;
  fHandle = str2func(['@(x, a, b, c, d) ' f]);
  for x=1:N
    y = fHandle(x, a, b, c, d);
  end
end

enter image description here enter image description here enter image description here

str2func vs eval (without function handle): The results show that even for evaluating the function once, it is around 50% faster to use str2func than eval (without function handle). For a large number of evaluations, str2func may be around 100x faster (depending on the function you are evaluating).

str2func vs eval (with function handle): eval is around 100% slower than str2func for a single evaluation, but becomes are almost equally fast for a large number of evaluations (eval is ~5% slower).

eval with and without function handle: Note that for a single evaluation creating a function handle with eval is ~50% slower than evaluating it directly.

Conclusion: str2func is always faster than eval.

Cowlick answered 12/9, 2017 at 15:9 Comment(0)
S
1

In the following benchmark a collection of function handles is created and performance of eval is compare to that of str2func to evaluate strings. The functions are combination of variables with +- signs.

n = 16;
op=char((dec2bin(0:2^n-1)-48)*2+43);
vars= 'a':'z';
v = vars(1:n+1);
s(1:2^n,1:2:2*n+1)=repmat(v,2^n,1);
s(:,2:2:end)=op;

h=repmat(['@(' sprintf('%c,',v(1:end-1)) v(end) ')'],2^n,1);
farray=[h,s];
tic
for k = 1:2^n
    f = eval(farray(k,:));
end
toc

tic
for k = 1:2^n
    f = str2func(farray(k,:));
end
toc

Result in Octave:

There is no difference between two methods.

Sanderlin answered 14/9, 2017 at 10:19 Comment(1)
Matlab result: Elapsed time is 62.920203 seconds. Elapsed time is 60.706978 seconds.Cowlick

© 2022 - 2024 — McMap. All rights reserved.