之前就 bsxfun
与 repmat
之间的性能比较提出了很少的问题。
Matlab - bsxfun no longer faster than repmat?
。这个尝试调查repmat
和bsxfun
之间的性能比较,特别是在输入数组本身的列中执行减去输入数组的平均值,因此只会探索{@minus
和bsxfun
1}} repmat
的一部分与其bsxfun
等效。{/ li>
In Matlab, when is it optimal to use bsxfun?
。那个人试图通过列中的平均值进行相同的减法操作,并且没有扩展到其他内置操作。通过这篇文章,我正在尝试调查 repmat
和 bsxfun
之间的效果数字,以涵盖所有 {{ 1}} 内置函数可以为它提供更广泛的视角,因为它们都提供了良好的矢量化解决方案。
具体来说,我对这篇文章的提问是:
bsxfun
的各种内置操作如何针对 repmat
等效执行? bsxfun
支持@plus
,@minus
,@times
等浮点运算,以及@ge
等关系和逻辑运算, @and
等等。那么,是否有特定的内置插件可以使用 bsxfun
比使用 repmat
给我带来明显的加速当量?
Loren在她的blog post
中针对 repmat
对 bsxfun
进行了基准测试,时间@() A - repmat(mean(A),size(A,1),1)
针对{分别为{1}}。如果我需要涵盖所有内置函数的基准测试,我是否可以使用其他可用于浮点,关系和逻辑运算的比较模型?
答案 0 :(得分:43)
关于bsxfun
是否优于repmat
或反之亦然的争论一直在进行。在这篇文章中,我们将尝试比较MATLAB附带的不同内置函数如何在运行时性能方面与repmat
等价物进行对比,并希望从中得出一些有意义的结论。
如果从MATLAB环境或Mathworks website中提取官方文档,可以看到bsxfun
支持的完整内置函数列表。该列表具有浮点,关系和逻辑运算的功能。
在MATLAB 2015A
上,支持的逐元素浮点运算是:
第二组由元素关系操作组成,它们是:
第三组也是最后一组包括此处列出的逻辑操作:
请注意,我们已从比较测试中排除了两个内置插件@max (maximum)
和@min (minimum)
,因为可以通过多种方式实现其repmat
等效内容。
要真实地比较repmat
和bsxfun
之间的效果,我们需要确保时间只需要涵盖预期的操作。因此,像bsxfun(@minus,A,mean(A))
这样的东西不会是理想的,因为它必须在mean(A)
调用内计算bsxfun
,但是时间可能无关紧要。相反,我们可以使用与B
相同大小的其他输入mean(A)
。
因此,我们可以使用:A = rand(m,n)
& B = rand(1,n)
,其中m
和n
是我们可以改变的大小参数,并根据它们研究性能。这完全在我们下一节列出的基准测试中完成。
对这些输入进行操作的repmat
和bsxfun
版本看起来像这样 -
REPMAT: A + repmat(B,size(A,1),1)
BSXFUN: bsxfun(@plus,A,B)
最后,我们正处于这篇文章的关键时刻,看看这两个家伙是否正在努力解决这个问题。我们将基准测试分为三组,一组用于浮点运算,另一组用于关系,第三组用于逻辑运算。我们已经将前面讨论的比较模型扩展到所有这些操作。
Set1:浮点运算
这是第一套使用repmat
和bsxfun
进行浮点运算的基准测试代码 -
datasizes = [ 100 100; 100 1000; 100 10000; 100 100000;
1000 100; 1000 1000; 1000 10000;
10000 100; 10000 1000; 10000 10000;
100000 100; 100000 1000];
num_funcs = 11;
tsec_rep = NaN(size(datasizes,1),num_funcs);
tsec_bsx = NaN(size(datasizes,1),num_funcs);
for iter = 1:size(datasizes,1)
m = datasizes(iter,1);
n = datasizes(iter,2);
A = rand(m,n);
B = rand(1,n);
fcns_rep= {@() A + repmat(B,size(A,1),1),@() A - repmat(B,size(A,1),1),...
@() A .* repmat(B,size(A,1),1), @() A ./ repmat(B,size(A,1),1),...
@() A.\repmat(B,size(A,1),1), @() A .^ repmat(B,size(A,1),1),...
@() rem(A ,repmat(B,size(A,1),1)), @() mod(A,repmat(B,size(A,1),1)),...
@() atan2(A,repmat(B,size(A,1),1)),@() atan2d(A,repmat(B,size(A,1),1)),...
@() hypot( A , repmat(B,size(A,1),1) )};
fcns_bsx = {@() bsxfun(@plus,A,B), @() bsxfun(@minus,A,B), ...
@() bsxfun(@times,A,B),@() bsxfun(@rdivide,A,B),...
@() bsxfun(@ldivide,A,B), @() bsxfun(@power,A,B), ...
@() bsxfun(@rem,A,B), @() bsxfun(@mod,A,B), @() bsxfun(@atan2,A,B),...
@() bsxfun(@atan2d,A,B), @() bsxfun(@hypot,A,B)};
for k1 = 1:numel(fcns_bsx)
tsec_rep(iter,k1) = timeit(fcns_rep{k1});
tsec_bsx(iter,k1) = timeit(fcns_bsx{k1});
end
end
speedups = tsec_rep./tsec_bsx;
Set2:关系操作
时间关系操作的基准测试代码会将早期基准测试代码中的fcns_rep
和fcns_bsx
替换为这些对应项 -
fcns_rep = {
@() A == repmat(B,size(A,1),1), @() A ~= repmat(B,size(A,1),1),...
@() A < repmat(B,size(A,1),1), @() A <= repmat(B,size(A,1),1), ...
@() A > repmat(B,size(A,1),1), @() A >= repmat(B,size(A,1),1)};
fcns_bsx = {
@() bsxfun(@eq,A,B), @() bsxfun(@ne,A,B), @() bsxfun(@lt,A,B),...
@() bsxfun(@le,A,B), @() bsxfun(@gt,A,B), @() bsxfun(@ge,A,B)};
Set3:逻辑操作
最后一组基准测试代码将使用此处列出的逻辑操作 -
fcns_rep = {
@() A & repmat(B,size(A,1),1), @() A | repmat(B,size(A,1),1), ...
@() xor(A,repmat(B,size(A,1),1))};
fcns_bsx = {
@() bsxfun(@and,A,B), @() bsxfun(@or,A,B), @() bsxfun(@xor,A,B)};
请注意,对于此特定集,所需的输入数据A和B是逻辑阵列。因此,我们必须在早期的基准测试代码中进行这些编辑以创建逻辑数组 -
A = rand(m,n)>0.5;
B = rand(1,n)>0.5;
基准测试代码在此系统配置上运行:
MATLAB Version: 8.5.0.197613 (R2015a)
Operating System: Windows 7 Professional 64-bit
RAM: 16GB
CPU Model: Intel® Core i7-4790K @4.00GHz
在运行基准测试后,bsxfun
超过repmat
获得的加速比如下图所示为三组绘制。
:一种。浮点运算:
从加速图中可以得出很少的观察结果:
bsxfun
atan2
和atan2d
的两个优秀加速案例主要针对30% - 50%
和repmat
。7
等效代码相比,7
执行提升。 @hypot
操作,其加速似乎非常接近统一,因此需要进一步检查。加速图可以缩小到那些@mod
操作,如下所示 -
根据上面的情节,我们可以看到,除了bsxfun
和repmat
,7
的一次性案例,bsxfun
仍然比bsxfun
大约好10% repmat
次操作。
<强> B中。关系操作:
这是bsxfun
支持的后续6个内置关系操作的第二组基准测试。
观察上面的加速图,忽略在10x
和bsxfun
之间具有可比运行时间的起始情况,可以很容易地看到bsxfun
赢得这些关系操作。加速 @xor
时,bsxfun
总是更适合这些情况。
<强>℃。逻辑操作:
这是repmat
支持的其余3个内置逻辑操作的第三组基准测试。
在开始时忽略bsxfun
的一次性可比运行时案例,bsxfun
似乎也对这组逻辑运算有优势。
5 - 7%
可能很容易被遗忘,而不是bsxfun
。对于其他情况,如果可以容忍bsxfun
性能较低的一个案例,则仍然可以使用ragged patterns
。{/ li>
bsxfun
使用关系和逻辑操作时,看到了巨大的性能提升,可以考虑使用bsxfun
来处理数据 bsxfun
,类似于单元阵列的性能优势。我喜欢将这些解决方案案例称为使用 bsxfun
的屏蔽功能。这基本上意味着我们创建逻辑数组,即具有repmat
的掩码,可用于在单元数组和数字数组之间交换数据。在数值数组中使用可行数据的一个优点是可以使用矢量化方法来处理它们。同样,由于repmat
是一个很好的矢量化工具,你可能会发现自己再次使用它来解决同一个问题,因此有更多理由去了解bsxfun
。我能够探索这些方法的几个解决方案案例链接在这里是为了读者的利益:
1,2,
3,4,
5。目前的工作重点是使用{{1}}在一个维度上复制数据。现在,{{1}}可以在多个维度上进行复制,因此{{1}}的扩展等同于复制。{{1}}因此,使用这两个函数对复制和扩展进行多个维度的类似测试会很有趣。