为什么Mathematica代码中的纯函数更快?

时间:2011-06-13 16:40:01

标签: wolfram-mathematica

  

可能重复:
  Performance difference between functions and pattern matching in Mathematica

我经常在这里发布的很多答案中发现大量使用纯函数,而且这些解决方案通常比使用命名模式快得多。为什么会这样?为什么纯函数比其他函数更快?是否与mma口译人员的工作量有关?

4 个答案:

答案 0 :(得分:8)

首先,让我们考虑一些示例基准:

In[100]:= f[x_]:=x^2;

In[102]:= Do[#^2&[i],{i,300000}]//Timing
Out[102]= {0.406,Null}

In[103]:= Do[f[i],{i,300000}]//Timing
Out[103]= {0.484,Null}

In[104]:= Do[Function[x,x^2][i],{i,300000}]//Timing
Out[104]= {0.578,Null}

由于两个原因,纯函数通常(更快)更快。首先,匿名纯函数(使用插槽定义的函数 - #&)不需要解析变量名称的名称冲突。因此,它们比模式定义的更快,其中发生了一些名称冲突解决。但是你会发现带有命名变量的纯函数实际上比模式定义的更慢,而不是更快。我可以推测这是因为他们还必须解决他们体内可能发生的冲突,而基于规则的冲突则忽略了这些冲突。在任何情况下,速度差异大约为10-20%。

另一个更为戏剧性的区别在于它们用于Map,Scan,Table等功能,因为后者在大型数字(打包)列表上自动编译。但是,虽然通常可以编译纯函数,但模式定义的函数基本上不能,因此它们无法访问这种速度增益。例如:

In[117]:= ff[x_] := Sqrt[x];

In[116]:= Map[Sqrt[#] &, N@Range[100000]]; // Timing

Out[116]= {0.015, Null}

In[114]:= Map[ff, N@Range[100000]]; // Timing

Out[114]= {0.094, Null}

答案 1 :(得分:0)

纯功能有几个优点: - 可以缓存结果。 - 计算可以安全地进行分析。 - 在某些情况下,结果可以在编译时计算(CTFE),并且函数最后不会执行。 - 由于未修改外部作用域,因此无需通过复制传递所有参数。

因此,如果编译器能够相对于这些函数管理优化,那么您的程序将更快。无论是什么语言。

答案 2 :(得分:0)

实际上,模式匹配似乎通常比Function[{u},...]构造更快,并且与#& - 类型构造一样快(忽略编译的可能性,这在mma 8中变得更加令人兴奋)。

要看到这个定义一个函数来计算短代码的时间:

SetAttributes[timeshort, HoldAll];
timeshort[expr_] := Module[{t = Timing[expr;][[1]], tries = 1},
    While[t < 1.,
        tries *= 2;
        t = Timing[Do[expr, {tries}];][[1]]];
    Return[t/tries]]

然后试试这个:

ClearAll[f]; f[x_] := Cos[x]
Trace[f[5.]]
f[5] // timeshort

ClearAll[f]; f = Function[x, Cos[x]]
Trace[f[5.]]
f[5] // timeshort

ClearAll[f]; f = Cos[#] &
Trace[f[5.]]
f[5] // timeshort

给出

{f[5.],Cos[5.],0.283662}
8.45641\[Times]10^-7
Function[x,Cos[x]]
{{f,Function[x,Cos[x]]},Function[x,Cos[x]][5.],Cos[5.],0.283662}
1.51906\[Times]10^-6
Cos[#1]&
{{f,Cos[#1]&},(Cos[#1]&)[5.],Cos[5.],0.283662}
8.04602\[Times]10^-7

即,模式匹配和#&Function快。我不明白为什么。

编辑:猜猜我应该检查一下belisarius之前提出的问题...请参阅here与我给出的答案基本相同,并阅读评论以进行一些有趣的讨论。

答案 3 :(得分:-1)

是。这意味着它永远不必复制很多东西,因为纯函数不能改变它。列表处理纯粹可以进行一定量的复制,但通常将功能算法安排得高效。一旦编译完东西,命令式代码将快速(几乎总是),但对于解释的Mathematica字节码,纯粹通常很快。