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

Ale*_*kov 13 wolfram-mathematica

Mathematica中总有几种方法可以做同样的事情.例如,当我使用Condition WReach的解决方案来解决我最近遇到的问题时:

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

但是,我们可以做同样的事情Block:

ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] := 
 Block[{done}, 
  Internal`WithLocalSettings[Null, done = f[expr], 
   AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]]]]
Run Code Online (Sandbox Code Playgroud)

或者Module:

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

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

Leo*_*rin 18

这两个ModuleBlock是相当有效的,因此由它们引起的开销仅为noticable当其变量您本地化几乎不做一个函数体.还有的开销主要有两个原因:作用域结构开销(作用域结构必须分析它们包围,以解决可能名称冲突的代码,并绑定变量-这发生两个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]
Run Code Online (Sandbox Code Playgroud)

我们在这里定义了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} 
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑

通过组合BlockModule这里建议的方式:

Module[{xl}, fmbp[x_] := Block[{xl = x}, xl]]
Run Code Online (Sandbox Code Playgroud)

你可以拥有两全其美的功能:一个快速的功能Block- 一个范围的功能和使用它的安全Module.

  • 嗨Leonid,很好的回答.对于开头附近的部分可能有一个修正:与模块不同,Block不会对其参数进行任何词法分析(因为没有必要的符号重命名).在某些情况下,这可能是一个重要的区别.要查看差异,请构造一个表达式,其中词法分析很昂贵但评估很便宜:scoped =使用[{large = ConstantArray [x,10 ^ 7]},保持[{x = 5},如果[True,x ,大]]]; 现在评估并缓存所有内容:Timing [scoped;].然后将Timing [Module @@ scoped]与Timing [Block @@ scoped]进行比较. (2认同)