- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想在 gcc(AT&T 语法)中运行以下代码(Intel 语法)。
; float a[128], b[128], c[128];
; for (int i = 0; i < 128; i++) a[i] = b[i] + c[i];
; Assume that a, b and c are aligned by 32
xor ecx, ecx ; Loop counter i = 0
L: vmovaps ymm0, [b+rcx] ; Load 8 elements from b
vaddps ymm0,ymm0,[c+rcx] ; Add 8 elements from c
vmovaps [a+rcx], ymm0 ; Store result in a
add ecx,32 ; 8 elements * 4 bytes = 32
cmp ecx, 512 ; 128 elements * 4 bytes = 512
jb L ;Loop
代码来自Optimizing subroutines in assembly language .
到目前为止我编写的代码是:
static inline void addArray(float *a, float *b, float *c) {
__asm__ __volatile__ (
"nop \n"
"xor %%ecx, %%ecx \n" //;Loop counter set to 0
"loop: \n\t"
"vmovaps %1, %%ymm0 \n" //;Load 8 elements from b <== WRONG
"vaddps %2, %%ymm0, %%ymm0 \n" //;Add 8 elements from c <==WRONG
"vmovaps %%ymm0, %0 \n" //;Store result in a
"add 0x20, %%ecx \n" //;8 elemtns * 4 bytes = 32 (0x20)
"cmp 0x200,%%ecx \n" //;128 elements * 4 bytes = 512 (0x200)
"jb loop \n" //;Loop"
"nop \n"
: "=m"(a) //Outputs
: "m"(b), "m"(c) //Inputs
: "%ecx","%ymm0" //Modifies ECX and YMM0
);
}
标记为“错误”的行生成:(除了 gdb 反汇编)
0x0000000000000b78 <+19>: vmovaps -0x10(%rbp),%ymm0
0x0000000000000b7d <+24>: vaddps -0x18(%rbp),%ymm0,%ymm0
我想要得到这样的东西(我猜):
vmovaps -0x10(%rbp,%ecx,%0x8),%ymm0
但我不知道如何指定 %ecx 作为我的索引寄存器。
你能帮我一下吗?
编辑
我已经尝试过(%1,%%ecx):
__asm__ __volatile__ (
"nop \n"
"xor %%ecx, %%ecx \n" //;Loop counter set to 0
"loop: \n\t"
"vmovaps (%1, %%rcx), %%ymm0 \n" //;Load 8 elements from b <== MODIFIED HERE
"vaddps %2, %%ymm0, %%ymm0 \n" //;Add 8 elements from c
"vmovaps %%ymm0, %0 \n" //;Store result in a
"add 0x20, %%ecx \n" //;8 elemtns * 4 bytes = 32 (0x20)
"cmp 0x200,%%ecx \n" //;128 elements * 4 bytes = 512 (0x200)
"jb loop \n" //;Loop"
"nop \n"
: "=m"(a) //Outputs
: "m"(b), "m"(c) //Inputs
: "%ecx","%ymm0" //Modifies ECX and YMM0
);
我得到了:
inline1.cpp: Assembler messages:
inline1.cpp:90: Error: found '(', expected: ')'
inline1.cpp:90: Error: junk `(%rbp),%rcx)' after expression
最佳答案
我认为不可能将其字面翻译为 GAS 内联汇编。在AT&T语法中,语法为:
displacement(base register, offset register, scalar multiplier)
这会产生类似于:
movl -4(%ebp, %ecx, 4), %eax
或者根据您的情况:
vmovaps -16(%rsp, %ecx, 0), %ymm0
问题是,当您使用内存约束 (m
) 时,无论您在何处编写 %n
(其中 n
是输入/输出的数量):
-16(%rsp)
没有办法将上面的内容操纵成你真正想要的形式。你可以写:
(%1, %%rcx)
但这会产生:
(-16(%rsp),%rcx)
这显然是错误的。无法获取这些括号内的偏移寄存器(它所属的位置),因为 %n
正在发出整个 -16(%rsp)
作为一个 block 。
当然,这并不是真正的问题,因为您编写内联汇编是为了获得速度,并且从内存加载并没有什么速度。您应该将输入放在寄存器中,并且当您对输入/输出 (r
) 使用寄存器约束时,就不会出现问题。请注意,这将需要稍微修改您的代码
内联汇编的其他问题包括:
$
开头。l
和 64 位的 q
。a
写入时,您会破坏内存,因此您应该有一个内存
破坏器。nop
指令完全没有意义。他们甚至没有调整分支目标。\n
) 之外,每一行实际上都应该以制表符 (\t
) 结尾,以便在您检查拆卸情况。这是我的代码版本:
void addArray(float *a, float *b, float *c) {
__asm__ __volatile__ (
"xorl %%ecx, %%ecx \n\t" // Loop counter set to 0
"loop: \n\t"
"vmovaps (%1,%%rcx), %%ymm0 \n\t" // Load 8 elements from b
"vaddps (%2,%%rcx), %%ymm0, %%ymm0 \n\t" // Add 8 elements from c
"vmovaps %%ymm0, (%0,%%rcx) \n\t" // Store result in a
"addl $0x20, %%ecx \n\t" // 8 elemtns * 4 bytes = 32 (0x20)
"cmpl $0x200, %%ecx \n\t" // 128 elements * 4 bytes = 512 (0x200)
"jb loop" // Loop"
: // Outputs
: "r" (a), "r" (b), "r" (c) // Inputs
: "%ecx", "%ymm0", "memory" // Modifies ECX, YMM0, and memory
);
}
这会导致编译器发出以下内容:
addArray(float*, float*, float*):
xorl %ecx, %ecx
loop:
vmovaps (%rsi,%rcx), %ymm0 # b
vaddps (%rdx,%rcx), %ymm0, %ymm0 # c
vmovaps %ymm0, (%rdi,%rcx) # a
addl $0x20, %ecx
cmpl $0x200, %ecx
jb loop
vzeroupper
retq
或者,采用更熟悉的英特尔语法:
addArray(float*, float*, float*):
xor ecx, ecx
loop:
vmovaps ymm0, YMMWORD PTR [rsi + rcx]
vaddps ymm0, ymm0, YMMWORD PTR [rdx + rcx]
vmovaps YMMWORD PTR [rdi + rcx], ymm0
add ecx, 32
cmp ecx, 512
jb loop
vzeroupper
ret
在 System V 64 位调用约定中,前三个参数在 rdi
、rsi
和 rdx
寄存器中传递,因此代码不需要将参数移动到寄存器中——它们已经在那里了。
但是您没有充分利用输入/输出约束。您不需要将 rcx
用作计数器。您也不需要使用 ymm0 作为暂存寄存器。如果让编译器选择要使用的空闲寄存器,将使代码更加高效。您也不需要提供明确的破坏者列表:
#include <stdint.h>
#include <x86intrin.h>
void addArray(float *a, float *b, float *c) {
uint64_t temp = 0;
__m256 ymm;
__asm__ __volatile__(
"loop: \n\t"
"vmovaps (%3,%0), %1 \n\t" // Load 8 elements from b
"vaddps (%4,%0), %1, %1 \n\t" // Add 8 elements from c
"vmovaps %1, (%2,%0) \n\t" // Store result in a
"addl $0x20, %0 \n\t" // 8 elemtns * 4 bytes = 32 (0x20)
"cmpl $0x200, %0 \n\t" // 128 elements * 4 bytes = 512 (0x200)
"jb loop" // Loop
: "+r" (temp), "=x" (ymm)
: "r" (a), "r" (b), "r" (c)
: "memory"
);
}
当然,正如评论中提到的,整个练习是浪费时间。 GAS 风格的内联汇编虽然功能强大,但正确编写极其非常困难(我什至不能 100% 肯定我的代码是正确的!),因此您不应该使用内联汇编编写任何内容你绝对不必这样做。 当然不是您必须这样做的情况 - 编译器会自动优化加法循环:
void addArray(float *a, float *b, float *c) {
for (int i = 0; i < 128; i++) a[i] = b[i] + c[i];
}
使用 -O2
和 -mavx2
,GCC 将其编译为以下内容:
addArray(float*, float*, float*):
xor eax, eax
.L2:
vmovss xmm0, DWORD PTR [rsi+rax]
vaddss xmm0, xmm0, DWORD PTR [rdx+rax]
vmovss DWORD PTR [rdi+rax], xmm0
add rax, 4
cmp rax, 512
jne .L2
rep ret
嗯,这看起来非常熟悉,不是吗?公平地说,它不像您的代码那样进行矢量化。您可以使用 -O3
或 -ftree-vectorize
获得该值,但您还可以获得 a lot more code generated ,所以我需要一个基准来说服我,它实际上更快并且值得代码大小的爆炸式增长。但其中大部分是为了处理输入未对齐的情况 - if you indicate that it is aligned and that the pointer is restrict
ed, that solves these problems and improves the code generation substantially 。请注意,它完全展开循环,并对加法进行矢量化。
关于gcc - 使用 gcc 使用 YMM 指令添加数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41921508/
是否存在任何 SIMD/向量寄存器指令,其中 ymm 寄存器是在通用寄存器(或 SIMD 寄存器)中指定的,而不是在指令本身中指定的? 本质上,我想做的是编写一个函数,将任何一系列连续的 ymm 寄存
从 SIMD 寄存器加载和存储生成用途寄存器的最佳方式是什么?到目前为止,我一直使用堆栈作为临时的。例如, mov [rsp + 0x00], r8 mov [rsp + 0x08], r9 mov
考虑: movdqa xmm0, xmmword ptr [rcx] movdqa xmm1, xmmword ptr [rcx + 16] movdqa xmm2, xmmword ptr [rcx
幸运的是,PTEST 不会影响进位标志,而只会设置(相当尴尬的)ZF。还会影响 CF 和 ZF。 我提出了以下序列来测试大量值,但我对糟糕的运行时间感到不满意。 Laten
考虑: movdqa xmm0, xmmword ptr [rcx] movdqa xmm1, xmmword ptr [rcx + 16] movdqa xmm2, xmmword ptr [rcx
如何在最少的时钟周期内将 YMM 寄存器的最低 64 位设置为某个常数?我知道可以使用 SSE 指令以及 AVX 指令 VBROADCASTSD 执行此操作的各种方法,但我不确定哪种方法会产生最佳结果
我想在 gcc(AT&T 语法)中运行以下代码(Intel 语法)。 ; float a[128], b[128], c[128]; ; for (int i = 0; i : vmovaps -
这个问题适用于 Haswell 上带有 XMM/YMM 寄存器的打包、单精度浮点运算。 所以根据真棒,真棒table由 Agner Fog 汇总,我知道 MUL 可以在端口 p0 和 p1 上完成(r
伙计们,假设我有一个模板函数: template Vector* Vector::overwrite(const Vector* copy) { this->_normalized = co
我有 2 个变量来模拟 X86 XMM 和 YMM,如下所示: uint64_t xmm_value[2]; uint64_t ymm_value[4]; 现在我想使用内联汇编来读写 XMM/YMM
考虑 x86 中的以下循环: ; on entry, rdi has the number of iterations .top: ; some magic happens here to calcu
我有三个 ymm 寄存器 - ymm4、ymm5 和 ymm6 - 包含 double (qword) float : ymm4: 73 144 168 41 ymm5: 144 348 2
我有三个 ymm 寄存器 - ymm4、ymm5 和 ymm6 - 包含 double (qword) float : ymm4: 73 144 168 41 ymm5: 144 348 2
我正在使用英特尔 AVX 内在函数实现粒子系统。当粒子的 Y 位置小于或等于零时,我想重置粒子。 粒子系统按照这样的 SOA 模式排序: class ParticleSystem { priv
如何将单个 32 位浮点加载/转换为 AVX 256 ymm 寄存器,以便所有 8 个 float 都来自单个源 float ? 之前我使用 AVX 128 xmm 寄存器将单个 float 加载到
好像是recurring problem许多英特尔处理器(直到 Skylake,除非我错了)在将 AVX-256 指令与 SSE 指令混合时表现出较差的性能。 根据 Intel's documenta
我有一个 x86_64 例程,如果成功,它最终在 YMM 寄存器中以零结束,如果 YMM 寄存器,我想返回非零值。 我有一种方法可以通过清除另一个 YMM 寄存器,针对那个寄存器对我的寄存器进行 VP
xmm有什么区别和 ymm注册? 我以为xmm用于 SSE,和 ymm用于 AVX,但我写了一些代码: vmovups ymm1, [r9] vcvtss2si rcx, ymm
有什么办法可以交换 256 位 AVX(YMM) 寄存器中的中间两个 64 位吗? 我知道我们可以利用 VPERM2F128 来交换低 128 位和高 128 位,而 vshufps 似乎只能在高 1
几乎就像标题所说的那样,我需要一种方法来将 256-avx-register 寄存器中所有元素的位置移动/混洗 N 个位置。我发现的所有关于此的使用 32 或 64 位值(__builtin_ia32
我是一名优秀的程序员,十分优秀!