MATLAB循环索引的最佳实践

11

我很惊讶地发现使用MATLAB的for循环运行时出现了以下不同的成本:

ksize = 100;
klist = 1:ksize;

tic
for m = 1:100000        
    for k = 1:ksize

    end        
end
toc

tic
for m = 1:100000        
    for k = klist

    end        
end
toc

唯一的区别在于索引列表的创建方式。我本以为第二个版本会更快,但出乎意料!

Elapsed time is 0.055400 seconds.
Elapsed time is 1.695904 seconds.

我的问题有两个方面:第一,是什么导致了上述结果?第二,在MATLAB编程中还有哪些类似的细微差别?我希望能够更好地发现这些低效率的地方。谢谢大家。


3
这可能是由于JIT加速引起的,因为如果您使用“feature accel off”关闭它,两次运行的结果将类似。 - Eitan T
2个回答

4

for()的文档说明如下:

for index = values
   ...
end

where values has one of the following forms:

  • ...

  • valArray: creates a column vector index from subsequent columns of array valArray on each iteration. For example, on the first iteration, index = valArray(:,1). The loop executes for a maximum of n times, where n is the number of columns of valArray, given by numel(valArray, 1, :). The input valArray can be of any MATLAB data type, including a string, cell array, or struct.

因此,我认为存在显著的开销,并且编译器不检查1:ksize == klist以利用更快的实现。换句话说,根据Eitan的评论,JIT适用于前两种接受的
整个问题与以下索引任务(列与元素)有关:
tic
for m = 1:100000        
    for k = 1:ksize
        klist(:,k);
    end        
end
toc

tic
for m = 1:100000        
    for k = 1:ksize
        klist(k);
    end        
end
toc

Index column:  ~2.9 sec
Index element: ~0.28 sec

你可以看到,klist(:,k)有效地减慢了更快的循环速度,这表明在for k = klist中的问题与此情况下使用的列索引有关。
有关详细信息,请参见有关(低效)索引的长时间讨论

2
我的答案是猜测(因为只有Mathworks的人知道他们产品的实现),但我认为第一个k循环被优化为不创建实际的索引数组,而只是逐个扫描它们,因为它明确地显示了值的“构建”方式。第二个k循环无法进行优化,因为解释器事先不知道索引数组的内容是否会均匀增长。因此,每次循环开始时,它都将访问原始的klist,这就是为什么会有性能惩罚。
稍后编辑:另一个性能惩罚可能来自于klist数组中的索引访问,与即时创建索引值相比。

我想知道你是否可以通过创建一个从double派生并覆盖copy的类来测试这个问题... - wakjah
@wakjah 检查过了,这不是关于复制的问题(实际上没有进行复制 -- 我认为写时复制仍然有效)。这是关于 klist 中下标引用的问题。我已经更正了我的帖子。感谢您的建议。 - user2271770
@wakjah 另一方面,copy适用于句柄子类,而且不能同时继承值和句柄类(至少在我手头的R2010a版本中是这样)。我想知道我是否实际上覆盖了什么... :D - user2271770
这是真的...也许在值类的构造函数/subsasgn中? - wakjah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接