条件,块,模块 - 哪种方式的内存和计算效率最高?

时间:2011-09-29 11:09:29

标签: wolfram-mathematica

Mathematica 中总有几种方法可以做同样的事情。例如,在为我最近的问题调整WReach的解决方案时used Condition

ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] /; (Unset[done]; True) := 
 Internal`WithLocalSettings[Null, done = f[expr], 
  AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]; Unset[done]]]

但是,我们可以使用Block

执行相同的操作
ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] := 
 Block[{done}, 
  Internal`WithLocalSettings[Null, done = f[expr], 
   AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]]]]

Module

ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] := 
 Module[{done}, 
  Internal`WithLocalSettings[Null, done = f[expr], 
   AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]]]]

可能还有其他几种方法可以做到这一点。从内存和CPU使用的角度来看,哪种方式最有效(f可能会返回非常大的数据数组 - 但可能会返回非常小的数据?)

1 个答案:

答案 0 :(得分:17)

ModuleBlock都非常有效,因此当它们本地化的变量的函数体很少时,由它们引起的开销只是显而易见的。开销有两个主要原因:范围构造开销(范围构造必须分析它们所包含的代码以解决可能的名称冲突和绑定变量 - 这适用于ModuleBlock),以及在符号表中创建和销毁新符号的开销(仅适用于Module)。出于这个原因,Block有点快。要了解速度有多快,您可以进行简单的实验:

In[14]:= 
Clear[f,fm,fb,fmp]; 
f[x_]:=x;
fm[x_]:=Module[{xl = x},xl];
fb[x_]:=Block[{xl = x},xl];
Module[{xl},fmp[x_]:= xl=x]

我们在这里定义了4个函数,尽可能使用最简单的函数 - 只返回参数,可能分配给局部变量。我们可以预期这种效果在这里最为明显,因为身体的作用非常小。

In[19]:= f/@Range[100000];//Timing
Out[19]= {0.063,Null}

In[20]:= fm/@Range[100000];//Timing
Out[20]= {0.343,Null}

In[21]:= fb/@Range[100000];//Timing
Out[21]= {0.172,Null}

In[22]:= fmp/@Range[100000];//Timing
Out[22]= {0.109,Null} 

从这些时间开始,我们看到BlockModule快两倍,但是在最后一个函数中使用由Module创建的持久变量的版本只有一次,效率比Block高两倍,几乎和简单函数调用一样快(因为持久变量只创建一次,并且在应用函数时没有作用域开销)。

对于实际函数,大多数时候,ModuleBlock的开销都无关紧要,所以我会使用更安全的东西(通常是Module)。如果它确实重要,一个选项是使用Module只创建一次的持久局部变量。如果即使这个开销很重要,我也会重新考虑设计 - 从那时起你的功能显然太少了。有些情况下Block更有利,例如当你想确定所用的所有内存时局部变量将自动释放(这与DownValues的局部变量特别相关,因为它们在Module创建时并不总是垃圾收集。使用Block的另一个原因是当您预期可能出现例外或中止等中断时,并希望自动重置局部变量(Block会这样做)。但是,使用Block会冒名称冲突的风险,因为它会动态绑定变量而不是词法。

因此,总结一下:在大多数情况下,我的建议是这样的:如果您认为您的函数具有严重的内存或运行时效率低下,请查看其他地方 - 将范围构造作为主要瓶颈非常罕见。例外情况不包括垃圾收集的Module变量数据,非常频繁使用的轻量级函数,以及在非常有效的低级结构(如压缩数组和稀疏数组)上运行的函数,其中符号作用域开销可能与功能处理数据所需的时间相当,因为主体非常有效并且使用绕过主评估器的快速功能。

修改

以建议的方式here合并BlockModule

Module[{xl}, fmbp[x_] := Block[{xl = x}, xl]]

你可以充分利用这两个方面:一个与Block一样快的函数 - 范围一个,并且与使用Module的一个一样安全。