【问题标题】:How to pre-allocate a table with non-scalar sized variables?如何使用非标量大小的变量预先分配表?
【发布时间】:2020-01-26 15:54:15
【问题描述】:

当我遇到以下挑战时,由于各种原因,我正在使用 tables 作为常规数值数组的替代品:如何(预)分配具有非标量变量的表?

给定这样的循环:

function A = myfun(...)
N = large number
A = zeros(N,4);

for i = 1:N
   do stuff
   A(i,:) = [scalar, vector];
end

我想返回一个带有命名变量的表。

我可以简单地改写为:

function T = myfun2(...)
N = large number
A = zeros(N,4);

for i = 1:N
   do stuff
   A(i,:) = [scalar, vector];
end
T = table(A(:,1), A(:,2:end),'VariableNames',{'scalar','vector'});

这显然会产生一个格式如下的表格:

T =

  N×2 table

    scalar      vector   
    ______    ___________

      0       0    0    0
      0       0    0    0
      0       0    0    0
     ...          ...

现在,如果我想预先分配输出表并为每次迭代更新它,我会尝试以下方式:

function T = myfun3(...)
N = large number
T = table('Size',[N,2],...
       'VariableTypes',{'double','double'},...
       'VariableNames',{'scalar', 'vector'});

for i = 1:N
   do stuff
   T(i,:) = {scalar, vector};
end

myfun3的问题在于T的格式是:

T =

  N×2 table

    scalar    vector
    ______    ______

      0         0   
      0         0   
      0         0   

很明显,变量“向量”现在是标量而不是数组/向量。从table 文档中阅读,'size' 类型的预分配似乎不能接受数组大小?

Q1:如何使用非标量变量预分配table

Q2:如果myfun2 中的A 很大,开销是否不好,或者这是一个可以接受的解决方案?

我担心索引入/出表的额外开销与数字数组相比非常大,这会对性能代码产生不利影响。

======= 编辑 =======

我联系了 MathWorks,他们确认从 MATLAB R2019b 开始,使用 size 参数无法实现 Q1。

【问题讨论】:

  • 正如@Bentoy13 的回答所示,虽然您可以创建一个表并在其上循环,但这可能非常慢(可能比填充数组然后在结束。)我是表格的忠实粉丝,但它们必须与大小写一起使用。

标签: matlab performance matlab-table


【解决方案1】:

您可以在for循环之前创建表,然后通过列名访问它:

function T = myfun2(...)
N = large number
A = zeros(N,4);
T = table(A(:,1), A(:,2:end),'VariableNames',{'scalar','vector'});
for i = 1:N
   do stuff
   T.scalar(i,:) = scalar_i;
   T.vector(i,:) = vector_i;
   % or in one line: T(i,:) = table(scalar_i, vector_i);
end

我不确定每次迭代创建一个小表是否有效,所以可能更喜欢一次访问一列。

注意

正如 Juhl 在 cmets 中指出的那样,使用临时对象创建表可能会进行双重分配,而使用“Size”参数,您可以预期只分配了一块数据。

所以让我们检查一下。在我的电脑上,使用 Matlab 2019a,有:

>> memory
Maximum possible array:       56239 MB (5.897e+10 bytes) *

所以我可以在单个数组中分配 56.239e9 / 8 = 7.0299e9 元素(知道双精度数在 8 个字节上)。让我们总结一下,假设我想创建一个表,其中一列超过一半(3.51e9 元素):

>> T = table(zeros(4e9,1));
>> memory
Maximum possible array:       33644 MB (3.528e+10 bytes)

分配需要很长时间,但完成。使用 'Size' 完全一样:

>> T = table(zeros(4e9,1));
>> memory
Maximum possible array:       33677 MB (3.531e+10 bytes) *

看来我们没有双重分配。

有一个有趣的事实:T 占用的内存比我们预期的要少。如果我尝试修改表的最后一个元素,它似乎会消耗内存达到预期的内存大小:

>> T.Var1(end) = 1;
>> memory
Maximum possible array:       27574 MB (2.891e+10 bytes)

披露

请注意,修改这种表格需要时间:

>> tic; T.Var1(end) = 1; toc
Elapsed time is 33.286967 seconds.

所以我的结论是:使用普通数组会快很多:

>> tic; T = table('Size', [4e9, 1], 'VariableTypes',{'double'}); toc
Elapsed time is 15.997680 seconds.
>> tic; T.Var1(end) = 1; toc
Elapsed time is 33.286967 seconds.
>> clear T;

>> tic; A = zeros(4e9,1); toc
Elapsed time is 0.043366 seconds.
>> tic; A(end) = 1; toc
Elapsed time is 0.002430 seconds.
>> clear A;

【讨论】:

  • 是的,这会起作用,但是你没有使用table 的'size'参数,我假设性能影响不是太大,即它不会同时分配两者数据块?
  • 感谢性能更新。修改这样的表比我想象的要糟糕得多!在分配和修改节目时打开内存图(Win:资源监视器)并且内存在波动,这可能是性能不足的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 2023-03-09
相关资源
最近更新 更多