- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我试图使下面的代码更快地将两个变量(我们需要重用的变量)保留在寄存器中或比高速缓存更近的任何位置。该代码在位置idx
处将数组中的三个相邻元素相加在一起。
void stencil(double * input, double * output){
unsigned int idx = 1;
output[0] = input[0] + input[1];
for(; idx < SIZE - 1; idx++){
output[idx] = input[idx-1] + input[idx] + input[idx+1];
}
output[idx] = input[idx-1] + input[idx];
}
void stencil(double * input, double * output){
unsigned int idx = 0;
double x , y = 0, z;
z = input[idx];
for(; idx < SIZE - 1; idx++){
x = y;
y = z;
z = input[idx + 1];
output[idx] = x + y + z;
}
output[idx] = y + z;
}
AMD Opteron(tm) Processor 6320
CPU上使用gcc,并使用以下标志编译代码:
-march=native -O3 -Wall -std=c99
。
-march=native
标志如下所示:
stencil:
.LFB7:
.cfi_startproc
subl $1, %edx
movsd (%rdi), %xmm1
je .L4
movq %rsi, %rcx
xorpd %xmm0, %xmm0
xorl %eax, %eax
jmp .L3
.p2align 4,,10
.p2align 3
.L6:
movapd %xmm1, %xmm0
movapd %xmm2, %xmm1
.L3:
addl $1, %eax
addsd %xmm1, %xmm0
addq $8, %rcx
movl %eax, %r8d
movsd (%rdi,%r8,8), %xmm2
leaq 0(,%r8,8), %r9
addsd %xmm2, %xmm0
movsd %xmm0, -8(%rcx)
cmpl %edx, %eax
jne .L6
.L2:
addsd %xmm2, %xmm1
movsd %xmm1, (%rsi,%r9)
ret
.L4:
movapd %xmm1, %xmm2
xorl %r9d, %r9d
xorpd %xmm1, %xmm1
jmp .L2
-march=native
标志看起来像这样:
stencil:
.LFB20:
.cfi_startproc
vmovsd (%rdi), %xmm1
vxorpd %xmm0, %xmm0, %xmm0
leaq 144(%rdi), %rdx
leaq 136(%rsi), %rax
xorl %ecx, %ecx
.p2align 4,,10
.p2align 3
.L2:
vaddsd %xmm1, %xmm0, %xmm0
vmovsd -136(%rdx), %xmm4
prefetcht0 (%rdx)
addl $8, %ecx
prefetchw (%rax)
addq $64, %rdx
addq $64, %rax
vaddsd %xmm1, %xmm4, %xmm1
vaddsd %xmm4, %xmm0, %xmm0
vmovsd %xmm0, -200(%rax)
vmovsd -192(%rdx), %xmm3
vaddsd %xmm3, %xmm1, %xmm1
vaddsd %xmm3, %xmm4, %xmm4
vmovsd %xmm1, -192(%rax)
vmovsd -184(%rdx), %xmm2
vaddsd %xmm2, %xmm4, %xmm4
vaddsd %xmm2, %xmm3, %xmm3
vmovsd %xmm4, -184(%rax)
vmovsd %xmm4, -184(%rax)
vmovsd -176(%rdx), %xmm0
vaddsd %xmm0, %xmm3, %xmm3
vaddsd %xmm0, %xmm2, %xmm2
vmovsd %xmm3, -176(%rax)
vmovsd -168(%rdx), %xmm1
vaddsd %xmm1, %xmm2, %xmm2
vaddsd %xmm1, %xmm0, %xmm0
vmovsd %xmm2, -168(%rax)
vmovsd -160(%rdx), %xmm2
vaddsd %xmm2, %xmm0, %xmm0
vaddsd %xmm2, %xmm1, %xmm1
vmovsd %xmm0, -160(%rax)
vmovsd -152(%rdx), %xmm0
vaddsd %xmm0, %xmm1, %xmm1
vaddsd %xmm0, %xmm2, %xmm2
vmovsd %xmm1, -152(%rax)
vmovsd -144(%rdx), %xmm1
vaddsd %xmm1, %xmm2, %xmm2
vmovsd %xmm2, -144(%rax)
cmpl $1399999992, %ecx
jne .L2
movabsq $11199999944, %rdx
movabsq $11199999936, %rcx
addq %rdi, %rdx
addq %rsi, %rcx
xorl %eax, %eax
jmp .L3
.p2align 4,,7
.p2align 3
.L4:
vmovaps %xmm2, %xmm1
.L3:
vaddsd %xmm0, %xmm1, %xmm0
vmovsd (%rdx,%rax), %xmm2
vaddsd %xmm2, %xmm0, %xmm0
vmovsd %xmm0, (%rcx,%rax)
addq $8, %rax
vmovaps %xmm1, %xmm0
cmpq $56, %rax
jne .L4
vaddsd %xmm2, %xmm1, %xmm1
movabsq $11199999992, %rax
vmovsd %xmm1, (%rsi,%rax)
ret
最佳答案
这是个好主意,但是如果编译器知道它的安全性,它们已经为您完成了。使用double *restrict output
和const double *restrict input
保证存储到output[]
的编译器不会更改将从input[]
读取的内容。
但是,使用SIMD进行自动矢量化是更为重要的优化,每条指令产生2或4个double
结果。检查重叠后,GCC和ICC将在-O3
执行此操作。 (但是clang无法自动向量化它,只是使用标量[v]addsd
展开以避免不必要的重载。
不幸的是,您的优化版本无法实现自动矢量化! (这是编译器的错误,即,当它知道输出不重叠时,错过了优化错误,因此从内存中重新读取源代码是等效的)。
看起来gcc在原始版本和-O3 -march=native
上做得非常好(尤其是在针对Intel进行调优时,值得使用AVX的更宽的矢量。)我从3个未对齐的负载和2个并行计算4个double
结果vaddpd ymm
。
它在使用向量化循环之前检查重叠。您可以使用double *restrict output
和input
保证指针不会重叠,因此不需要回退循环。
L1d缓存带宽在现代CPU上非常出色;重新加载相同的数据不是什么大问题(每个时钟2次加载)。指令吞吐量更成问题。内存源addsd
与将数据保存在寄存器中相比,花费不多。
如果使用128位向量进行向量化,则将in[idx+1..2]
向量保留为下一次迭代用作in[idx+ -1..1]
向量是有意义的。海湾合作委员会实际上就是这样做的。
但是,当每条指令产生4个结果时,一次迭代的3个输入向量都不会对下一次迭代直接有用。不过,通过改组节省一些加载端口带宽以从加载结果中创建3个向量之一可能会很有用。如果尝试使用__m256d
内在函数进行向量化,我会尝试一下。或带有128位float
向量的__m128
。
#define SIZE 1000000
void stencil_restrict(double *restrict input, double *restrict output)
{
int idx = 1;
output[0] = input[0] + input[1];
for(; idx < SIZE - 1; idx++){
output[idx] = input[idx-1] + input[idx] + input[idx+1];
}
output[idx] = input[idx-1] + input[idx];
}
gcc8.3 -O3 -Wall -std=c99 -march=broadwell -masm=intel
,
from the Godbolt compiler explorer编译为此asm(在这种情况下不需要
-ffast-math
,并且对内部循环没有影响。)
stencil_restrict:
vmovsd xmm0, QWORD PTR [rdi]
vaddsd xmm0, xmm0, QWORD PTR [rdi+8]
xor eax, eax
vmovsd QWORD PTR [rsi], xmm0 # first iteration
### Main loop
.L12:
vmovupd ymm2, YMMWORD PTR [rdi+8+rax] # idx +0 .. +3
vaddpd ymm0, ymm2, YMMWORD PTR [rdi+rax] # idx -1 .. +2
vaddpd ymm0, ymm0, YMMWORD PTR [rdi+16+rax] # idx +1 .. +4
vmovupd YMMWORD PTR [rsi+8+rax], ymm0 # store idx +0 .. +3
add rax, 32 # byte offset += 32
cmp rax, 7999968
jne .L12
# cleanup of last few elements
vmovsd xmm1, QWORD PTR [rdi+7999976]
vaddsd xmm0, xmm1, QWORD PTR [rdi+7999968]
vaddsd xmm1, xmm1, QWORD PTR [rdi+7999984]
vunpcklpd xmm0, xmm0, xmm1
vaddpd xmm0, xmm0, XMMWORD PTR [rdi+7999984]
vmovups XMMWORD PTR [rsi+7999976], xmm0
vmovsd xmm0, QWORD PTR [rdi+7999984]
vaddsd xmm0, xmm0, QWORD PTR [rdi+7999992]
vmovsd QWORD PTR [rsi+7999992], xmm0
vzeroupper
ret
vaddpd
指令对SnB系列前端(包括Broadwell Xeon E5-2698 v4)的前端进行了2 oups的分层。
Micro fusion and addressing modes
vmovupd ymm2, YMMWORD PTR [rdi+8+rax] # 1 uop, no micro-fusion
vaddpd ymm0, ymm2, YMMWORD PTR [rdi+rax] # 2 uops. (micro-fused in decoders/uop cache, unlaminates)
vaddpd ymm0, ymm0, YMMWORD PTR [rdi+16+rax] # 2 uops. (ditto)
vmovupd YMMWORD PTR [rsi+8+rax], ymm0 # 1 uop (stays micro-fused, but can't use the port 7 store AGU)
add rax, 32 # 1 uop
cmp rax, 7999968 # 0 uops, macro-fuses with JNE
jne .L12 # 1 uop
[v]addpd
只能在端口1上运行,而
[v]mulpd
或FMA具有两倍的吞吐量。 (Skylake删除了专用的FP add单元,并与mul和fma相同地运行FP add。)因此,这也是每个迭代瓶颈2个周期。
3 * (8+3)/8 = 4.125
个负载。我不知道是否需要重播存储地址。可能不是;只是重要的是数据从存储缓冲区提交到L1d的时间,与存储地址或存储数据位无关。 (只要不跨越4k边界,输出对齐就会发生这种情况)。
output[1]
以外的任何内容的输出对齐方式为32字节对齐方式。 asm将
output[0]
存储在循环外部,然后有效地执行
output[i*4 + 1]
,因此,每个其他存储都将是一个缓存行拆分。
gcc8.3 -O3 -Wall -std=c99 -march=broadwell -mno-avx
的内部循环
# prologue to reach an alignment boundary somewhere?
.L12:
movupd xmm2, XMMWORD PTR [rdi+rax]
movupd xmm1, XMMWORD PTR [rdi+8+rax]
addpd xmm0, xmm2
addpd xmm0, xmm1
movups XMMWORD PTR [rsi+rax], xmm0
add rax, 16
movapd xmm0, xmm1 # x = z
cmp rax, 7999992
jne .L12
# prologue to reach an alignment boundary so one load can be aligned.
# r10=input and r9=input+8 or something like that
# r8=output
.L18: # do {
movupd xmm0, XMMWORD PTR [r10+rdx]
add ecx, 1
addpd xmm0, xmm1 # x+y
movapd xmm1, XMMWORD PTR [r9+rdx] # z for this iteration, x for next
addpd xmm0, xmm1 # (x+y) + z
movups XMMWORD PTR [r8+rdx], xmm0
add rdx, 16
cmp ecx, r11d
jb .L18 # } while(i < max);
movupd xmm0
加载并使用
vaddpd xmm0, xmm1, [r10+rdx]
。
addpd
中找到已知的对齐后,也无法利用将负载折叠到
input
的内存操作数中的优势:
double in[SIZE+10];
之类的静态缓冲区,gcc会使用非索引寻址模式创建一个循环版本。这样可以使它多次循环运行,从〜800ms加速到〜700ms,SIZE = 1000。稍后将更新更多详细信息。
关于c - 优化简单的模具操作,将变量保存在寄存器中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54843964/
我正在尝试在复制到构建目录的 Stencil 组件中使用第三方脚本。我打算在各种网站上使用独立组件。我不是在构建 Stencil 应用程序。 模板配置.ts export const config:
我正在尝试应用来自包含 stencilJS 组件的网站的样式……但不知道如何操作。 import { Component } from '@stencil/core'; @Component({
我正在按照本教程Openalpr for Windows尝试在Windows 10(x64)笔记本电脑上安装Openalpr,但是当我在上运行时,为面向x64的工具链v120 构建发行版时,在 Win
我正在使用 StencilJS 开发一个网络组件库,但我在使用 CSS + 选择器时遇到了问题。我有一个 Breadcrumb web 组件,它将包含多个 breadcrumb 元素(以及 web 组
我已经从 http://developer.android.com/design/downloads/index.html 下载了整个包我想知道如何使用这些。我是将它放在 android sdk 文件
我们的平台建立在带有 Web 组件的微前端架构上。我们对其中一些使用 Stencil,这意味着我们在一个网页中有多个 Stencil 应用程序。此外,我们还有一个 UI 库,也是用 Stencil 构
有没有人有一套很好的用于 MS SQL Server 的 Visio 模板? 最佳答案 某些版本的 Visio 内置了数据库支持。 Visio 的 MVP 站点上有几个链接: 新网址:http://v
我是一名优秀的程序员,十分优秀!