MATLAB文档中有避免不必要的数据副本一节,其中可以找到以下语句:
抄写 如果函数不修改输入参数,MATLAB就不会复制输入变量中包含的值。
在这种情况下,没有关于varargin的任何消息。我试图找出一个能够监视内存使用情况的函数,但没有成功。所以我想问的是:复制写功能与varargin一起工作吗?
假设函数function Y = f(x,y,z)
相对于函数function Y = f(varargin)
。在第一种情况下,函数调用f(a,b,c)
不会复制a
、b
和c
(不管变量的类型如何)。在第二种情况下,函数调用f(a,b,c)
的行为并不清楚。MATLAB会指出varargin{1}
到a
,varargin{2}
到b
,varargin{3}
到c
而不显式创建单元数组,还是varargin
是a
、b
和c
的显式连接(因此内存将在单元数组中存储这三个变量的副本)?
发布于 2021-01-12 21:50:46
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编译器可能会进行更多的优化,并且它可能会就地创建变量,因此根本不会创建单元格数组。另一种可能的优化可能与标量这样的小对象有关。它们是廉价的对象,可以廉价复制,而且可能没有参考计数。
发布于 2021-01-14 17:44:29
这是一个涉及更多的问题,这似乎在一开始。部分原因是MATLAB文档中没有完整的文档,还有一部分是因为多年来幕后的共享机制发生了变化。首先,我将简要描述MATLAB变量是什么。然后我将描述MATLAB使用的各种共享机制。最后,我将描述这些共享机制是如何在MATLAB幕后使用的。
MATLAB变量:
MATLAB变量基本上是一个名为mxArray的C结构,它包含各种字段,用于保存大小、类、存储类和数据指针等信息。这个C结构的地址通常称为变量的“结构地址”,数据指针通常称为"Pr“、" Pi”、"Ir“、"Jc”等。对于MATLAB的后期版本,复杂的数据被交织在一起,没有Pi指针。对于内部数字、逻辑和char类,数据直接存在于Pr和Pi数据指针(以及用于稀疏变量索引的Ir和Jc指针)后面。对于OOP类mex类变量,在实际数据存在的数据指针背后有一种专有结构,用户无法直接访问该结构(限制OOP类mex变量在mex例程中的有用性的根本缺陷IMO )。
变量共享:
MATLAB以下列方式共享变量:
深拷贝:问题中的变量不是与任何其他变量共享任何内容。
共享数据副本:多个变量可以有不同的结构地址,但具有相同的数据指针。例如,这通常是直接的全变量赋值或完全变量的整形所产生的结果。过去,mxArray (CrossRef)中有一个字段,它是所有这些变量链接列表的一部分。稍后版本的MATLAB只有一个计数器来告诉您有多少变量是列表的一部分,但是列表本身不再是用户可以访问的。
引用副本:多个变量可以具有完全相同的结构地址。mxArray (refcount)中的字段指示有多少变量共享相同的结构地址。这是通常用于单元格或结构变量元素的内容。
父副本:它本身并不是一个副本,但是在嵌套的结构和单元格数组中,由于上游共享,变量最终可能与变量的其他部分或其他变量共享。在mxArray本身中没有这方面的迹象。也就是说,CrossRef和refcount看起来是不共享的,但是共享实际上正在发生。
句柄复制:如果OOP类then变量是从句柄派生出来的,那么多个变量基本上是共享的。在mxArray本身中不会出现这种情况,并且这些变量不遵循正常的“复制即写”或“延迟复制”规则。
什么时候使用共享?
这是它变得粘稠的地方。这些规则没有公布,而且这些年来发生了变化。我能做的就是举几个例子:
-共享数据副本示例
A = B; % direct whole variable assignment (earlier versions of MATLAB)
A{1} = B; % assigning from workspace into cell or struct (earlier versions of MATLAB)
A = reshape(B,whatever); % reshape of full variable
B{1} % cell or struct element in expression or assignment
fun(B); % function arguments are passed as shared data copies of original
A = typecast(B,'whatever'); % later versions of MATLAB only. Early versions did deep copy.
-参考副本
A = B; % direct whole variable assignment (later versions of MATLAB)
A{1} = B{1}; % assignment among cell or struct elements
A = 1:5; % literal assignment of small variable can result in background reference copy
-家长副本示例
A.x = 5; B = A; % A.x is sharing with B.x through the parent A and B sharing.
原来的问题是:
非Mex函数参数通过某种类型的复制机制传递到函数中。无论是文字变量还是varargin,通常都使用共享数据副本(无论是显式参数还是构建varargin单元数组的结果)。我看到的唯一例外是,有时嵌套函数可以传递一个标量变量的深度副本,而不是共享的数据副本。因此,“写上复制”或“延迟复制”机制既适用于函数内部的文字参数,也适用于varargin参数,因为在这两种情况下,您实际上都在处理函数中原始数据的共享数据副本(或者在稍后版本的MATLAB中可能是引用副本)。要注意的是,如果在函数调用中使用特殊的语法,则可以让MATLAB解析器认识到您正在尝试修改变量“就地”,并避免深拷贝,否则就会发生。
Mex函数参数略有不同。旧版本的MATLAB总是用于传递原始的可变结构地址,但较晚版本的MATLAB使用与非Mex函数相同的规则并传递共享数据副本(尽管标量可能作为深度副本传递)。
因此,函数中的“复制即写”或“延迟复制”机制实际上并没有什么特别之处。传入原始变量的共享数据副本或引用副本。因此,如果不对其进行任何更改,则函数内部将不会进行深度复制。如果您确实更改了参数变量的元素,那么将首先进行深度复制(即未共享)。但这是在MATLAB的任何级别上发生的行为.如果您更改了共享变量的元素,则必须首先进行深度复制。无论你是否在一个函数中,应用的规则都是相同的.如果该变量是共享的,并且您更改了一个元素,那么将首先进行深度复制。
发布于 2021-01-13 07:44:49
作为@rahnema1 1,MATLAB的复制写机制(也称为延迟复制)适用于每个副本,而不仅仅是函数参数。
演示这一点的一种方法是使用从Yair的无文件的MATLAB博客修改的以下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
请注意,单元格数组只不过是一个数组,其数据类型为“数组”,因此可以包含任何类型的数组作为其元素。基本上,它是指向其他数组的指针数组。
https://stackoverflow.com/questions/65690681
复制相似问题