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

Wolfram mathematica 条件、块、模块-哪种方式的内存和计算效率最高?,wolfram-mathematica,Wolfram Mathematica,在Mathematica中,做同一件事总有几种方法。例如,在为我最近的问题调整花环的解决方案时,我: 但是,我们可以对块执行相同的操作: ClearAll[ff]; SetAttributes[ff, HoldAllComplete]; ff[expr_] := Block[{done}, Internal`WithLocalSettings[Null, done = f[expr], AbortProtect[If[! ValueQ[done], Print["Interru

在Mathematica中,做同一件事总有几种方法。例如,在为我最近的问题调整花环的解决方案时,我:

但是,我们可以对
执行相同的操作:

ClearAll[ff];
SetAttributes[ff, HoldAllComplete];
ff[expr_] := 
 Block[{done}, 
  Internal`WithLocalSettings[Null, done = f[expr], 
   AbortProtect[If[! ValueQ[done], Print["Interrupt!"]]]]]
或使用
模块

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

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

模块和块都非常有效,因此,只有当您对其变量进行本地化的函数体所做的工作很少时,才会注意到它们所导致的开销。造成开销的主要原因有两个:作用域构造开销(作用域构造必须分析它们所包含的代码,以解决可能的名称冲突和绑定变量-这在
模块
中都会发生),以及在符号表中创建和销毁新符号的开销(仅适用于
模块
)。因此,
稍微快一点。要了解快多少,您可以做一个简单的实验:

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} 
从这些计时中,我们可以看到
的速度大约是
模块
的两倍,但是在最后一个函数中使用
模块
创建的持久变量仅一次的版本,其效率大约是
的两倍,并且几乎与简单函数调用一样快(因为持久变量只创建一次,并且在应用函数时没有范围开销)

对于真正的函数,在大多数情况下,
Module
Block
的开销应该无关紧要,因此我会使用更安全的方法(通常,
Module
)。如果这真的很重要,一个选择是只使用一次由模块创建的持久局部变量。如果即使这种开销很大,我也会重新考虑设计-因为从那时起,显然您的函数做得太少了。有些情况下,
Block
更有益,例如,当您希望确保本地变量使用的所有内存变量将被自动释放(这与具有
下值的局部变量特别相关,因为它们在由
模块创建时并不总是被垃圾收集)。使用
Block
的另一个原因是,当您预期可能出现异常或中止等中断,并希望本地变量自动重置时(Block
确实如此)。但是,使用
Block
会有名称冲突的风险,因为它会动态绑定变量,而不是按词汇绑定变量

因此,总结一下:在大多数情况下,我的建议是:如果您觉得您的函数内存严重不足或运行时效率低下,请查看其他地方——范围界定结构成为主要瓶颈的情况非常罕见。例外情况包括未垃圾收集的
模块
变量和累积数据,非常轻量级的函数会影响我们由于函数体非常高效,并且使用绕过主求值器的快速函数,因此符号作用域开销可能与函数处理其数据所需的时间相当,因此在非常高效的低级结构(如压缩数组和稀疏数组)上操作的函数

编辑

通过以建议的方式组合
模块


您可以两全其美:一个与
一样快的函数,一个与使用
模块
一样安全的函数。非常清楚的定时比较,谢谢!只需注意:从内存泄漏和内存碎片的角度来比较这些函数的内存效率也是非常有趣的由
模块
诱发的,与周期
设置
-
取消设置
设置
-
清除
设置
-
删除
相比,此时我不知道该怎么做(可能需要收集一些统计数据,以便对一些特殊设计的代码进行多次评估),但我认为这样的比较会非常有趣和有价值。@Alexey我同意关于内存消耗/碎片的数据会非常有用。但是,正如您所指出的,这是一项艰巨的任务,因为内存分配的细节不会向用户公开。而且,因为内存管理可以被视为一种实现方法所有(从用户的角度来看),我们无法确定我们收集的任何数据在未来版本中是否真实,因为实现确实会发生变化。只有在Mathematica的未来版本能够提供更好的用户级内存管理控制时,这种情况才会发生变化(我对此表示怀疑)@Alexey说,我天真的期望是,在内存消耗/碎片方面,
模块
可以与
Set-Remove
相媲美,而
Block
-可以
设置为Clear
。我不认为会有显著的区别,因为我同时看到
模块
在某种意义上,它位于具有
设置
取消设置
删除
、和
清除
的系统的“顶部”。嗨,Leonid,回答得不错。在接近开始的部分可能有一个更正:与模块不同,Block不对其参数进行任何词法分析(因为不需要符号重命名)。在某些情况下,这可能是一个重要的差异。若要查看差异,请构造一个词法分析成本较高但计算成本较低的表达式:scop
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} 
Module[{xl}, fmbp[x_] := Block[{xl = x}, xl]]