Performance of `eval` compared to `str2func` to ev

2019-02-18 10:28发布

问题:

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).

回答1:

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

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.



回答2:

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.