如何解决MATLAB 中的 Copy-on-Write 和 varargin
MATLAB 文档的 Avoid Unnecessary Copies of Data 部分包含以下语句:
写时复制
如果函数不修改输入参数,MATLAB 不会复制输入变量中包含的值。
在这种情况下没有关于 varargin 的词。我试图制定一个能够监控内存使用情况的函数,但没有成功。所以我在这里问:写时复制功能是否适用于 varargin?
假设函数 function Y = f(x,y,z)
与函数 function Y = f(varargin)
。在第一种情况下,函数调用 f(a,b,c)
不会复制 a
、b
和 c
(无论变量的类型如何)。在第二种情况下,函数调用 f(a,c)
的行为不清楚。 MATLAB 会在不显式创建元胞数组的情况下将 varargin{1}
指向 a
,将 varargin{2}
指向 b
并将 varargin{3}
指向 c
,还是 {{ 1}} varargin
、a
和 b
的显式串联(因此内存将存储元胞数组内三个变量的副本)?
解决方法
varargin
是一个元胞数组。当您将对象放入元胞数组时,该对象并未真正被复制,但其引用计数会增加:
a = [1 2 3];
b = 5;
c = {4,6};
varargin = {a,b,c};
这里只是增加了 a
、b
和 c
指向的对象的引用计数。当你这样做时:
varargin{1}(2) = 7;
因为它想写入 a
所指向的对象,所以它制作了该数组对象的副本并将新数组的第二个元素设置为 7
。新数组放置在 varargin
的第一个单元格中,并且 a
指向的对象的引用计数减少。但是,MATLAB jit 编译器可能会进行更多优化,并且可能会就地创建变量,因此根本不会创建元胞数组。另一种可能的优化可能与标量等小对象有关。它们是廉价的对象,可以廉价地复制,而且它们可能没有引用计数。
作为 @rahnema1 stated,MATLAB 的写时复制机制(也称为延迟复制)适用于每个副本,而不仅仅是函数参数。
证明这一点的一种方法是使用以下从 Yair's Undocumented MATLAB Blog 修改的 MEX 文件:
#include "mex.h"
#include <cstdint>
void mexFunction( int /*nlhs*/,mxArray* plhs[],int nrhs,mxArray const* prhs[]) {
if (nrhs < 1) mexErrMsgTxt("One input required.");
plhs[0] = mxCreateNumericMatrix(1,1,mxUINT64_CLASS,mxREAL);
std::uint64_t* out = static_cast<std::uint64_t*>(mxGetData(plhs[0]));
out[0] = reinterpret_cast<std::uint64_t>(mxGetData(prhs[0]));
}
您可以将其另存为 getaddr.cpp
并使用 mex getaddr.cpp
进行编译。您现在有了一个函数,可以显示数组的数据存储地址。
例如,如果我们制作一个数组的副本,该副本将具有相同的数据地址。当我们再写入副本时,它的数据地址会发生变化:
>> a=zeros(5);
>> getaddr(a)
ans =
uint64
105553130112928
>> a(1)=1;
>> getaddr(a)
ans =
uint64
105553130112928
>> b=a;
>> getaddr(b)
ans =
uint64
105553130112928
>> b(1)=4;
>> getaddr(b)
ans =
uint64
105553130078944
元胞数组也是如此,这与问题直接相关,因为输入参数收集在元胞数组 varargin
中:
>> a=zeros(5);
>> b=zeros(8);
>> v={a,b};
>> getaddr(a)
ans =
uint64
105553130246144
>> getaddr(v{1})
ans =
uint64
105553130246144
请注意,元胞数组只不过是数据类型为“数组”的数组,因此可以包含任何类型的数组作为其元素。基本上它是一个指向其他数组的指针数组。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。