以下两个代码片段执行相同的任务(从N-dim球体均匀生成M个样本)。我想知道为什么后者比前一个消耗更多的时间。
fopen("/test/pcm_in.pcm", "w");
输出结果为:
%% MATLAB R2014a
M = 30;
N = 10000;
#1
tic
S = zeros(M, N);
for k = 1:M
P = ones(1, N);
for i = 1:N - 1
t = rand*2*pi;
P(1:i) = P(1:i)*sin(t);
P(i+1) = P(i+1)*cos(t);
end
S(k,:) = P;
end
toc
#2
tic
S = ones(M, N);
for k = 1:M
for i = 1:N - 1
t = rand*2*pi;
S(k, 1:i) = S(k, 1:i)*sin(t);
S(k, i+1) = S(k, i+1)*cos(t);
end
end
toc
我也试过M = 1,
Elapsed time is 15.007667 seconds.
Elapsed time is 59.745311 seconds.
#2比#1慢近4倍。在#2中频繁使用2d元素是否需要耗费时间?
答案 0 :(得分:1)
时差是由于内存访问模式以及它们映射到缓存的程度。也可能是MATLAB对您的硬件向量单元(SSE / AVX)的利用。 MATLAB存储矩阵" column-major",意味着S(2,1)
在S(1,1)
旁边。
在#1中,您使用载体P处理每个样本,载体P位于连续的内存中。这些80,000字节很容易适合L2缓存,以便您快速重复访问。他们也是邻居,并且很容易被矢量化(我不确定MATLAB是否会执行此优化,但我希望如此......)
在#2中,您一次访问一行S,不是连续的,而是由M值交错。因此每行分布在30 * 80,000字节,这不适合L2缓存。即使您忽略了该数据中的29/30值,也必须在每次重复访问时重新读回。
这是测试。我所做的只是转换S,以便您可以一次处理一列,然后将其放回到最后,以获得相同的结果:
#3
tic
S = ones(N, M);
for k = 1:M
for i = 1:N - 1
t = rand*2*pi;
S(1:i, k) = S(1:i, k)*sin(t);
S(i+1, k) = S(i+1, k)*cos(t);
end
end
S = S.';
toc
结果:
Elapsed time is 11.254212 seconds.
Elapsed time is 45.847750 seconds.
Elapsed time is 11.501580 seconds.
是的,转置S使我们获得与单独的向量方法相同的连续访问和性能。顺便说一下,L3与L2的时钟周期大约是4倍...... 1
让我们看看是否可以找到与缓存大小相关的任何断点。这里的N = 1000,其中一切都应该适合L2:
Elapsed time is 0.240184 seconds.
Elapsed time is 0.373448 seconds.
Elapsed time is 0.258566 seconds.
差别很小,但现在我们可能会进入L1效应。
最后,这是解决问题的完全不同的方法。它依赖于多变量正常RV具有正确对称性的事实。
#4
tic
S = randn(M, N);
S = bsxfun(@rdivide, S, sqrt(sum(S.*S, 2)));
toc
Elapsed time is 10.714104 seconds.
Elapsed time is 45.351277 seconds.
Elapsed time is 11.031061 seconds.
Elapsed time is 0.015068 seconds.
答案 1 :(得分:0)
我怀疑优势来自于在数组访问中使用硬编码1。如果你尝试M = 1,你仍然可以看到sin(t)线的显着加速。我的猜测是,引擎盖下的程序集可以使用立即指令,而不是将变量K重新加载到寄存器中。