`eval` 与 `str2func` 相比从字符串评估函数的性能

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

eval and str2func 都能够计算由字符串 f.e 表示的函数。 f='a^x+exp(b)+sin(c*x)+d':

两种方法中哪种方法的性能最好?

备注

请注意,此基准测试的灵感来自

请注意,我知道使用 evalstr2func 通常是不好的做法[1][2](如评论中所述)。

简答:使用str2func

基准

基准测试将针对 N 个不同的 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(没有函数句柄): 结果表明,即使对函数求值一次,它的速度也快了大约 50%使用 str2func 而不是 eval(没有函数句柄)。对于大量评估,str2func 可能快 100 倍左右(取决于您评估的函数)。

str2func vs eval(带函数句柄): evalstr2func 慢 100% 左右单个评估,但对于大量评估来说变得几乎同样快(eval 慢 ~5%)。

eval 带和不带函数句柄: 请注意,对于单个评估,使用 eval 创建函数句柄比评估它慢 ~50%直接。

结论: str2func总是比eval快。

在以下基准测试中,创建了一个函数句柄集合,并将 eval 的性能与 str2func 的性能进行比较以评估字符串。这些函数是变量与 +- 符号的组合。

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

八度结果:

两种方法没有区别。