[英]sse C++ memory commands
SSE组件具有SQRTPS命令。
SQRTPS命令具有2个版本:
SQRTPS xmm1, xmm2
SQRTPS xmm1, m128
gcc / clang / vs(所有)编译器具有辅助函数_mm_sqrt_ps
。
但是_mm_sqrt_ps
仅适用于预加载的xmm(使用_mm_set_ps / _mm_load_ps)。
例如,从Visual Studio: http : //msdn.microsoft.com/zh-cn/library/vstudio/8z67bwwk%28v=vs.100%29.aspx
我的期望:
__attribute__((aligned(16))) float data[4];
__attribute__((aligned(16))) float result[4];
asm{
sqrtps xmm0, data // DIRECTLY FROM MEMORY
movaps result, xmm0
}
我所拥有的(C语言):
__attribute__((aligned(16))) float data[4];
__attribute__((aligned(16))) float result[4];
auto xmm = _mm_load_ps(&data) // or _mm_set_ps
xmm = _mm_sqrt_ps(xmm);
_mm_store_ps(&result[0], xmm);
(在asm中):
movaps xmm1, data
sqrtps xmm0, xmm1 // FROM REGISTER
movaps result, xmm0
换句话说,我想看到这样的东西:
__attribute__((aligned(16))) float data[4];
__attribute__((aligned(16))) float result[4];
auto xmm = _mm_sqrt_ps(data); // DIRECTLY FROM MEMORY, no need to load (because there is such instruction)
_mm_store_ps(&result[0], xmm);
快速研究:我制作了以下文件,名为mysqrt.cpp
:
#include <pmmintrin.h>
extern "C" __m128 MySqrt(__m128* a) {
return _mm_sqrt_ps(a[1]);
}
尝试gcc,即g++4.8 -msse3 -O3 -S mysqrt.cpp && cat mysqrt.s
:
_MySqrt:
LFB526:
sqrtps 16(%rdi), %xmm0
ret
clang++3.6 -msse3 -O3 -S mysqrt.cpp && cat mysqrt.s
( clang++3.6 -msse3 -O3 -S mysqrt.cpp && cat mysqrt.s
):
_MySqrt: ## @MySqrt
.cfi_startproc
## BB#0: ## %entry
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
sqrtps 16(%rdi), %xmm0
popq %rbp
retq
不了解VS,但如果需要的话,至少gcc和clang似乎都可以生成sqrtps
内存版本。
UPDATE函数用法示例:
#include <iostream>
#include <pmmintrin.h>
extern "C" __m128 MySqrt(__m128* a);
int main() {
__m128 x[2];
x[1] = _mm_set_ps1(4);
__m128 y = MySqrt(x);
std::cout << y[0] << std::endl;
}
// output:
2
更新2:关于您的代码,您应该这样做:
auto xmm = _mm_sqrt_ps(*reinterpret_cast<__m128*>(data));
当然,这需要您自担风险,您应保证data
包含有效的__m128
并正确对齐。
我认为您误解了原始_mm_sqrt_ps(__m128)
提供的接口。 这里的参数类型可以是保存在存储器或寄存器中的变量。 扩展类型__m128
行为类似于任何常规的内置类型,例如double
,并且未绑定到xmm寄存器,但也可以存储在内存中。
编辑除非您使用asm
,否则编译器将确定是否以及何时将变量加载到寄存器中或保留在内存中。 因此,在以下代码段中
__m128 foo(const __m128 x, const __m128*y, std::size_t n)
{
__m128 result = _mm_set_ps(1.0);
while(n--)
result = _mm_mul_ps(result,_mm_add_ps(x,_mm_sqrt_ps(*y++)));
return result;
}
哪些变量存储在寄存器中由编译器决定。 我认为编译器会将x
和result
放入xmm寄存器,但直接从内存获取*y
。
您的问题的答案是,您至少无法使用内部函数来控制this,至少对于对齐的负载而言。 由编译器决定是否使用SQRTPS xmm1,xmm2或SQRTPS xmm1,m128。 如果要100%确定,则必须以汇编形式编写。 我认为,这是内在函数的不足之一(至少在当前已实现)。
一些代码可以帮助解释这一点。
我们可以得到GCC(带有-O3的64位)来使用对齐和不对齐的负载生成两个版本
float x[4], y[4]
__m128 x4 = _mm_loadu_ps(x);
__m128 y4 = _mm_sqrt_ps(x4);
_mm_storeu_ps(y,y4);
这给出了(使用Intel语法)
movups xmm0, XMMWORD PTR [rdx]
sqrtps xmm0, xmm0
但是,如果我们进行对齐的荷载,则会得到另一种形式
float x[4], y[4]
__m128 x4 = _mm_load_ps(x);
__m128 y4 = _mm_sqrt_ps(x4);
_mm_storeu_ps(y,y4);
这将载荷和平方根组合成一条指令
sqrtps xmm0, XMMWORD PTR [rax]
大多数人会说“信任编译器”。 我不同意。 如果使用内在函数,则应假定您知道自己在做什么,而不是编译器。 这是msvc和gcc在高度优化的矩阵multp之间的性能差异示例,其中GCC选择了一种形式,而MSVC选择了另一种形式(用于乘法而不是sqrt),性能差异。
因此,再次重申,如果您使用对齐的负载,则只能祈祷编译器可以执行所需的操作。 然后也许在下一版的编译器中它做了一些不同的工作...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.