是否有可能使这个矢量化的MATLAB代码更快?

时间:2012-09-06 11:16:28

标签: matlab vectorization

在我的程序中,我需要计算总和:

sum

我使用Cz的新值计算此总和2500次。

参数 z 可以是矢量。我编写了简单的for循环和矢量化版本代码,如下所示:

K = 200;
n_z = 40000;
C = ones(K,1); % an example, in real life the arey some coefficients (at next call will be new)
k = 0:K-1;
z = linspace(0, 2*pi, n_z); % at next call will be new

tic;
    my_sum_for = zeros(1, K);
    for i=1:n_z
       my_sum_for(i) = C' * tan(k' * z(i));
    end
toc; % Elapsed time is 1.820485 seconds.

tic;
     my_sum = C' * tan(k' * z);
toc; % Elapsed time is 0.160924 seconds.

矢量化版本更快,但还不够。 是否有可能改进矢量化版本?

在Dominique Jacquel的回答之后我有了这个矢量化版本,它更快:

    K = 200;
    n_z = 40000;
    C = ones(K,1)';  % an example, in real life they are some coefficients (at next call will be new)
    k = (0:K-1)';
    z = linspace(0, 2*pi, n_z);  % at next call will be new

    tic;
        my_sum_for = zeros(1, K);
        for i=1:n_z
           my_sum_for(i) = C * tan(k * z(i));
        end
    toc; % Elapsed time is 1.521587 seconds.

    tic;
         my_sum = C * tan(k * z);
    toc; % Elapsed time is 0.125468 seconds.

是否有可能进一步改进矢量化版本(bsxfun,arrayfun或其他东西)? 250秒的时间对我来说仍然很慢(占所有计算的75%)。

3 个答案:

答案 0 :(得分:6)

我认为你非常接近这里的硬件限制。 Matlab中的矩阵乘法是使用BLAS libraries完成的,在性能方面已证明难以击败。

AFAIK,切线函数具有实际dedicated hardware来计算其值。此外,Matlab会自动在多个内核上分配大型矩阵的触发函数,因此基本上没有什么可以改进的。

另外,如果我错了,请纠正我,但考虑到可能的数据开销和内存问题,我认为这个计算实际上会在GPU上更慢

现在,如果你比较这些:

tic;
for ii = 1:10
    my_sum = C * tan(k * z);
end
toc 

tic;
for ii = 1:10
    my_sum_notan = C * k * z;
end
toc

你会看到所有的痛苦来自切线功能,所以你最好关注这一点。正如你可以阅读here,如果你牺牲一些准确性,基本上只能加速触发三角函数。

总之,你应该问自己这些问题:

  1. 你是否愿意放弃全双精度,或者说是“足够接近”的6位数?

  2. 你不能重新制定问题,以便事后计算切线吗?还是之前?或者,对于少得多的元素?在所述的问题中,这显然是不可能的,但我不知道完整的代码 - 可能有一些很好的触发身份可能适用于您的问题。

  3. 考虑到以上所有因素,优化这一点所需的工作量是否真的超过了更长的运行时间?与编写实现残缺但快速触发功能的自定义,难以移植的MEX功能相比,250秒听起来并不算太糟糕。

答案 1 :(得分:3)

尽可能多地进行矩阵运算(在这种情况下换位)以节省一些时间在循环中

K = 200;
n_z = 40000;
C = ones(K,1)';
k = (0:K-1)';
z = linspace(0, 2*pi, n_z);

tic;
    my_sum_for = zeros(1, K);
    for i=1:n_z
       my_sum_for(i) = C * tan(k * z(i));
    end
toc

tic;
     my_sum = C * tan(k * z);
toc;

我之前的执行时间

Elapsed time is 1.266158 seconds.
Elapsed time is 0.531173 seconds.

之后

Elapsed time is 0.496803 seconds.
Elapsed time is 0.185396 seconds.

答案 2 :(得分:3)

这是一个在我的计算机上运行速度稍快的版本:

k = repmat((0:K-1)', 1, n_z);
z = repmat(linspace(0, 2*pi, n_z), K, 1);
C = ones(1, K);
tic
my_sum = C*tan(k.*z);
toc

基本上,我不是k和z的外积,而是直接在矩阵上运算。

第一版

Elapsed time is 0.652923 seconds.
Elapsed time is 0.240300 seconds.

在Dominique Jacquel的回答之后

Elapsed time is 0.376218 seconds.
Elapsed time is 0.214047 seconds.

我的版本

Elapsed time is 0.168535 seconds.

您可能需要添加repmats的费用,但也许您只能这样做一次,我不知道其余的代码。

我完全赞同Rody Oldenhuis。大部分工作在于切线函数。我可以告诉你更多。 k。* z计算非常有效,无法进行太多改进。如果计算内存带宽,我的计算机上的内存带宽大约为10GB / s。我能得到的峰值大约是16GB / s,所以它的接近。那里的可能性不多。与C * T相同。这是一个简单的BLAS2矩阵向量乘法,它是存储有界的。对于系统大小,您显示MATLAB开销不是太大。

编辑:正如Rody所提到的,MATLAB的新版本已经并行化了tan()。所以这里也不多。

你只能希望改进tan() - 可能是通过并行运行它。毕竟,这是一个简单的可并行化任务...考虑将此导出到MEX文件,该文件将使用OpenMP。非常简单的工作,如果你有一些备用核心,可以加速很多。