MATLAB中元素访问的时间复杂度

时间:2016-03-28 07:26:18

标签: performance matlab

以下两个代码片段执行相同的任务(从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元素是否需要耗费时间?

2 个答案:

答案 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重新加载到寄存器中。