内存使用Apply vs Map.虚拟内存使用和锁定

Sjo*_*ies 9 memory-management wolfram-mathematica

我需要在一长串对中找到所有数字对的总和.在Mathematica中有很多方法可以做到这一点,但我正考虑使用其中任何一个PlusTotal.由于Total在列表上工作,Map是在那里使用的函数式编程工具,Apply在级别1(@@@)是要使用的Plus,因为Plus要将数字作为参数添加.

这是一些演示代码(警告:在执行此操作之前保存所有工作!):

pairs = Tuples[Range[6000], {2}]; (* toy example *)

TimeConstrained[Plus @@@ pairs; // Timing, 30]

(* Out[4]= {21.73, Null} *)

Total /@ pairs; // Timing

(* Out[5]= {3.525, Null} *)
Run Code Online (Sandbox Code Playgroud)

您可能已经注意到我已添加TimeConstrained到代码中Plus.这是我为你提供的保护措施,因为裸代码使我的PC几乎瘫痪.事实上,上面的代码对我有用,但如果我将第一行的范围增加到7000,我的计算机就会锁定并且永远不会回来.什么都行不通,没有alt-period,程序切换,ctrl-alt-delete,尝试使用任务栏启动进程管理器,关闭笔记本电脑盖让它睡觉等等,真的没什么.

问题是由Plus @@@ pairs线路的极端内存使用引起的.虽然'对'本身占用大约288 MB,而总数的一半,但Plus系列的计算速度很快消耗约7 GB.这是我的免费物理内存的结束,任何更大的内容都会导致在磁盘上使用虚拟内存.当使用虚拟内存时,Mathematica和/或Windows显然效果不佳(BTW,MacOS和Linux表现更好吗?).相反,Total行对内存使用情况图没有明显影响.

我有两个问题:

  1. 鉴于文档中所述的等价Plus和(Total[Total]列表相当于Apply [Plus,list].)如何解释行为的极端差异?我认为这与之间的差异做ApplyMap,但我很好奇,所涉及的内部机制.
  2. 我知道我可以通过使用来限制命令的内存占用MemoryConstrained,但是如果您怀疑Mathematica可能会占用您的所有系统资源,那么必须在任何地方使用它是一件痛苦的事.是否有一个全局设置可以告诉Mathematica仅为其所有操作使用物理内存(或者,最好是其中的某一部分)?这将是非常有帮助的,因为这种行为导致了过去几周的一些锁定,并且它真的开始惹恼我.

Jos*_*ell 9

Plus@@@pairs 打开包装:

In[11]:= On["Packing"]
In[12]:= pairs=Tuples[Range[6000],{2}];
In[13]:= TimeConstrained[Plus@@@pairs;//Timing,30]
During evaluation of In[13]:= Developer`FromPackedArray::punpack1: Unpacking array with dimensions {36000000,2}. >>
Out[13]= $Aborted
Run Code Online (Sandbox Code Playgroud)

这将做同样的事情,不解包,这意味着它使用更少的内存.

On["Packing"]
pairs=Tuples[Range[6000],{2}];
a = pairs[[All, 1]];b=pairs[[All, 2]];
Plus[a, b];
Run Code Online (Sandbox Code Playgroud)

您可以在此处阅读有关Mathematica包装的更多信息:http: //www.wolfram.com/technology/guide/PackedArrays/

  • 但是Total/@对也解包了,所以它不能成为速度和内存消耗差异的原因(我也立即考虑打包,因为这种差异通常发生在那里,但显然还有其他事情发生) (2认同)

Leo*_*rin 7

我只是想补充一些可能会更清楚地澄清情况的观察结果.正如@Joshua的回答中所指出的那样(参见本文对类似讨论的评论),效率低下的原因与解包有关.我的猜测是,为什么一般的原因Apply解压是编译器(Compile)有一个非常有限的支持Apply-即只能使用3头- List,PlusTimes.出于这个原因,在中SystemOptions["CompileOptions"],我们可以看到编译长度Apply设置为无穷大 - 一般来说,甚至尝试自动编译都没有意义Apply.然后可能,当编译长度大于实际数组维度时,它会解压缩.当我们将其设置"ApplyCompileLength"为有限长度时,行为确实会发生变化:

On["Packing"]
pairs=Tuples[Range[2000],{2}];
SetSystemOptions["CompileOptions"->"ApplyCompileLength"->100];
TimeConstrained[Plus@@@pairs;//Timing,30]

{0.594,Null}
Run Code Online (Sandbox Code Playgroud)

再次更改它会恢复观察到的初始行为:

In[34]:= 
SetSystemOptions["CompileOptions" -> "ApplyCompileLength" -> Infinity];
TimeConstrained[Plus @@@ pairs; // Timing, 30]

During evaluation of In[34]:= Developer`FromPackedArray::punpack1: Unpacking 
array with dimensions  {4000000,2}. >>

Out[35]= {2.094, Null}
Run Code Online (Sandbox Code Playgroud)

关于你的第二个问题:或许,约束内存的系统方法与@Alexey Popkov所做的一致,就是使用主内核来控制内存不足时重启的从内核.我可以提供一个远不那么复杂但可能仍然有用的黑客.以下功能

ClearAll[totalMemoryConstrained];
SetAttributes[totalMemoryConstrained, HoldRest];
Module[{memException},
  totalMemoryConstrained[max_, body_, failexpr_] :=
   Catch[MemoryConstrained[body,
     Evaluate[
       If[# < 0, Throw[failexpr, memException], #] &@(max -
         MemoryInUse[])], failexpr], memException]]; 
Run Code Online (Sandbox Code Playgroud)

将尝试约束内核使用的总内存,而不仅仅是在给定的特定计算中.因此,您可以尝试将其包装在顶级函数调用周围,只需执行一次.因为它依赖于MemoryConstrainedMemoryInUse,这只是因为他们是那么好.有关如何使用它的更多详细信息,请参阅 Mathgroup文章.您可以使用$Pre它自动将此应用程序应用于您的输入,并减少样板代码的数量.