作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有 n x n 下三角矩阵 A 的压缩稀疏列 (csc) 表示,主对角线上有零,并且想求解 b
(A + I)' * x = b
void backsolve(const int*__restrict__ Lp,
const int*__restrict__ Li,
const double*__restrict__ Lx,
const int n,
double*__restrict__ x) {
for (int i=n-1; i>=0; --i) {
for (int j=Lp[i]; j<Lp[i+1]; ++j) {
x[i] -= Lx[j] * x[Li[j]];
}
}
}
b
通过参数
x
传入,并被解覆盖。
Lp
,
Li
,
Lx
分别是稀疏矩阵的标准 csc 表示中的行、索引和数据指针。这个函数是程序中的顶级热点,用行
x[i] -= Lx[j] * x[Li[j]];
gcc-8.3 -O3 -mfma -mavx -mavx512f
给
backsolve(int const*, int const*, double const*, int, double*):
lea eax, [rcx-1]
movsx r11, eax
lea r9, [r8+r11*8]
test eax, eax
js .L9
.L5:
movsx rax, DWORD PTR [rdi+r11*4]
mov r10d, DWORD PTR [rdi+4+r11*4]
cmp eax, r10d
jge .L6
vmovsd xmm0, QWORD PTR [r9]
.L7:
movsx rcx, DWORD PTR [rsi+rax*4]
vmovsd xmm1, QWORD PTR [rdx+rax*8]
add rax, 1
vfnmadd231sd xmm0, xmm1, QWORD PTR [r8+rcx*8]
vmovsd QWORD PTR [r9], xmm0
cmp r10d, eax
jg .L7
.L6:
sub r11, 1
sub r9, 8
test r11d, r11d
jns .L5
ret
.L9:
ret
vmovsd QWORD PTR [r9], xmm0
最佳答案
这应该在很大程度上取决于矩阵的精确稀疏模式和所使用的平台。我用 gcc 8.3.0
测试了一些东西和编译器标志 -O3 -march=native
(在我的 CPU 上是 -march=skylake
)在 this matrix 的下三角形上维度为 3006,具有 19554 个非零条目。希望这有点接近您的设置,但无论如何我希望这些可以让您知道从哪里开始。
我使用的时间安排 google/benchmark与 this source file .它定义了 benchBacksolveBaseline
它对问题和 benchBacksolveOptimized
中给出的实现进行了基准测试它对建议的“优化”实现进行了基准测试。还有benchFillRhs
它分别对两者中使用的函数进行基准测试,以便为右侧生成一些并非完全无关紧要的值。要获得“纯”反向求解的时间,即 benchFillRhs
的时间需要减去。
1.严格向后迭代
实现中的外循环向后迭代列,而内循环向前迭代当前列。似乎向后遍历每一列也会更加一致:
for (int i=n-1; i>=0; --i) {
for (int j=Lp[i+1]-1; j>=Lp[i]; --j) {
x[i] -= Lx[j] * x[Li[j]];
}
}
------------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------------
benchFillRhs 2737 ns 2734 ns 5120000
benchBacksolveBaseline 17412 ns 17421 ns 829630
benchBacksolveOptimized 16046 ns 16040 ns 853333
i < Li[j]
.因此我们知道
x[Li[j]]
不会因
x[i]
的变化而变化在内循环中。我们可以通过使用临时变量将这些知识放入我们的实现中:
for (int i=n-1; i>=0; --i) {
double xi_temp = x[i];
for (int j=Lp[i+1]-1; j>=Lp[i]; --j) {
xi_temp -= Lx[j] * x[Li[j]];
}
x[i] = xi_temp;
}
gcc 8.3.0
将存储从内部循环内部移动到内存,直接在其结束之后(
https://godbolt.org/z/vM4gPD )。我的系统上测试矩阵的基准测试显示了一个小的改进:
------------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------------
benchFillRhs 2737 ns 2740 ns 5120000
benchBacksolveBaseline 17410 ns 17418 ns 814545
benchBacksolveOptimized 15155 ns 15147 ns 887129
clang
在第一个建议的代码更改后,已经开始展开循环,
gcc 8.3.0
还没有。因此,让我们通过额外传递
-funroll-loops
来尝试一下。 .
------------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------------
benchFillRhs 2733 ns 2734 ns 5120000
benchBacksolveBaseline 15079 ns 15081 ns 953191
benchBacksolveOptimized 14392 ns 14385 ns 963441
gcc
8 次展开可能走得有点远。对于我的设置,我实际上只需一个简单的手动展开就可以做得更好。所以放下旗帜
-funroll-loops
再次并使用以下内容实现展开:
for (int i=n-1; i>=0; --i) {
const int col_begin = Lp[i];
const int col_end = Lp[i+1];
const bool is_col_nnz_odd = (col_end - col_begin) & 1;
double xi_temp = x[i];
int j = col_end - 1;
if (is_col_nnz_odd) {
xi_temp -= Lx[j] * x[Li[j]];
--j;
}
for (; j >= col_begin; j -= 2) {
xi_temp -= Lx[j - 0] * x[Li[j - 0]] +
Lx[j - 1] * x[Li[j - 1]];
}
x[i] = xi_temp;
}
------------------------------------------------------------------
Benchmark Time CPU Iterations
------------------------------------------------------------------
benchFillRhs 2728 ns 2729 ns 5090909
benchBacksolveBaseline 17451 ns 17449 ns 822018
benchBacksolveOptimized 13440 ns 13443 ns 1018182
关于c++ - 优化稀疏下三角线性系统的反向求解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60232977/
我有一个绕其 3 轴旋转的立方体,当 key[a] == true 时,它会向左旋转,就好像它正在滚动一样。将立方体向任何方向旋转 45 度,将其向后旋转 90 度,以获得继续的错觉。这将保持 3
我是一名优秀的程序员,十分优秀!