如何“复制”矩阵而不在内存中创建导致内存溢出的临时矩阵?
Posted
技术标签:
【中文标题】如何“复制”矩阵而不在内存中创建导致内存溢出的临时矩阵?【英文标题】:How to 'copy' matrix without creating a temporary matrix in memory that caused memory overflow? 【发布时间】:2020-04-09 02:31:40 【问题描述】:通过将矩阵分配到更大的分配内存中,matlab 在“复制”它时会以某种方式复制它,如果要复制的矩阵足够大,就会出现内存溢出。这是示例代码:
main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
parfor i=1:n
slice_matrix(:,:,i)=gather(gpuArray(rand(500,500)));
end
main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end
有什么方法可以在没有开销的情况下将slice_matrix
“粉碎”到main_mat
上?提前致谢。
编辑:
在预先分配main_mat
时发生溢出。如果main_mat
用main_mat=zeros(500,500,1);
(较小的大小)初始化,则不会发生溢出,但会减慢,因为在分配矩阵之前没有完成分配。随着k
范围的增加,这将显着降低性能。
【问题讨论】:
至于你的循环:it's recommended to set the outer loop to aparfor
loop for optimisation purposes。此外,parfor
将您的数据复制到每个单独的工作人员,因此假设有 4 个工作人员,它会将您的数据在 RAM 中复制四次。
你有什么迹象表明Matlab实际上是在复制内存?你在使用memory
函数吗?任务管理器? Matlab的内存错误?它发生在哪一行代码?
如您所见,我在代码中注释的位置,main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)
是发生内存溢出问题的位置。当我预先分配main_mat
时验证过,它会溢出,如果我不这样做,它就不会。 Matlab 将返回“内存不足错误”。
您的 500x500x2000 矩阵是否适合内存?它约为 4 Gb。请参阅***.com/q/51987892/7328782,了解为什么只有在写入数组时才会发生内存不足错误。
为了更好地理解您的问题,您能否在main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;
之前插入h=h+slice_matrix(end)
(并将h 初始化为0)?我怀疑这条新添加的行已经导致您的内存问题。
【参考方案1】:
您可以使用以下代码。你实际上不需要 slice_matrix
main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
parfor i=1:n
main_mat(:,:,1+(k-1)*n + i - 1) = gather(gpuArray(rand(500,500)));
end
%% now you don't need this main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end
【讨论】:
你不能在 parfor 循环中这样做 你试过了吗? 我将它移出 parfoor 循环是有原因的。我没有尝试完全相同的代码,但我知道它不会因为索引而工作。【参考方案2】:如果我理解正确,您的主要问题是parfor
不允许共享内存。将每个 parfor 工作人员视为几乎一个单独的 matlab 实例。
我知道基本上只有一种解决方法(我从未尝试过),即 Fileexchange 上的“共享矩阵”:https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix
更多解决方案:正如其他人建议的那样:删除 parfor 肯定是一种解决方案,获得更多内存,使用 tall 数组(当内存满时使用硬盘驱动器,read here),将操作划分为更小的块,最后但并非最不重要的一点,考虑Matlab 以外的替代方法。
【讨论】:
【参考方案3】:主要问题是数字比零占用更多空间。
main_mat=zeros(500,500,2000);
占用很少的 RAM 而 main_mat = rand(500,500,2000);
占用很多,无论您使用 GPU 还是 parfor(事实上,parfor 会让您使用更多 RAM)。所以这不是不自然的记忆膨胀。按照下面丹尼尔的链接,零的分配似乎只创建指向内存的指针,并且只有当您将矩阵用于“数字”时才会填充物理内存。这是由操作系统管理的。它适用于 Windows、Mac 和 Linux,您可以使用 Matlab 或其他语言(如 C)来完成。
【讨论】:
现在我不再了解 MATLAB。一旦我输入带有zeros
的命令,整个虚拟内存就被实际分配了,但没有使用任何内存。 whos
显示两个矩阵的大小相同,而我的操作系统显示不同的内存消耗。我删除了我的评论,因为你的回答绝对没有错。
我找到了一些解释:***.com/questions/51987892/…
很好的答案!谢谢。
@Gregor:我想确认一下,尝试使用ones
而不是zeros
,这样可以确保在调用相应函数时实际分配了内存。
当我理解一切正确时,结论是:没有临时副本。出现内存不足异常是因为main_mat
被分配了非零值。以前只分配虚拟内存(地址空间),现在分配给物理内存。【参考方案4】:
我假设您的代码只是一个示例代码,rand()
代表您的 MVE 中的自定义。所以对于matlab中的内存使用有一些提示和技巧。
The MathWorks 培训手册中有一个 sn-p:
在 MATLAB 中将一个变量分配给另一个变量时,就像将参数传递给函数时一样,MATLAB 会透明地创建对该变量的引用。仅当代码修改一个或多个值时,MATLAB 才会中断引用并创建该变量的副本。这种行为,称为 copy-on-write 或 lazy-copying,将复制大型数据集的成本推迟到代码修改值之前。因此,如果代码不进行任何修改,就不需要额外的内存空间和执行时间来复制变量。
要做的第一件事是检查代码的(内存)效率。即使是优秀程序员的代码也可以通过(一点)脑力进一步优化。这里有一些关于内存效率的提示
利用 matlab 的原生矢量化,例如sum(X,2)
, mean(X,2)
, std(X,[],2)
确保 matlab 不必扩展矩阵(隐式扩展最近已更改)。使用bsxfun
可能更有效
使用就地操作,例如x = 2*x+3
而不是 x = 2*x+3
...
请注意,关于内存使用的优化与您希望减少计算时间不同。因此,您可能需要考虑减少工作人员的数量或避免使用parfor
-loop。 (由于parfor
无法使用共享内存,因此使用 Parallel Toolbox 没有 copy-on-write 功能。
如果您想更深入地了解您的记忆,哪些是可用的以及哪些可以被 Matlab 使用,请查看 feature('memstats')
。对您来说有趣的是 虚拟内存,即
与整个 MATLAB 进程相关的总内存和可用内存。它受处理器架构和操作系统的限制。 或者使用这个命令
[user,sys] = memory
。
快速侧节点:Matlab 将矩阵一致地存储在内存中。您需要为大型矩阵提供大量可用 RAM。这也是为什么要分配变量的原因,因为动态更改它们会强制 Matlab 将整个矩阵复制到 RAM 中的一个更大的位置,每次它超出当前位置。
如果您确实有内存问题,您可能只想深入研究数据类型的艺术——这在低级语言中是必需的。例如。您可以从一开始就直接使用单精度 main_mat=zeros(500,500,2000,'single');
将内存使用量减少一半——顺便说一句,这也适用于 rand(...,'single')
和更多本机函数——尽管一些更复杂的 matlab 函数需要输入类型双倍,你可以再次向上。
【讨论】:
【参考方案5】:删除parfor
可能会解决您的问题。
parfor
在那里没有用。 MATLAB 的parfor
不使用共享内存并行(即它不启动新线程),而是使用分布式内存并行(它启动新进程)。它旨在将工作分配到一组或工作节点上。虽然它也可以在一个节点(或单个台式计算机)内工作以将工作分配到多个内核上,但这并不是在一个节点内进行并行处理的最佳方式。
这意味着parfor
启动的每个进程都需要有自己的slice_matrix
副本,这就是你的程序占用大量内存的原因。
请参阅 MATLAB 文档中的 "Decide When to Use parfor
" 以了解有关 parfor
的更多信息以及何时使用它。
【讨论】:
删除parfor
是唯一的方法?当我这样设计时,处理效果最好,因为parfor
中的所有内容都是 CPU 和 GPU 密集型的,因此它显着提高了性能。
@GregorIsack:我使用了您的示例代码,不知道您实际上在parfor
中做了很多工作。如果是这样,那么是的,它可能很有用。 -- 如果slice_matrix
不是gpuarray
,它可能不会被复制到作业中。
嗯,即使slice_matrix
不是gpuArray
,我仍然会出现溢出症状。我会让这个问题打开,让我们看看是否有任何替代解决方案。不过感谢您的回答!以上是关于如何“复制”矩阵而不在内存中创建导致内存溢出的临时矩阵?的主要内容,如果未能解决你的问题,请参考以下文章