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

时间:2011-06-19 21:48:41

标签: wolfram-mathematica memory-management

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

以下是一些演示代码(警告:在执行此操作之前保存所有工作!):<​​/ p>

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} *)

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

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

我有两个问题:

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

3 个答案:

答案 0 :(得分: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

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

On["Packing"]
pairs=Tuples[Range[6000],{2}];
a = pairs[[All, 1]];b=pairs[[All, 2]];
Plus[a, b];

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

答案 1 :(得分:7)

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

"ApplyCompileLength"

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

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

{0.594,Null}

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

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}

将尝试约束内核使用的总内存,而不仅仅是在给定的特定计算中。因此,您可以尝试将其包装在顶级函数调用周围,只需执行一次。由于它依赖于ClearAll[totalMemoryConstrained]; SetAttributes[totalMemoryConstrained, HoldRest]; Module[{memException}, totalMemoryConstrained[max_, body_, failexpr_] := Catch[MemoryConstrained[body, Evaluate[ If[# < 0, Throw[failexpr, memException], #] &@(max - MemoryInUse[])], failexpr], memException]]; MemoryConstrained,因此它只是与它们一样好。有关如何使用的更多详细信息,请参阅this Mathgroup帖子。您可以使用MemoryInUse自动将此应用程序应用于您的输入,并减少样板代码的数量。

答案 2 :(得分:4)

问题的第二部分对于 Mathematica 用户来说非常实际。我已经在官方新闻组中询问了相关问题并获得了以下answer from John Fultz

  

2011年3月10日星期四06:12:04 -0500   (EST),Alexey Popkov写道:

     
    

我宁愿拥有而不是MemoryConstrained     'FreeMemoryConstrained'     防止安全交换...

  
     

这不是现代经营的方式   系统工作。所有记忆都是虚拟的   记忆。是否由RAM支持,   磁盘,或其他一些存储介质   操作系统的一个细节   管理,而不是应用程序(使用   机制之类的例外   内存映射文件)。如果是   应用程序确实有能力   将其内存锁定到RAM中,就可以了   对其他人来说真的很不友好   系统上的应用程序。

     

你真的想要一款应用吗?   坚持保留2千兆字节的RAM   在为自己发挥(或十   应用程序可以保持200   每个兆字节),即使是   应用程序没有碰巧做   现在和其他任何计算   应用程序完全缺乏内存?   这可能会导致完全失败   操作系统本身就是这样   比交换更糟糕。

     

现代操作系统根本无法做到   允许应用以这种方式运行。   如果他们这样做,那么而不是交换   地狱,你会最终做到例行公事   整个运营的失败   系统本身。

     

此致

     

John Fultz

尽管如此,我已经实现了一个函数,该函数每秒检查大约100次空闲物理内存量,并且其体积减少到某个用户定义的阈值以下,重新启动slave内核并在新的从属MathKernel进程用户中执行 - 定义的命令。

此功能依赖于NETLink,目前仅适用于32位Windows系统。它不是非常昂贵,并且不需要相当多的额外处理器时间,因为它通过调用kernel32.dll的GlobalMemoryStatusEx函数来获取与内存相关的信息,这非常快。