【问题标题】:Dataset array indexing is very slow with Statistics Toolbox使用 Statistics Toolbox 的数据集数组索引非常慢
【发布时间】:2011-10-05 14:12:38
【问题描述】:

为什么索引到数据集数组这么慢? dataset.subsref 函数的峰值表明数据集的所有列都存储在一个元胞数组中。但是,单元格索引比数据集索引要快得多,后者只是索引到引擎盖下的单元格数组。我的猜测是,这与 MATLAB OOP 的一些开销有关。有关如何加快速度的任何想法?

%% Using R2011a, PCWIN64
feature accel off;  % turn off JIT

dat = (1:1e6)';
dat2 = repmat({'abc'}, 1e6, 1);
celldat = {dat dat2};
ds = dataset(dat, dat2);
N = 1e2;

tic;
for j = 1:N
    tmp = celldat{2};
end
toc;

tic;
for j = 1:N
    tmp2 = ds.dat2; % 2.778sec spent on line 262 of dataset.subsref
end
toc;

feature accel on;  % turn JIT back on
Elapsed time is 0.000165 seconds.
Elapsed time is 2.778995 seconds.

编辑:我已将示例更新为更像我所看到的问题。在 dataset.subsref -“b = a.data{varIndex};”的第 262 行上花费了大量时间。这对我来说很奇怪,因为它是一个简单的单元格取消引用。我想知道是否有一个 OOP 技巧可以让我在没有奇怪开销的情况下索引“a.data”。

EDIT2:根据 Andrew 的建议,我已将此作为错误提交给 MatWorks。如果我收到他们的任何消息,会更新。

EDIT3: Matlab 回应说他们现在已经意识到这个问题,并将在未来的版本中修复它。他们指出问题是特定于元胞数组的,并尽可能避免它们。

【问题讨论】:

  • 在 MATLAB profiler 下是什么样子的?
  • +1 Richie 的评论是任何 Matlab 性能问题的最佳答案。
  • 超过 90% 的时间花在 dataset.subsref 的第 262 行,这很奇怪,因为它是一个简单的单元格取消引用。不幸的是,我给出的例子太简单了,无法展示。我会更新一个更现实的例子。
  • @Rich C: "b = a.data{varIndex}" 不是简单的单元格取消引用。这 ”。”部分是 MCOS 对象字段访问,然后是“{}”单元格取消引用。我破解了 dataset.subsref 将其分成两行:“tmp = a.data; b = tmp{varIndex};”并重新分析它。所有时间都花在“a.data”字段访问上。同样,MCOS 开销。
  • 等等,你修改后的代码只有 1e2 次通过?这是惊人的缓慢。对于 subsref 中的“a.data”字段访问,这是 20 毫秒,而不是微秒。除了正常的开销之外,这里还发生了一些可疑的事情。看起来那个时间与数据有关。例如。如果我将其更改为“dat = 42; dat2 = {'foo'};”,那么它会变快。几乎就像写时复制优化不起作用,它正在对数据进行大量复制。

标签: matlab


【解决方案1】:

是的,您很可能会看到 Matlab OOP 方法调用的开销。与单元格索引或其他一些语言中的方法调用相比,它们的成本很高。您的 .513872 秒 / 1e4 ~= 51 微秒每次调用,这是几个 MCOS 方法调用的大致成本;在我见过的机器上,它们每个约为 5-15 微秒。所以这看起来像是 subsref() 调用本身的方法开销以及它依次调用的其他方法和属性访问。

一些细节和讨论见:Is MATLAB OOP slow or am I doing something wrong?

除了构建代码以尽量减少对“ds.dat”或其他方法的调用之外,我不知道有什么方法可以加快速度。如果可能,在处理数据集时,调用一次“ds.dat”,将其保存在局部变量中并在那里使用,然后将其推回 ds 对象。

警告:我不知道“功能加速”是做什么的,也不知道它会如何影响这些时间。

编辑:我按照 Richie 的建议将其放入分析器中。在我的 R2009b 上,看起来大约一半时间是方法调用开销,其余时间在 find()、strcmp() 和 subsref 内的其他操作中; subsref 不会依次调用任何其他方法。

编辑 2:修改后的示例显示了更高的时序。方法调用开销并不能说明所有这些。

【讨论】:

  • 是的,保存 ds.dat 是节省时间的好方法。如果我知道我将需要循环中的同一列,我总是这样做。 accel off 关闭 JIT,因此它不会扭曲单元阵列循环的时间。
猜你喜欢
  • 2013-08-08
  • 2015-02-23
  • 2022-10-24
  • 2022-11-12
  • 2016-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多