【问题标题】:Time complexity of element accessing in MATLABMATLAB中元素访问的时间复杂度
【发布时间】:2016-03-28 07:26:17
【问题描述】:

以下两个代码 sn-ps 执行相同的任务(从 N-dim 球体均匀生成 M 个样本)。我想知道为什么后一个比前一个消耗更多的时间。

%% 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

输出是:

Elapsed time is 15.007667 seconds.
Elapsed time is 59.745311 seconds.

我也试过 M = 1,

Elapsed time is 0.463370 seconds.
Elapsed time is 1.566913 seconds.

#2 比 #1 慢近 4 倍。 #2 中频繁访问 2d 元素是否会导致耗时?

【问题讨论】:

    标签: performance matlab


    【解决方案1】:

    时间差异是由于内存访问模式,以及它们映射到缓存的程度。也可能是 MATLAB 对您的硬件矢量单元 (SSE/AVX) 的利用。 MATLAB 存储矩阵“column-major”,意思是S(2,1)S(1,1) 旁边。

    在 #1 中,您使用位于连续内存中的向量 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.
    

    【讨论】:

    • 很好的解释。而且,新方法非常简单高效。
    【解决方案2】:

    我怀疑优势来自在访问数组时使用硬编码的 1。如果您尝试 M=1,您仍然会看到 sin(t) 线的显着加速。我的猜测是,引擎盖下的程序集可以执行一些使用立即指令,而不是将变量 K 重新加载到寄存器中。

    【讨论】:

    • 我的错。第一种情况下的 P 应该被初始化为一个全一个数组。我修改了我的问题。注意代码中使用的 rand 函数,S 在#2 中不会成为一个全一矩阵。
    • 已编辑答案以使其更加清晰。我知道 S 不会最终成为一个全为矩阵。我只是说 S 从一个矩阵开始。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    • 2019-08-26
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多