在 MATLAB 中循环添加多个稀疏矩阵的最快方法
Fastest way to add multiple sparse matrices in a loop in MATLAB
我有一个代码可以在循环中重复计算稀疏矩阵(准确地说,它执行了 13472 次计算)。这些稀疏矩阵中的每一个都是唯一的。
每次执行后,它将新计算的稀疏矩阵添加到原来的稀疏零矩阵中。
添加完所有 13742 个矩阵后,代码退出循环,程序终止。
添加稀疏矩阵时出现代码瓶颈。我制作了一个虚拟版本的代码,它表现出与我的真实代码相同的行为。它由一个 MATLAB 函数和下面给出的脚本组成。
(1) 生成稀疏矩阵的函数:
function out = test_evaluate_stiffness(n)
ind = randi([1 n*n],300,1);
val = rand(300,1);
[I,J] = ind2sub([n,n],ind);
out = sparse(I,J,val,n,n);
end
(2) 主脚本(程序)
% Calculate the stiffness matrix
n=1000;
K=sparse([],[],[],n,n,n^2);
tic
for i=1:13472
temp=rand(1)*test_evaluate_stiffness(n);
K=K+temp;
end
fprintf('Stiffness Calculation Complete\nTime taken = %f s\n',toc)
我对稀疏矩阵运算不是很熟悉,所以我可能在这里遗漏了一个关键点,这可能会大大加快我的代码速度。
我在代码中是否以合理的方式处理刚度矩阵的更新?有没有我应该使用稀疏的另一种方法可以得到更快的解决方案?
下面还提供了分析器报告:
如果您只需要这些矩阵的总和,而不是单独构建所有矩阵然后对它们求和,只需连接向量 I
、J
和 vals
并调用sparse
只有一次。如果[I,J]
中有重复行[i,j]
,对应的值S(i,j)
会自动求和,所以代码是绝对等价的。由于调用 sparse
涉及对排序算法的内部调用,因此您可以节省 13742-1 次中间排序,并且只需要一次即可。
这涉及将 test_evaluate_stiffness
的签名更改为输出 [I,J,val]
:
function [I,J,val] = test_evaluate_stiffness(n)
并删除行 out = sparse(I,J,val,n,n);
.
然后您将其他功能更改为:
n = 1000;
[I,J,V] = deal([]);
tic;
for i = 1:13472
[I_i, J_i, V_i] = test_evaluate_stiffness(n);
nE = numel(I_i);
I(end+(1:nE)) = I_i;
J(end+(1:nE)) = J_i;
V(end+(1:nE)) = rand(1)*V_i;
end
K = sparse(I,J,V,n,n);
fprintf('Stiffness Calculation Complete\nTime taken = %f s\n',toc);
如果您提前知道 test_evaluate_stiffness
的输出长度,您可以通过预分配数组 I
、J
和 V
来节省一些时间使用适当大小的 zeros
矩阵并使用类似以下内容设置它们:
I((i-1)*nE + (1:nE)) = ...
J((i-1)*nE + (1:nE)) = ...
V((i-1)*nE + (1:nE)) = ...
The biggest remaining computation, taking 11s, is the sparse operation
on the final I
,J
,V
vectors so I think we've taken it down to the bare
bones.
几乎...但最后一个技巧:如果您可以创建向量以使 J
升序排序,那么您将大大提高 sparse
调用的速度,大约提高 4 倍根据我的经验。
(如果 I
排序更容易,则创建转置矩阵 sparse(J,I,V)
并在之后取消转置。)
我有一个代码可以在循环中重复计算稀疏矩阵(准确地说,它执行了 13472 次计算)。这些稀疏矩阵中的每一个都是唯一的。
每次执行后,它将新计算的稀疏矩阵添加到原来的稀疏零矩阵中。
添加完所有 13742 个矩阵后,代码退出循环,程序终止。
添加稀疏矩阵时出现代码瓶颈。我制作了一个虚拟版本的代码,它表现出与我的真实代码相同的行为。它由一个 MATLAB 函数和下面给出的脚本组成。
(1) 生成稀疏矩阵的函数:
function out = test_evaluate_stiffness(n)
ind = randi([1 n*n],300,1);
val = rand(300,1);
[I,J] = ind2sub([n,n],ind);
out = sparse(I,J,val,n,n);
end
(2) 主脚本(程序)
% Calculate the stiffness matrix
n=1000;
K=sparse([],[],[],n,n,n^2);
tic
for i=1:13472
temp=rand(1)*test_evaluate_stiffness(n);
K=K+temp;
end
fprintf('Stiffness Calculation Complete\nTime taken = %f s\n',toc)
我对稀疏矩阵运算不是很熟悉,所以我可能在这里遗漏了一个关键点,这可能会大大加快我的代码速度。
我在代码中是否以合理的方式处理刚度矩阵的更新?有没有我应该使用稀疏的另一种方法可以得到更快的解决方案?
下面还提供了分析器报告:
如果您只需要这些矩阵的总和,而不是单独构建所有矩阵然后对它们求和,只需连接向量 I
、J
和 vals
并调用sparse
只有一次。如果[I,J]
中有重复行[i,j]
,对应的值S(i,j)
会自动求和,所以代码是绝对等价的。由于调用 sparse
涉及对排序算法的内部调用,因此您可以节省 13742-1 次中间排序,并且只需要一次即可。
这涉及将 test_evaluate_stiffness
的签名更改为输出 [I,J,val]
:
function [I,J,val] = test_evaluate_stiffness(n)
并删除行 out = sparse(I,J,val,n,n);
.
然后您将其他功能更改为:
n = 1000;
[I,J,V] = deal([]);
tic;
for i = 1:13472
[I_i, J_i, V_i] = test_evaluate_stiffness(n);
nE = numel(I_i);
I(end+(1:nE)) = I_i;
J(end+(1:nE)) = J_i;
V(end+(1:nE)) = rand(1)*V_i;
end
K = sparse(I,J,V,n,n);
fprintf('Stiffness Calculation Complete\nTime taken = %f s\n',toc);
如果您提前知道 test_evaluate_stiffness
的输出长度,您可以通过预分配数组 I
、J
和 V
来节省一些时间使用适当大小的 zeros
矩阵并使用类似以下内容设置它们:
I((i-1)*nE + (1:nE)) = ...
J((i-1)*nE + (1:nE)) = ...
V((i-1)*nE + (1:nE)) = ...
The biggest remaining computation, taking 11s, is the sparse operation on the final
I
,J
,V
vectors so I think we've taken it down to the bare bones.
几乎...但最后一个技巧:如果您可以创建向量以使 J
升序排序,那么您将大大提高 sparse
调用的速度,大约提高 4 倍根据我的经验。
(如果 I
排序更容易,则创建转置矩阵 sparse(J,I,V)
并在之后取消转置。)