Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用SSE2(浮动)缩放字节像素值(y=ax+;b)?_C++_Visual Studio_X86_Simd_Sse2 - Fatal编程技术网

C++ 使用SSE2(浮动)缩放字节像素值(y=ax+;b)?

C++ 使用SSE2(浮动)缩放字节像素值(y=ax+;b)?,c++,visual-studio,x86,simd,sse2,C++,Visual Studio,X86,Simd,Sse2,我想计算y=ax+b,其中x和y是像素值[即,值范围为0~255的字节],而a和b是浮点值 因为我需要对图像中的每个像素应用这个公式,另外,a和b对于不同的像素是不同的。C++中的直接计算是慢的,所以我有兴趣了解C++中的SSE2指令。 搜索之后,我发现与sse2在浮点运算中的乘法和加法与\u mm\u mul\u ps和\u mm\u add\u ps一样。但首先,我需要将字节中的x转换为float(4字节) 问题是,在我从字节数据源(\u mm\u load\u si128)加载数据之后,如

我想计算
y=ax+b
,其中x和y是像素值[即,值范围为0~255的字节],而
a
b
是浮点值

因为我需要对图像中的每个像素应用这个公式,另外,a和b对于不同的像素是不同的。C++中的直接计算是慢的,所以我有兴趣了解C++中的SSE2指令。 搜索之后,我发现与sse2在浮点运算中的乘法和加法与
\u mm\u mul\u ps
\u mm\u add\u ps
一样。但首先,我需要将字节中的x转换为float(4字节)


问题是,在我从字节数据源(
\u mm\u load\u si128
)加载数据之后,如何将数据从字节转换为浮点?

我猜您正在查看
\u m128\u mm\u cvtpi8\u ps(\uu\u m64 a)
复合内部结构

下面是一个简单的例子:

#include <xmmintrin.h>
#include <stdio.h>

int main() {
  unsigned char  a[4] __attribute__((aligned(32)))= {1,2,3,4};
  float b[4] __attribute__((aligned(32)));
  _mm_store_ps(b, _mm_cvtpi8_ps(*(__m64*)a));
  printf("%f %f, %f, %f\n", b[0], b[1], b[2], b[3]);
  return 0;
}
#包括
#包括
int main(){
无符号字符a[4]uuu属性uuu((对齐(32))={1,2,3,4};
浮动b[4]uuu属性_uuu((对齐(32));
_商店(b,cvtpi8(a));
printf(“%f%f,%f,%f\n”,b[0],b[1],b[2],b[3]);
返回0;
}

a
b
对于每个像素是不同的吗?这将使矢量化变得困难,除非有一个模式或者你可以生成它们

有什么方法可以有效地生成向量中的
a
b
,无论是定点还是浮点?否则,插入4个FP值或8个16位整数可能比仅插入标量运算更糟糕


定点 如果
a
b
可以重用,或者首先使用定点生成,那么这可能是定点数学的一个很好的用例。(即表示值*2^刻度的整数)。SSE/AVX没有8b*8b->16b乘法运算;最小的元素是字,因此必须将字节解压为字,但不能一直解压到32位。这意味着每个指令可以处理两倍的数据

如果
b
a
的更改频率不够高,或者您可以轻松生成交替使用a*2^4和b*2^1字节的向量,则有一条
\u mm\u maddubs\u epi16
指令可能很有用。显然这是真的,但如果我们能准备一个a和b向量,它仍然可以用最小的洗牌为我们完成任务

float a, b;
const int logascale = 4, logbscale=1;
const int ascale = 1<<logascale;  // fixed point scale for a: 2^4
const int bscale = 1<<logbscale;  // fixed point scale for b: 2^1

const __m128i brescale = _mm_set1_epi8(1<<(logascale-logbscale));  // re-scale b to match a in the 16bit temporary result

for (i=0 ; i<n; i+=16) {
    //__m128i avec = get_scaled_a(i);  
    //__m128i bvec = get_scaled_b(i);
    //__m128i ab_lo = _mm_unpacklo_epi8(avec, bvec);
    //__m128i ab_hi = _mm_unpackhi_epi8(avec, bvec);

    __m128i abvec = _mm_set1_epi16( ((int8_t)(bscale*b) << 8) | (int8_t)(ascale*a) );  // integer promotion rules might do sign-extension in the wrong place here, so check this if you actually write it this way.

    __m128i block = _mm_load_si128(&buf[i]);  // call this { v[0] .. v[15] }

    __m128i lo = _mm_unpacklo_epi8(block, brescale);  // {v[0], 8, v[1], 8, ...}
    __m128i hi = _mm_unpackhi_epi8(block, brescale);  // {v[8], 8, v[9], 8, ...
    lo = _mm_maddubs_epi16(lo, abvec);  // first arg is unsigned bytes, 2nd arg is signed bytes
    hi = _mm_maddubs_epi16(hi, abvec);
    // lo = { v[0]*(2^4*a) + 8*(2^1*b), ... }

    lo = _mm_srli_epi16(lo, logascale);  // truncate from scaled fixed-point to integer
    hi = _mm_srli_epi16(hi, logascale);

    // and re-pack.  Logical, not arithmetic right shift means sign bits can't be set
    block = _mm_packuswb(lo, hi);  
    _mm_store_si128(&buf[i], block);
}
// then a scalar cleanup loop
这个答案的前一个版本来自float->uint8 vectors和packusdw/packuswb,并且有一整节介绍了不使用SSE4.1的变通方法。如果您只是简单地执行以下操作,则不需要在未签名包之后屏蔽符号位。我假设这就是为什么SSE2只包括从dword到word的有符号包,但包括从word到字节的有符号包和无符号包
packuswd
仅当您的最终目标是
uint16\u t
而不是进一步打包时才有用


最后一个没有SSE4.1的CPU是Intel Conroe/merom(第一代Core2,2007年底之前)和AMD pre Barcelona(2007年底之前)。如果这些CPU可以工作但速度较慢,只需为AVX2编写一个版本,为SSE4.1编写一个版本。或者SSSE3(使用4x pshufb模拟寄存器中四个32b元素的pmovzxbd)pshufb在Conroe上速度较慢,因此如果您关心没有SSE4.1的CPU,请编写一个特定版本。实际上,Conroe/merom也有慢xmm
punpcklbw
等等(除了q->dq)。4倍慢速
pshufb
仍应优于6倍慢速解包。矢量化在pre Wolfdale上的胜利要小得多,因为解包和重新打包的慢洗牌。定点版本的解包/重新打包要少得多,在这方面会有更大的优势


在我意识到需要多少额外的指令之前,查看编辑历史记录,了解使用
punpck
的未完成尝试。删除它是因为这个答案已经很长了,另一个代码块会让人困惑。

可能重复:您是否尝试过在Project->Properties->code Generation中启用SSE2?编译器可能愿意为您这样做。
\u mm\u cvtpi8\u ps
内部指令不是一条指令。它用于有符号字节,因此对于大于127的值,它会给出错误的答案。另外,gcc的实现在输入和pcmpgtb0之间使用
punpcklbw
,input对extend进行符号扩展而不是零扩展,然后对word->dword再次进行符号扩展<您的答案中的code>gcc-O3不幸地生成了使用MMX寄存器的讨厌的asm,并且即使可用时也不使用
pmovsx
(使用
-march=sandybridge
\u mm\u cvtpu8\u ps
做OP想要做的事情,但是由于它使用MMX寄存器而不是SSE,它仍然需要做更多的工作。这将比使用
punpck
\uuu m128i
类型手工编写的任何代码都要慢。将字节转换为浮点的解决方案是可行的,但将浮点转换为无符号字符的解决方案对于大于128@Edwin例如我现在明白问题了。我误读了
pack*
的文档:它们将输入内容视为已签名。只是输出钳位范围不同于
packus
packss
。我对此发表了评论,直到现在我才开始思考。如果在开始浮动和返回之前将字节转换为[-128..127]范围(使用
\u mm\u add\u epi8(block,\u mm\u set1\u epi8(-128))
),则可以使用有符号饱和,然后使用单个
mm\u sub
进行转换。编辑我的答案。@Edwin:我必须afk,所以我没有非常仔细地校对,但我认为我有一个工作定点的想法,并且在值大于32767时修复了包错误。(你确定是128吗?)更新:要么我写这篇文章的时候没有想到
packssdw/packuswb
,要么我没有想到ATM有什么问题。@Edwin:如果你还在使用这个,我最近注意到浮动中有一个巨大的简化-
char *buf;

// const __m128i zero = _mm_setzero_si128();
for (i=0 ; i<n; i+=16) {
    __m128 a = get_a(i);
    __m128 b = get_b(i);

    // IDK why there isn't an intrinsic for using `pmovzx` as a load, because it takes a m32 or m64 operand, not m128.  (unlike punpck*)
    __m128i unsigned_dwords = _mm_cvtepu8_epi32((__m128i)(buf+i));  // load 4B at once.
    __m128 floats = _mm_cvtepi32_ps(unsigned_dwords);

    floats = _mm_fmadd_ps(floats, a, b);  // with FMA available, this might as well be 256b vectors, even with the inconvenience of the different lane-crossing semantics of pmovzx vs. punpck
    // or without FMA, do this with _mm_mul_ps and _mm_add_ps
    unsigned_dwords = _mm_cvtps_epi32(floats);

    // repeat 3 more times for buf+4, buf+8, and buf+12, then:

    __m128i packed01 = _mm_packss_epi32(dwords0, dwords1);  // SSE2
    __m128i packed23 = _mm_packss_epi32(dwords2, dwords3);
    // packuswb wants SIGNED input, so do signed saturation on the first step

    // saturate into [0..255] range
    __m12i8 packedbytes=_mm_packus_epi16(packed01, packed23);  // SSE2
    _mm_store_si128(buf+i, packedbytes);  // or storeu if buf isn't aligned.
}

// cleanup code to handle the odd up-to-15 leftover bytes, if n%16 != 0