1

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

和.

C我用和的新值计算了这个总和 2500 次z

参数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%)。

4

3 回答 3

6

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

AFAIK,切线函数具有实际的专用硬件来计算其值。此外,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

你会发现所有的痛苦都来自于切线函数,所以你最好专注于此。正如您在此处所读到的,基本上只有在牺牲一些准确性的情况下才能加快三角函数的速度。

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

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

  2. 您不能重新制定问题以便事后计算切线吗?还是以前?或者无论如何,元素数量要少得多?在上述问题中,这显然是不可能的,但我不知道完整的代码——可能有一些很好的三角标识可以适用于您的问题。

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

于 2012-09-06T20:13:57.300 回答
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.
于 2012-09-06T11:22:58.520 回答
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 开销并不太大。

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

您只能希望改进 tan() - 可能通过并行运行它。毕竟,这是一个可并行化的任务……考虑将其导出到一个 MEX 文件,该文件将使用 OpenMP。非常简单的工作,如果您有几个备用内核,则可以大大加快速度。

于 2012-09-06T22:05:10.483 回答