【问题标题】:Matlab: is removing rows/columns from a matrix expensive?Matlab:从矩阵中删除行/列是否昂贵?
【发布时间】:2017-11-17 06:41:27
【问题描述】:

在 MATLAB 中将行/列附加到矩阵中似乎是一件值得考虑的事情。例如,当我尝试将一列附加到具有多行多列的矩阵 A 时,例如

A = [A, added_col]

Matlab 会警告我,因为这必须在内存中制作 A 的副本,所以我最好使用预分配来提高速度。这是可以理解的,因为A中的底层数据占用了一块连续的内存。

我的问题是,删除行/列会导致类似的问题吗?比如去掉A的第二行:

A(2,:) = []

此操作是否就地进行?我真的不确定,因为一方面它似乎没有为内存中的数据腾出任何新空间,另一方面,A 的行将不连续存储(因为删除了第 2 行)。

那么内部会发生什么?这种操作是否足够有效以在实践中使用?谢谢!


刚刚用100000的复杂度测试了一下:

clc; clear;
N = 100000;
A = zeros(N, 3);

t1 = tic;
for ii = 1:N
    A(ii, :) = [1 2 3];
end
t2 = toc;

还有

clc; clear;
N = 100000;
A = zeros(N, 3);

t1 = tic;
for ii = (N-1):-1:2
    A(ii, :) = [];
end
t2 = toc;

结果:第一个(修改预分配矩阵)为 0.009 秒,第二个(从矩阵中删除行)为 53.429 秒。我认为这基本上解决了这个问题:不,从矩阵中删除行/列效率不高,因为它肯定涉及深度复制数据和重新分配内存。

此外,删除 columns 而不是行也不是一个好主意。正如我测试的那样,在上述复杂度范围内,它仍然需要大约两分钟:

N = 100000;
test_m = zeros(3, N);
tic
for ii = (N - 1):-1:2
    test_m(:, ii) = [];
end
toc
% result: 105.436595 seconds. 
% This was run on a different machine than the previous examples.
% But is still enough evidence that dynamically resizing a big matrix is a BAD idea.

所以,故事的结尾:不要尝试以这种方式删除列或行,除非你有一个非常小的矩阵。对于庞大的矩阵,请始终使用预分配。

【问题讨论】:

  • 由于 MATLAB 将数据存储在 column-major(按列)编号方案中,因此删除列应该更有效。
  • @rahnema1 谢谢。但是内存的重新分配是不可避免的吗?
  • 不一定!它可能会根据索引的类型使用不同的方法。例如,删除第一列只需要一次调用memmove 而无需重新分配。或者以更优化的方式,它可能不需要 memmove 并使用偏移量来表示数据的开始。
  • @rahnema1 不幸的是,当我测试它时,情况并非如此。执行列删除至少需要几十秒。
  • 最好提供您的测试结果。例如,一个删除第一列的测试和另一个删除最后一列的测试以及第一行和最后一行的相同测试。

标签: matlab performance matrix memory


【解决方案1】:

这里有几个问题:

  1. 是否为删除分配内存
  2. 行主要与列主要内存访问
  3. 代码示例
  4. 预分配修改与删除?

1) 不使用 mex,您无法控制矩阵删除是否使用相同的内存。但是,您可以判断它是否发生。一种方法是用 mex 写一些东西。或者您可以激活format debug

N = 100000;
test_m = zeros(3, N);
t = evalc('disp(test_m)');
disp(t(1:100))
test_m(:,2:N-1) = [];
disp(t(1:100))

这会产生输出

Structure address = 1259f6da0
m = 3
n = 100000
pr = 15d6d2020
pi = 0
  Columns 1 through 9

     0 

Structure address = 1259f6b70
m = 3
n = 2
pr = 608001c95320
pi = 0
     0     0
     0     0
     0

     0 

请注意,为了保持显示的合理性,我捕获了显示变量的输出,然后只显示它的一部分。这个输出,特别是pr(指向真实数据的指针)表明发生了重新分配。我找不到任何没有发生重新分配的情况。

2) 正如在一些 cmets 中提到的并在问题中提到的那样,内存存储为主要列。因此,当您删除一列时,它可能比删除一行更有效...

3) 我不完全确定代码示例是否真实,但一次删除所有列或行更有意义。这发生得非常快。

N = 100000;
test_m = zeros(3, N);
test_m(:,2:N-1) = [];

4) 最后,我不确定您的措辞是指修改与删除的预分配。大局最好避免在循环中删除行或列。而是保留一个数组,该数组指示应删除哪些列或行,然后一次性完成。

【讨论】:

  • 干得好!理论上可以就地删除行或列,但我相信创建一个新矩阵并复制数据会更容易。您是否尝试删除最后一列?这根本不需要任何数据副本。但也许他们没有打扰特殊情况。
  • @CrisLuengo 我也试过了,我惊讶地发现指针发生了变化。我在 mex 中使用 mxRealloc 进行了类似的截断,我似乎记得它几乎总是保持相同的指针。
  • 您可以在 mex 例程中就地更改尺寸以删除尾随列。它会很快,但仍然分配了尾随列的内存。任何就地方法的问题是(像往常一样)如果正在进行任何引用共享(即共享 mxArray 标头的变量),您可能会搞砸其他变量。对于更高版本的 MATLAB,原始 mxArray 指针甚至不会传递到 mex 例程中(传递了指向共享数据副本的指针)。
猜你喜欢
  • 1970-01-01
  • 2016-05-26
  • 1970-01-01
  • 2011-05-09
  • 2012-06-17
  • 1970-01-01
  • 1970-01-01
  • 2013-03-03
  • 1970-01-01
相关资源
最近更新 更多