performance - 加权 Gram-Schmidt 正交化的 MATLAB 优化

标签 performance matlab optimization linear-algebra vectorization

我在 MATLAB 中有一个函数执行 Gram-Schmidt Orthogonalisation对内积应用了非常重要的权重(我认为 MATLAB 的内置函数不支持这一点)。 据我所知,这个函数运行良好,但是,它在大型矩阵上太慢了。 改进这一点的最佳方法是什么?

我已尝试转换为 MEX 文件,但我失去了与我正在使用的编译器的并行化,因此速度变慢了。

我正在考虑在 GPU 上运行它,因为元素乘法是高度并行化的。 (但我更希望实现易于移植)

任何人都可以向量化此代码或使其更快吗?我不确定如何优雅地做到这一点......

我知道这里的 stackoverflow 头脑很棒,认为这是一个挑战 :)

函数

function [Q, R] = Gram_Schmidt(A, w)
    [m, n] = size(A);
    Q = complex(zeros(m, n));
    R = complex(zeros(n, n));
    v = zeros(n, 1);

    for j = 1:n
        v = A(:,j);
        for i = 1:j-1
            R(i,j) = sum(   v    .* conj( Q(:,i) ) .* w ) / ...
                     sum( Q(:,i) .* conj( Q(:,i) ) .* w );
            v = v - R(i,j) * Q(:,i);
        end
        R(j,j) = norm(v);
        Q(:,j) = v / R(j,j);
    end
end

其中 A 是一个 m x n 复数矩阵,w 是一个 m x 1 实数向量.

瓶颈

这是 R(i,j) 的表达式,它是函数中最慢的部分(不能 100% 确定符号是否正确): Weighted Inner-Product

其中 w 是一个非负权重函数。 维基百科的几个页面上提到了加权内积,this is one on the weight functionthis is one on orthogonal functions .

复制

您可以使用以下脚本生成结果:

A = complex( rand(360000,100), rand(360000,100));
w = rand(360000, 1);
[Q, R] = Gram_Schmidt(A, w);

其中 Aw 是输入。

速度和计算

如果您使用上述脚本,您将获得与以下同义的探查器结果: Profiler Times Profiler Code Times

测试结果

您可以使用以下脚本通过将一个函数与上面的函数进行比较来测试结果:

A = complex( rand( 100, 10), rand( 100, 10));
w = rand( 100, 1);
[Q , R ] = Gram_Schmidt( A, w);
[Q2, R2] = Gram_Schmidt2( A, w);
zeros1 = norm( Q - Q2 );
zeros2 = norm( R - R2 );

其中 Gram_Schmidt 是前面描述的函数,Gram_Schmidt2 是替代函数。结果 zeros1zeros2 应该非常接近于零。

注意事项:

我尝试用以下方法加速 R(i,j) 的计算,但无济于事......

R(i,j) = ( w' * (   v    .* conj( Q(:,i) ) ) ) / ...
         ( w' * ( Q(:,i) .* conj( Q(:,i) ) ) );

最佳答案

1)

我第一次尝试向量化:

function [Q, R] = Gram_Schmidt1(A, w)
    [m, n] = size(A);
    Q = complex(zeros(m, n));
    R = complex(zeros(n, n));

    for j = 1:n
        v = A(:,j);
        QQ = Q(:,1:j-1);
        QQ = bsxfun(@rdivide, bsxfun(@times, w, conj(QQ)), w.' * abs(QQ).^2);
        for i = 1:j-1
            R(i,j) = (v.' * QQ(:,i));
            v = v - R(i,j) * Q(:,i);
        end
        R(j,j) = norm(v);
        Q(:,j) = v / R(j,j);
    end
end

不幸的是,它比原来的功能慢。


2)

然后我意识到这个中间矩阵QQ的列是增量构建的,之前的没有修改。所以这是我的第二次尝试:

function [Q, R] = Gram_Schmidt2(A, w)
    [m, n] = size(A);
    Q = complex(zeros(m, n));
    R = complex(zeros(n, n));
    QQ = complex(zeros(m, n-1));

    for j = 1:n
        if j>1
            qj = Q(:,j-1);
            QQ(:,j-1) = (conj(qj) .* w) ./ (w.' * (qj.*conj(qj)));
        end
        v = A(:,j);
        for i = 1:j-1
            R(i,j) = (v.' * QQ(:,i));
            v = v - R(i,j) * Q(:,i);
        end
        R(j,j) = norm(v);
        Q(:,j) = v / R(j,j);
    end
end

从技术上讲,没有进行主要的向量化;我只预先计算了中间结果,并将计算移到了内部循环之外。

基于快速基准测试,这个新版本肯定更快:

% some random data
>> M = 10000; N = 100;
>> A = complex(rand(M,N), rand(M,N));
>> w = rand(M,1);

% time
>> timeit(@() Gram_Schmidt(A,w), 2)     % original version
ans =
    1.2444
>> timeit(@() Gram_Schmidt1(A,w), 2)    % first attempt (vectorized)
ans =
    2.0990
>> timeit(@() Gram_Schmidt2(A,w), 2)    % final version
ans =
    0.4698

% check results
>> [Q,R] = Gram_Schmidt(A,w);
>> [Q2,R2] = Gram_Schmidt2(A,w);
>> norm(Q-Q2)
ans =
   4.2796e-14
>> norm(R-R2)
ans =
   1.7782e-12

编辑:

根据评论,我们可以重写第二个解决方案以摆脱 if-statmenet,方法是将该部分移动到外循环的末尾(即在计算新列 Q(:,j) 之后立即执行) ,我们计算并存储相应的QQ(:,j))。

功能在输出上是相同的,时间上也没有什么不同;代码稍微短了一点!

function [Q, R] = Gram_Schmidt3(A, w)
    [m, n] = size(A);
    Q = zeros(m, n, 'like',A);
    R = zeros(n, n, 'like',A);
    QQ = zeros(m, n, 'like',A);

    for j = 1:n
        v = A(:,j);
        for i = 1:j-1
            R(i,j) = (v.' * QQ(:,i));
            v = v - R(i,j) * Q(:,i);
        end
        R(j,j) = norm(v);
        Q(:,j) = v / R(j,j);
        QQ(:,j) = (conj(Q(:,j)) .* w) ./ (w.' * (Q(:,j).*conj(Q(:,j))));
    end
end

请注意,我使用了 zeros(..., 'like',A) 语法(在最近的 MATLAB 版本中是新的)。这允许我们在 GPU 上运行未修改的函数(假设您有并行计算工具箱):

% CPU
[Q3,R3] = Gram_Schmidt3(A, w);

对比

% GPU
AA = gpuArray(A);
[Q3,R3] = Gram_Schmidt3(AA, w);

不幸的是,就我而言,它并没有更快。事实上,在 GPU 上运行比在 CPU 上运行要慢很多倍,但值得一试:)

关于performance - 加权 Gram-Schmidt 正交化的 MATLAB 优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26886082/

相关文章:

php - 如何防止一个页面出现多条sql查询?

matlab - 从圆和侵 eclipse 半径中移除突起

matlab - 使用 scatter3 用 3D 立方体填充空间

java - 使用数组来最小化变量的使用

MySQL - 从带有主键的磁盘表中读取

asp.net - 如何找到 IIS 在负载/性能测试期间模拟的平均并发用户数?

performance - 如何让 Android 应用保持横向

mysql - 为什么 ActiveRecord destroy_all 需要这么长时间?

Python,文字检测OCR

mysql - Laravel Web 应用程序的可扩展性