C++ 将单个浮点移动到xmm寄存器

C++ 将单个浮点移动到xmm寄存器,c++,gcc,x86,sse,simd,C++,Gcc,X86,Sse,Simd,我想将存储在一个xmm寄存器中的数据乘以一个浮点值,并将结果保存在一个xmm寄存器中。 我做了一个小图表来更好地解释它 正如你看到的,我有一个xmm0寄存器,里面有我的数据。例如,它包含: xmm0=| 4.0 | 2.5 | 3.5 | 2.0| 每个浮点存储在4个字节中。我的xmm0寄存器是128位,16字节长 效果不错。现在我想将0.5存储在另一个xmm寄存器中,例如xmm1,然后将这个寄存器与xmm0寄存器相乘,这样存储在xmm0中的每个值都与0.5相乘 我完全不知道如何在XMM寄存器

我想将存储在一个xmm寄存器中的数据乘以一个浮点值,并将结果保存在一个xmm寄存器中。 我做了一个小图表来更好地解释它

正如你看到的,我有一个xmm0寄存器,里面有我的数据。例如,它包含:

xmm0=| 4.0 | 2.5 | 3.5 | 2.0|

每个浮点存储在4个字节中。我的xmm0寄存器是128位,16字节长

效果不错。现在我想将0.5存储在另一个xmm寄存器中,例如xmm1,然后将这个寄存器与xmm0寄存器相乘,这样存储在xmm0中的每个值都与0.5相乘

我完全不知道如何在XMM寄存器中存储0.5。 有什么建议吗

:C++中的内嵌汇编程序。< /P>
void filter(image* src_image, image* dst_image)
{
    float* src = src_image->data;
    float* dst = dst_image->data;

    __asm__ __volatile__ (              
        "movaps (%%esi), %%xmm0\n"      
        // Multiply %xmm0 with a float, e.g. 0.5
        "movaps %%xmm0, (%%edi)\n" 

        :
        : "S"(src), "D"(dst) :  
    );
}

这是我想做的事情的简单版本。我得到了一些存储在浮点数组中的图像数据。指向这些数组的指针将传递给程序集。movaps获取数组的前4个浮点值,将这16个字节存储在xmm0寄存器中。在此之后,xmm0应乘以例如0.5。然后,“新”值将从edi存储在数组中。

假设您使用的是内部函数:
\uuuuum128 halfx4=\umm\uset1\ups(0.5f)

编辑:

您最好使用Intrinsic:

__m128 x = _mm_mul_ps(_mm_load_ps(src), halfx4);
_mm_store_ps(dst, x);
void filter(image* src_image, image* dst_image)
{
    const __m128 data = _mm_load_ps(src_image->data);
    const __m128 scaled = _mm_mul_ps(data, _mm_set1_ps(0.5f));
    _mm_store_ps(dst_image->data, scaled);
}

如果
src
dst
浮点数据没有16字节对齐,您需要:
\u mm\u loadu\u ps
\u mm\u storeu\u ps
-这两个数据比较慢。

您正在寻找MOVSS指令(它将单个精度浮点从内存加载到SSE寄存器的最低4字节中),然后是洗牌,用该值填充其他3个浮点数:

movss  (whatever), %%xmm1
shufps %%xmm1, %%xmm1, $0
这也是
\u mm\u set1\u ps
内在函数可能实现的方式。然后,您可以将这些SSE值相乘或执行任何您想要的操作:

mulps %%xmm1, %%xmm0

正如人们在评论中指出的那样,对于这种非常简单的操作,本质上最好使用intrinsic:

__m128 x = _mm_mul_ps(_mm_load_ps(src), halfx4);
_mm_store_ps(dst, x);
void filter(image* src_image, image* dst_image)
{
    const __m128 data = _mm_load_ps(src_image->data);
    const __m128 scaled = _mm_mul_ps(data, _mm_set1_ps(0.5f));
    _mm_store_ps(dst_image->data, scaled);
}
只有在编译器生成错误代码时(并且只有在向编译器供应商提交错误后),才应该求助于内联ASM

如果您真的想留在汇编中,有许多方法可以完成此任务。可以在ASM块外定义比例向量:

    const __m128 half = _mm_set1_ps(0.5f);
然后在ASM中使用它,就像使用其他操作数一样

如果您真的想:

    "mov    $0x3f000000, %%eax\n"  // encoding of 0.5
    "movd   %%eax,       %%xmm1\n" // move to xmm1
    "shufps $0, %%xmm1,  %%xmm1\n" // splat across all lanes of xmm1

这只是两种方法。还有很多其他的方法。你可以在英特尔指令集引用上花费一些质量时间。

< P>如果你使用GCC并使用EasySSE C++,你的代码可以如下:
void filter(float* src_image, float* dst_image){
    *(PackedFloat128*)dst_image =  Packefloat128(0.5) * (src_image+0);
}
这是假设给定的指针是16字节对齐的。
您可以检查assy代码以验证变量是否正确映射到向量寄存器。

以下是一种方法:

#include <stdio.h>
#include <stdlib.h>

typedef struct img {
    float *data;
} image_t;

image_t *src_image;
image_t *dst_image;
void filter(image_t*, image_t*);

int main()
{
    image_t src, dst;
    src.data = malloc(64);
    dst.data = malloc(64);
    src_image=&src;
    dst_image=&dst;

    *src.data = 42.0;
    filter(src_image, dst_image);

    printf("%f\n", *dst.data);
    free(src.data);
    free(dst.data);
    return 0;
}

void filter(image_t* src_image, image_t* dst_image)
{
    float* src = src_image->data;
    float* dst = dst_image->data;

    __asm__ __volatile__ (              
        "movd   %%esi, %%xmm0;"
        "movd   %%xmm0, %%edi;"
        : "=D" (*dst)
        : "S" (*src)
    );
}
#包括
#包括
类型定义结构img{
浮动*数据;
}图像;
图像*src\u图像;
图像t*dst图像;
空过滤器(图像_t*,图像_t*);
int main()
{
图像src、dst;
src.data=malloc(64);
dst.data=malloc(64);
src_image=&src;
dst_图像=&dst;
*src.data=42.0;
过滤器(src_图像、dst_图像);
printf(“%f\n”,*dst.data);
免费(src.数据);
免费(dst.数据);
返回0;
}
无效过滤器(图像*src\U图像,图像*dst\U图像)
{
float*src=src\u image->data;
浮点*dst=dst_图像->数据;
__asm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
movd%%esi,%%xmm0
movd%%xmm0,%%edi
:“=D”(*dst)
:“S”(*src)
);
}

我可以在内联汇编程序中使用它吗?您可以将其作为操作数传递。您使用的是什么编译器?我使用的是用于linux的gcc。“将其作为操作数传递”是什么意思?您需要显示内联汇编块,特别是它的输入和输出。现在最好使用内部函数。这样,您的代码是独立于编译器的,并且可以自动分配寄存器。+1 MOVD加上立即数比我的版本要好得多,我的版本只能从内存加载。没有考虑过使用整数运算。@ChristianRau:我想我不会说它“好得多”;这将取决于周围的环境。它们只是不同的方法。我认为常数可能更好,因为它直接来自指令缓存,不必从静态内存获取。但你是对的,这要看情况而定,反正我不是一个机器专家。@Copa:我恰好知道这个值;我写了很多低级的FP代码。IEEE-754单精度数字具有8位指数字段和23位有效位字段。指数场的偏差为127。因此
1.0f=2^0
127+0@Christian:从通用寄存器加载xmm寄存器可能会产生比一级缓存延迟大得多的惩罚,具体取决于具体的硬件。此外,任何延迟都可以通过重新排序来隐藏。