在MATLAB中将大型数组直接写入磁盘时,是否需要预先分配?

时间:2014-10-01 11:11:29

标签: matlab performance file-io dynamic-arrays large-data

我需要编写一个太大的数组,无法将内存放入.mat二进制文件中。这可以通过matfile函数来完成,该函数允许随机访问磁盘上的.mat文件。

通常,接受的建议是预分配数组,因为在循环的每次迭代中扩展它们都很慢。但是,当我asking how to do this时,我发现在写入磁盘而不是RAM时,这可能不是一个好建议。

增长阵列适用的性能是否相同,如果那么, 显着< / em> 什么时候与写入磁盘的时间相比呢?

(假设整个文件将写入一个会话,因此严重文件碎片的风险为 。)

4 个答案:

答案 0 :(得分:7)

问:阵列增长会产生相同的性能影响,如果是这样,那么与写入磁盘所需的时间相比是否会很重要?

答:是的,如果您在没有预先分配的情况下显着增加磁盘上的文件,性能将受到影响。性能受到影响将是碎片化的结果。正如您所提到的,如果文件是在一个会话中编写的,则碎片风险较小,但如果文件显着增长则会导致问题。

MathWorks网站上提出related question,接受的答案是尽可能预先分配。

如果您没有预先分配,那么您的效果问题的程度将取决于:

  • 您的文件系统(数据如何存储在磁盘上,群集大小),
  • 您的硬件(硬盘寻道时间或SSD访问时间),
  • mat文件的大小(是否移动到非连续空间),
  • 以及存储的当前状态(现有碎片/可用空间)。

让我们假装您正在运行最近的Windows操作系统,因此使用NTFS file-system。让我们进一步假设它已经设置了默认的4 kB簇大小。因此,磁盘上的空间将在4 kB chunks中分配,并且这些位置将被索引到主文件表。如果文件增长且连续空间不可用,则只有两个选择:

  1. 将整个文件重写到磁盘的新部分,其中有足够的可用空间。
  2. 将文件分段,将其他数据存储在磁盘上的其他物理位置。
  3. 文件系统选择执行最不好的选项#2,并更新MFT记录以指示新群集在磁盘上的位置。

    Illustration of fragmented file on NTFS, from WindowsITPro

    现在,硬盘需要物理移动读头以便读取或写入新簇,这是一个(相对)慢的过程。在移动头部,并等待盘的正确区域在其下旋转......你可能正在寻找about 10ms的寻道时间。因此,每次敲击碎片时,HDD都会有额外的10ms延迟,以便访问新数据。 SSD具有更短的寻道时间(没有移动部件)。为简单起见,我们忽略了多盘系统和RAID阵列!

    如果您在不同时间继续增长文件,那么您可能会遇到很多碎片。这实际上取决于文件的增长时间/数量,以及您使用硬盘的方式。您遇到的性能影响还取决于您阅读文件的频率以及您遇到片段的频率。

    MATLAB将数据存储在Column-major order中,并且根据评论,您似乎对在数据集上执行逐列操作(总和,平均值)感兴趣。如果列在磁盘上变得不连续,那么您将在每次操作时击中大量碎片!

    如评论中所述,读取和写入操作都将通过缓冲区执行。正如@ user3666197指出的那样,操作系统可以推测性地预先读取磁盘上的当前数据,因为您可能希望接下来需要这些数据。如果硬盘有时处于空闲状态 - 保持其以最大容量运行并且在缓冲存储器中处理小部分数据可以极大地提高读写性能,则此行为尤其有用。但是,从你的问题来看,听起来好像你想对一个巨大的(太大的内存).mat文件执行大型操作。鉴于您的用例,硬盘将以容量无论如何工作,并且数据文件太大而无法容纳缓冲区 - 因此这些特殊技巧无法解决您的问题

    所以......是的,你应该预先分配。是的,在磁盘上增加阵列的性能会受到影响。是的,它可能会很重要(这取决于具体的增长量,碎片等)。如果你真的 进入HPC的精神状态,那就停止你正在做的事情,抛弃MATLAB,破坏你的数据并尝试类似Apache Spark的东西!但这是另一个故事。

    这会回答你的问题吗?

    P.S。更正/修改欢迎!我是在POSIX inode上长大的,如果这里有任何不准确之处,我真诚地道歉......

答案 1 :(得分:2)

在RAM中预先分配变量并在磁盘上预分配不能解决同样的问题。

在RAM中

为了在RAM中扩展矩阵,MATLAB创建一个具有新大小的新矩阵,并将旧矩阵的值复制到新矩阵中并删除旧矩阵。这会花费很多性能。

如果您预先分配了矩阵,则其大小不会改变。所以没有理由让MATLAB再次进行矩阵复制。

在硬盘上

GnomeDePlume说,硬盘上的问题是碎片。即使文件是在一个会话中编写的,碎片仍然是一个问题。

原因如下:硬盘通常会有点碎片化。想象一下

  • #是硬盘上已满的内存块
  • M是硬盘上用于保存矩阵数据的内存块
  • -是硬盘上的空闲内存块

现在,在将矩阵写入其中之前,硬盘看起来像这样:

###--##----#--#---#--------------------##-#---------#---#----#------

当您编写矩阵的某些部分(例如MMM块)时,您可以想象这个过程看起来像这样&gt;!(我举一个例子,文件系统将从左到右依次使用第一个足够大的可用空间 - 真正的文件系统是不同的):

  1. 第一个矩阵部分:
    ###--##MMM-#--#---#--------------------##-#---------#---#----#------
  2. 第二个矩阵部分: ###--##MMM-#--#MMM#--------------------##-#---------#---#----#------
  3. 第三个矩阵部分: ###--##MMM-#--#MMM#MMM-----------------##-#---------#---#----#------
  4. 依旧......
  5. 显然,硬盘上的矩阵文件是碎片化的,尽管我们在此期间没有做任何其他事情的情况下编写它。

    如果矩阵文件已预先分配,这可能会更好。换句话说,我们告诉文件系统我们的文件有多大,或者在这个例子中,我们要为它保留多少内存块。

    想象一下矩阵需要12个块:MMMMMMMMMMMM。我们通过预先分配告诉文件系统我们需要这么多,它将尽可能地满足我们的需求。在这个例子中,我们很幸运:有一个带有&gt; = 12个内存块的可用空间。

    1. 预分配(我们需要12个内存块):
      ###--##----#--#---# (------------) --------##-#---------#---#----#------
      文件系统保留矩阵的括号之间的空格,并写入那里。
    2. 第一个矩阵部分:
      ###--##----#--#---# (MMM---------) --------##-#---------#---#----#------
    3. 第二个矩阵部分:
      ###--##----#--#---# (MMMMMM------) --------##-#---------#---#----#------
    4. 第三个矩阵部分:
      ###--##----#--#---# (MMMMMMMMM---) --------##-#---------#---#----#------
    5. 矩阵的第四部分和最后部分:
      ###--##----#--#---# (MMMMMMMMMMMM) --------##-#---------#---#----#------
    6. Voilá,没有碎片!


      类比

      一般来说,您可以将此过程想象为购买大型团体的电影票。你想团结在一起,但是剧院里已经有一些座位由其他人保留。为了使收银员能够满足您的要求(大型团队想要团结在一起),他/她需要了解您的团队有多大(预分配)。

答案 2 :(得分:2)

对整个讨论的快速回答(如果您没有时间关注或技术理解):

  • Matlab中的预分配与RAM中的操作相关。 Matlab不提供对I / O操作的低级访问,因此我们不能谈论在磁盘上预先分配内容。
  • 当向磁盘写入大量数据时,已经发现写入次数越少,执行任务越快,磁盘碎片越小。

因此,如果你不能一次性写,将写入分成大块

答案 3 :(得分:-3)

序言

此答案基于作者在最近一周提供的原始帖子和澄清(两者)。

不良效果的问题(s)引入的低级别,依赖于物理媒体,&#34;碎片&#34; ,由文件系统&amp;文件访问层在TimeDOMAIN幅度和这些的这些的这些的ComputingDOMAIN重复性中进一步面对

最后提出了针对特定任务的最新,主要最快的解决方案,以便最大限度地减少浪费的努力和错误解释错误造成的损害从理想化或其他无效假设,类似于严重文件碎片的风险很低&#34;由于一个假设,整个文件将在一个会话中写入(在当前O / S的许多多核/多进程操作中实时过度,主要是不可能在当代COTS文件系统中,TB大小的BLOB文件对象的创建时间和一系列广泛的修改(参考MATLAB大小限制)。


有人可能会讨厌事实,但事实仍然存在,直到更快和更快。

更好的方法

首先,在考虑性能之前,要实现概念中的差距

  1. 真正的性能不利影响是HDD-IO 造成或与文件碎片有关

  2. 对于 .mat 文件

  3. 的半永久性存储,RAM 不是替代
  4. 其他操作系统限制和干预 +额外的驱动程序和基于硬件的抽象被忽略了对不可避免的开销的假设
  5. 上述计算方案从对最终效果
  6. 的最大影响/影响的评论中省略

    <强>假设:

    • 整个处理只是运行一次,没有优化/迭代,没有连续处理

    • 数据 1E6 double浮动值 x 1E5 columns =关于 0.8 TB (+ HDF5开销)

    • 尽管有原始帖子,但没有随机IO 与处理相关

    • 数据获取阶段与.NET通信,以便将DataELEMENT s 接收到MATLAB

      这意味着,自v7.4起,

      1.6 GB limit MATLAB WorkSpace ,32位胜利(2.7 GB,3GB开关)

      1.1 GB limit MATLAB最大矩阵,wXP / 1.4 GB wV / 1.5 GB

      有点&#34;发布&#34;关于32位Linux操作系统中最大矩阵的MATLAB WorkSpace + 2.3 GB限制2.6 GB limit

      拥有64位O / S无助于任何类型的32位MATLAB 7.4实现,而将无法工作,因为另一个限制,即阵列中的最大单元数,这不会覆盖1E12在这里要求。

      唯一的机会是两者

    • 数据存储阶段假定行排序数据块(行排序数据块的集合)的块写入到HDD设备上的MAT-file < / p>

    • 数据处理阶段假定在获取所有输入并将所有输入编组到基于文件的关闭后,重新处理HDD设备上的MAT-file中的数据-RAM存储,但按列顺序排列

    • 只需逐列mean() - s / max() - 需要计算(没有更复杂的

    <强>事实:

    • MATLAB使用&#34;限制&#34;为二进制文件实现 HDF5 文件结构。

    Review performance measurements on real-data & real-hardware ( HDD + SSD ) to get feeling of scales of the un-avoidable weaknesses thereof

    分层数据格式(HDF)于20年前诞生于1987年的国家超级计算应用中心( NCSA )。是的,那个老了。目标是开发一种文件格式,结合 灵活性 效率 来处理极大的数据集。不知何故HDF文件没有在主流中使用,因为只有少数几个行业确实能够真正利用它的可怕的容量或者根本不需要它们。

    灵活性意味着文件结构带来一些开销,如果阵列内容没有变化,则无需使用(您支付成本而不消耗任何好处使用它)和一个假设,HDF5限制数据的整体大小,它可以包含一些帮助和保存MATLAB方面的问题是不正确的。

    MAT-files原则上是好的,因为它们可以避免将整个文件加载到RAM中以便能够使用它。

    然而,MAT-files并不能很好地完成这里定义和澄清的简单任务。尝试这样做会导致性能不佳和HDD-IO文件碎片(在 write-through -s期间添加几十毫秒,而在上添加的内容少于几十毫秒在计算过程中read-ahead -s对判断整体业绩不佳的核心原因毫无帮助。


    专业解决方案

    而不是将整个巨大的 1E12 DataELEMENT集合移动到MATLAB内存代理数据阵列中,而不是将其安排在下一个即将到来的序列流中。 HDF5 / MAT-file HDD设备IO-s(write-through和O / S与硬件 - 设备链冲突/次优化read-ahead s)以便让所有的巨大工作&#34;只是[已婚]准备好&#34;为了几个&amp;简单地调用 mean() / max() MATLAB函数(将尽力修改每个 1E12 DataELEMENT只是另一个订单(甚至两次 - 是 - 另一个马戏团,在第一次工作处理噩梦一路下来之后,通过所有HDD-IO瓶颈)重新进入MATLAB in-RAM-objects,重新设计这一步从一开始就进入管道BigDATA处理。

    while true                                          % ref. comment Simon W Oct 1 at 11:29
       [ isStillProcessingDotNET,   ...                 %      a FLAG from .NET reader function
         aDotNET_RowOfVALUEs ...                        %      a ROW  from .NET reader function
         ] = GetDataFromDotNET( aDtPT )                 %                  .NET reader
       if ( isStillProcessingDotNET )                   % Yes, more rows are still to come ...
          aRowCOUNT = aRowCOUNT + 1;                    %      keep .INC for aRowCOUNT ( mean() )
          for i = 1:size( aDotNET_RowOfVALUEs )(2)      %      stepping across each column
             aValue     = aDotNET_RowOfVALUEs(i);       %      
             anIncrementalSumInCOLUMN(i) = ...
             anIncrementalSumInCOLUMN(i) + aValue;      %      keep .SUM for each column ( mean() )
             if ( aMaxInCOLUMN(i) < aValue )            %      retest for a "max.update()"
                  aMaxInCOLUMN(i) = aValue;             %      .STO a just found "new" max
             end
          endfor
          continue                                      %      force re-loop
       else
          break
       endif
    end
    %-------------------------------------------------------------------------------------------
    % FINALLY:
    % all results are pre-calculated right at the end of .NET reading phase:
    %
    % -------------------------------
    % BILL OF ALL COMPUTATIONAL COSTS ( for given scales of 1E5 columns x 1E6 rows ):
    % -------------------------------
    % HDD.IO:          **ZERO**
    % IN-RAM STORAGE:
    %                  Attr Name                       Size                     Bytes  Class
    %                  ==== ====                       ====                     =====  =====
    %                       aMaxInCOLUMNs              1x100000                800000  double
    %                       anIncrementalSumInCOLUMNs  1x100000                800000  double
    %                       aRowCOUNT                  1x1                          8  double
    %
    % DATA PROCESSING:
    %
    % 1.000.000x .NET row-oriented reads ( same for both the OP and this, smarter BigDATA approach )
    %         1x   INT   in aRowCOUNT,                 %%       1E6 .INC-s
    %   100.000x FLOATs  in aMaxInCOLUMN[]             %% 1E5 * 1E6 .CMP-s
    %   100.000x FLOATs  in anIncrementalSumInCOLUMN[] %% 1E5 * 1E6 .ADD-s
    % -----------------
    % about 15 sec per COLUMN of 1E6 rows
    % -----------------
    %                  --> mean()s are anIncrementalSumInCOLUMN./aRowCOUNT
    %-------------------------------------------------------------------------------------------
    % PIPE-LINE-d processing takes in TimeDOMAIN "nothing" more than the .NET-reader process
    %-------------------------------------------------------------------------------------------
    

    您的管道 d BigDATA计算策略将以智能方式主要避免 MATLAB中的临时存储缓冲,因为它将逐步计算结果不超过< strong> 3 x 1E6 ADD / CMP寄存器,均采用静态布局,避免代理存储到HDF5 / MAT-file绝对避免所有与HDD-IO相关的瓶颈和低BigDATA持续读取&#39;速度(完全没有谈到临时/ BigDATA持续写入......)并且也会避免表现不佳的内存映射用于计算mean-s和max-es。


    后记

    在太阳下,管道处理并不是什么新鲜事。

    它重复使用速度导向的HPC解决方案已经使用了几十年

    [BigDATA标签之前的几代人已经发明了#34;#34;在营销部门。 ]

    忘掉数以万计的HDD-IO阻塞操作&amp;进入流水线分布式流程到流程解决方案。


    没有比这更快的了


    如果,所有外汇业务和HFT对冲基金怪兽都已经在那里......