【问题标题】:Memory-efficient way to truncate large array in Matlab在 Matlab 中截断大数组的内存有效方法
【发布时间】:2013-10-28 11:11:03
【问题描述】:

我在 Matlab 中有一个大(多 GB)数组,我想截断它¹。天真地,我认为截断不需要太多内存,但后来我意识到它可能可以:

>> Z = zeros(628000000, 1, 'single');
>> Z(364000000:end) = [];
Out of memory. Type HELP MEMORY for your options.

除非 Matlab 进行一些巧妙的优化,否则在截断 Z 之前,此代码实际上创建一个数组(双精度型!)364000000:628000000。我不需要这个数组,所以我可以这样做:

>> Z = Z(1:363999999);

在这种情况下,第二个示例有效,并且适合我的目的。但是为什么它会起作用?如果Z(364000000:end) = 0 因中间数组364000000:628000000 所需的内存而失败,那么为什么Z = Z(1:363999999) 不会因为中间数组1:363999999 所需的内存而失败,即更大?当然,我需要这个中间数组,我会很高兴有一个解决方案可以截断我的数组而没有任何中间数组,或者如果 Matlab 优化了特定方法,则失败了。

  • 有什么方法可以在不创建中间索引数组的情况下截断数组?
  • 如果不是,上述任何一种方法是否比另一种更节省内存(似乎不是)?如果是这样,为什么? Matlab 真的在这两个例子中都创建了中间数组吗?

¹原因:我正在处理数据,但不知道要预分配多少。我做了一个有根据的猜测,我经常分配太多。我根据可用内存选择块大小,因为分成更少的块意味着更快的代码。所以我想避免任何不必要的内存使用。另见this post on allocating by chunk

【问题讨论】:

  • 这里有一些好问题。但是,可能不再需要分块分配,因为 it seems likely that MATLAB does this 已经在最近的版本中,尽管来自 MathWorks 的细节很少。
  • @chappjc 啊,有趣。我不知道这已经改变了。但是,仍然存在需要截断大数组的情况。
  • 同意。当我的电脑崩溃时,我只是想用profile('-memory','on'); 描述你的例子!
  • 在没有任何来源的情况下,我很确定 Matlab 会进行那些巧妙的优化,并且您的第二个命令不会生成中间数组。

标签: arrays matlab memory truncate


【解决方案1】:

我在一台具有 24GB RAM 和profile('-memory','on'); 的机器上运行了这两个示例。此分析器选项将显示在每一行代码上分配和释放的内存。这些应该是总额而不是净额。我检查了一个简单的函数,它有 net 0 free 和 alloc,它报告了总金额。但是,似乎没有 .m 代码支持它们的内置命令不会向分析器提供细粒度的内存报告。

我对以下代码进行了几次测试:

% truncTest.m
N = 628000000;
M = 364000000;

clear Z
Z = zeros(N,1,'single');
Z(M:end) = [];
Z(1) % just because

clear Z
Z = zeros(N,1,'single');
Z = Z(1:M);
Z(1)

对于它们的价值,NM 的内存分析结果是:

嗯,就分配和释放的内存而言,这两行看起来是相同的。也许这不是全部真相。

所以,出于好奇,我将 M 减少到 200(只有 200 个!),但没有更改 N,做了 profile clear 并重新运行。分析声明:

有趣的是,Z=Z(1:M); 现在几乎是瞬时的,而Z(M:end)=[]; 快了一点。正如预期的那样,两者都释放了大约 2.4GB 的内存。

最后,如果我们换个方向设置M=600000000;

现在即使Z=Z(1:M); 也很慢,但大约是Z(M:end)=[];两倍

这表明:

  1. Z=Z(1:M); 只是抓取指定的元素,将它们存储在新的缓冲区或临时变量中,释放旧缓冲区并将新的/临时的分配给数组Z。通过增加 M 并单独留下N,我能够让我较弱的 4GB 机器从 2.45 秒到翻动页面文件 5 分钟。对于小型M/N,可能在所有情况下都绝对更喜欢这个选项。
  2. Z(M:end)=[]; 总是重写缓冲区,并且执行时间也随着M 而增加。与Z=Z(1:M); 不同,实际上总是较慢,而且似乎呈指数级增长。
  3. 内存分析不提供有关这些内置操作的细粒度信息,不应被误解为提供了在命令执行过程中释放和分配的总内存,而是净变化。

更新 1:只是为了好玩,我在 M 的值范围内对测试进行了计时:

显然比分析提供更多信息。这两种方法都不是无操作,但Z=Z(1:M); 最快,但对于M/N 1 附近的Z,它几乎可以使用两倍的内存。

更新 2

在 R2008b 之前,32 位 Windows 中提供了一个相对未知的 feature,称为 mtic(和 mtoc)。我仍然把它安装在一台机器上,所以我决定看看这是否能提供更多的洞察力,因为我知道(a)从那时起发生了很大变化,(b)它是 32 位 MATLAB 中使用的完全不同的内存管理器。尽管如此,我还是将测试大小减小到N=128000000; M=101000000; 并查看了一下。首先,feature mtic 代表Z=Z(1:M-1);

>> tic; feature mtic; Z=Z(1:M-1); feature mtoc, toc

ans = 

      TotalAllocated: 808011592
          TotalFreed: 916009628
    LargestAllocated: 403999996
           NumAllocs: 86
            NumFrees: 77
                Peak: 808002024

Elapsed time is 0.951283 seconds.

清理,重新创建Z,反之:

>> tic; feature mtic; Z(M:end) = []; feature mtoc, toc

ans = 

      TotalAllocated: 1428019588
          TotalFreed: 1536018372
    LargestAllocated: 512000000
           NumAllocs: 164
            NumFrees: 157
                Peak: 1320001404

Elapsed time is 4.533953 seconds.

在每个指标(TotalAllocatedTotalFreedNumAllocs 等)中,Z(M:end) = []; 的效率都低于Z=Z(1:M-1);。我希望可以通过检查这些数字的NM 的值来辨别内存中发生了什么,但我们猜测的是旧的 MATLAB

【讨论】:

  • 一项非常有趣的研究。那么,是否有一个 matlab 命令可以有效地取消分配数组的一部分?
  • @Shai - 我想说唯一好的答案涉及mxReallocmexMakeMemoryPersistent 和朋友进行不受支持的就地数组修改,但这是一个更长的答案。我认为这里的测试表明这些 MATLAB 表达式不是很有效,除非在某些情况下。
  • @Shai - 请注意,从内存效率的角度来看,此MATLAB newsreader 线程中确定的快速 MEX 解决方案相当愚蠢,因为它实际上并没有释放任何内存,正如指出的那样格里特所以它必须是更复杂的东西......但我必须认为 MathWorks 可以比我们做得更好。 :)
  • 很有趣,而且我知道我真的应该总是选择Z = Z(1:M-1); 而不是Z(M:end) = [];。但是我遇到的内存问题似乎没有被分析器识别出来,甚至没有被 peak memory 列识别出来,所以我们似乎无法真正回答我的核心问题,或者我们可以吗?还有,为什么分析器中没有显示Z = zeros(N,1,'single');
  • @gerrit - 没错,分析器只对峰值和释放有用,对于包含内置 MALTAB 命令的表达式,它似乎只提供净值。峰值在这里似乎没用。 zeros 行未显示,因为这些行仅是 大部分 已花费的行(请注意“所有其他行”行)。在分析器中向下滚动,您将获得逐行摘要。
猜你喜欢
  • 2018-06-05
  • 1970-01-01
  • 1970-01-01
  • 2015-08-14
  • 1970-01-01
  • 2014-01-26
  • 2014-02-18
  • 1970-01-01
  • 2012-08-04
相关资源
最近更新 更多