matlab - 增量追加: How to avoid performance penalty of struct arrays

标签 matlab

如果必须增量地将数据追加到数组中,则使用基本数据类型的各个向量似乎比结构数组(每条记录一个向量元素)快几个数量级。即使尝试将各个向量收集到一个结构中似乎也会使时间增加一倍。测试是:

N=5e4;

fprintf('\nstruct array (array of structs):\n')
clear x y;
y=struct( 'a',[], 'b',[], 'c',[], 'd',[] );
tic
for iIns = 1 : N
   x.a=rand; x.b=rand; x.c=rand; x.d=rand;
   y(end+1)=x;
end % for iIns
toc

fprintf('\nSeparate arrays of scalars:\n')
clear a b c d;
a=[]; b=[]; c=[]; d=[];
tic
for iIns = 1 : N
   a(end+1) = rand;
   b(end+1) = rand;
   c(end+1) = rand;
   d(end+1) = rand;
end % for iIns
toc

fprintf('\nA struct with arrays of scalars for fields:\n')
clear a b c d x y
x.a=[]; x.b=[]; x.c=[]; x.d=[];
tic
for iIns = 1:N
   x.a(end+1)=rand;
   x.b(end+1)=rand;
   x.c(end+1)=rand;
   x.d(end+1)=rand;
end % for iIns
toc

结果:

struct array (array of structs):
Elapsed time is 24.127274 seconds.

Separate arrays of scalars:
Elapsed time is 0.048190 seconds.

A struct with arrays of scalars for fields:
Elapsed time is 0.084624 seconds.

尽管将基本数据类型的单个向量收集到结构中(上面的第三个场景)会带来这样的惩罚,但它可能比简单地使用单个向量(上面的第二个场景)更可取,因为变量更有组织性。您的变量 namespace 并没有填充这么多实际上在概念上分组的变量。

然而,对于这样的组织来说,这是一个相当大的惩罚。我认为没有办法避免这种情况吗?

最佳答案

有两种方法可以避免这种性能损失:(1) 预分配,(2) 重新考虑您对“组织”变量的立场。我建议两者都选。哦,如果可以的话,不要使用每个字段仅使用标量的结构数组 - 如果您的应用程序突然需要处理几个数量级的数据,内存开销将迫使您重写所有内容。

预分配

您通常知道数组最终将有多少个元素。因此,将数组初始化为 s = struct('a',NaN(1:N),'b',NaN(1:N));如果您事先不知道会有多少条目,但您可以估计上限,使用上限进行初始化,然后删除元素,或使用不关心是否存在的函数(例如 nanmean )该数组有一些额外的 NaN最后。如果您确实对最终大小一无所知(除了 N 将足够大到重要),请预先分配一个好的数字(例如 N=1337 ),并按 block 扩展数组。 MathWorks 在最近的版本中加快了数值数组的动态增长,但正如您在答案中所演示的那样,优化尚未应用于结构。不要指望 MathWorks 的优化团队来修复您的代码。

不错的变量

为什么要担心变量空间?只要你使用explicitVariableNames ,您的代码仍然具有可读性,并且您将可以轻松地选择正确的变量。但是好吧,假设您想要清理:保持事件变量数量较低的第一种方法是使用 clearkeep在代码中的战略点上,以确保只保留需要的内容。第二个(假设您想优化性能)是将上下文链接的向量放入同一数组中:objectDimensions = [lengthOfObject, widthOfObject, heightOfObject] 。这会将所有内容保留为数字数组(速度最快),并允许轻松矢量化,例如 objectVolume = prod(objectDimensions,2);

/aside:我应该透露一下,我曾经经常使用结构来组装结果(这样我就可以返回单个变量的大量信息,并使字段名称成为文档的一部分)。从那以后,我转而使用面向对象编程(通常是handle-对象),它不仅收集相关变量,还收集相关功能,并且有助于代码重用。我的性能确实受到了影响,但它节省了我编码的时间,足以弥补这一点。请注意,如果可能的话,我会进行预分配(如果不只是将数组增长三次)。


示例

假设您有一个函数 getDimensions读取物体的尺寸(长度、高度、宽度)。然而,有时,对象是 2D,有时是 3D。因此,您需要填充以下变量:twoD.length、twoD.width、 ThreeD.length、 ThreeD.width、 ThreeD.height,理想情况下作为结构体数组,以便结构体的每个元素对应于一个对象。你事先并不知道有多少个对象,你所能做的就是轮询函数 thereAreMoreObjects ,返回 true 或 false,直到不再有对象为止。

以下是如何以合理的效率和按 block 增长数组来做到这一点:

%// preassign the temporary variable, and some others
chunkSize = 1000;
numObjects = 0;
idAndDimensions = zeros(chunkSize,4);

while thereAreMoreObjects()
    objectId = getCurrentObjectId();
    %// hi==-1 if it's flat
    [len,wid,hi] = getObjectDimensions(objectId);

    %// allocate more, if needed
    numObjects = numObjects + 1;
    if numObjects > size(idAndDimensions,1)
       %// grow array
       idAndDimensions(end+chunkSize,1) = 0;
    end

    idAndDimensions(numObjects,:) = [objectId, len, wid, hi];
end

%// throw away excess
idAndDimensions = idAndDimensions(1:numObjects,:);

%// split into 2D and 3D objects
isTwoD = numObjects(:,end) == -1;

%// assign twoD struct
twoD = struct('id',num2cell(idAndDimensions(isTwoD,1),...
              'length',num2cell(idAndDimensions(isTwoD,2),...
              'width',num2cell(idAndDimensions(isTwoD,3));

%// assign threeD struct

%// clean up - we need only the two structs 
%// I use keep from the File Exchange instead of clearvars
clearvars -except twoD threeD

关于matlab - 增量追加: How to avoid performance penalty of struct arrays,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34382496/

相关文章:

Matlab:当我放大 plotyy 图时,yTicks 不会自动更新

Visual Studio 2015/TFS 中的 MATLAB 文件关联

parallel-processing - 使用并行工具箱创建具有有限数量工作人员 (ThreadPool) 的任务/作业池

c - C中二维卷积的实现

Matlab 套接字等待响应

MATLAB 计时器对象陷阱和不良使用

matlab - 是否可以在 MATLAB 对象之间共享数据,例如查找表?

matlab - HaarTraining - MATLAB 还是 OpenCV?

matlab - 在 Matlab 中生成矩阵的所有可能组合

matlab - 如果括号 `[]` 没有填充数字,那么它在 Matlab 中做什么?