我想知道是否要执行SSE2 / AVX2整数指令或指令序列(或内在函数)以实现以下结果:
给定一行8字节像素的格式:
A = {a, b, c, d, e, f, g, h}
有没有办法在包含8个32位ARGB像素的YMM寄存器中加载这些像素,以便将初始灰度值广播到每个相应32位像素的其他2个字节?结果应该是这样的:( 0是alpha值)
B = {0aaa, 0bbb, 0ccc, 0ddd, 0eee, 0fff, 0ggg, 0hhh}
我是矢量扩展的完全初学者,所以我甚至不确定如何处理它,或者它是否可能。
任何帮助将不胜感激。谢谢!
UPDATE1
感谢您的回答。我仍然有一个问题:
我将这个小例子放在一起,并在x64上用VS2015编译。
int main()
{
unsigned char* pixels = (unsigned char*)_aligned_malloc(64, 32);
memset(pixels, 0, 64);
for (unsigned char i = 0; i < 8; i++)
pixels[i] = 0xaa + i;
__m128i grayscalePix = _mm_load_si128((const __m128i*)pixels);
__m256i rgba = _mm256_cvtepu8_epi32(grayscalePix);
__m256i mulOperand = _mm256_set1_epi32(0x00010101);
__m256i result = _mm256_mullo_epi32(rgba, mulOperand);
_aligned_free(pixels);
return 0;
}
问题在于做完
__m256i rgba = mm256_cvtepu8_epi32(grayscalePix)
rgba 只设置了前四个双字。最后四个都是0。
英特尔开发人员手册说:
VPMOVZXBD ymm1,xmm2 / m64
在低8中零扩展8个压缩的8位整数 字节为xmm2 / m64到8个打包的32位整数 YMM1。
我不确定这是否是预期的行为,或者我仍然遗漏了一些东西。
感谢。
答案 0 :(得分:4)
从马克建议的PMOVZX开始。
但在此之后,PSHUFB(_mm256_shuffle_epi8
)将比PMULLD快得多,除了它与PMOVZX竞争shuffle端口。 (并且它在通道内运行,因此您仍然需要PMOVZX)。
因此,如果您只关心吞吐量而不是延迟,那么_mm256_mullo_epi32
就是好的。但是如果延迟很重要,或者你的吞吐量瓶颈在每个矢量的2个shuffle-port指令之外,那么PSHUFB复制每个像素中的字节应该是最好的。
实际上,即使对于吞吐量,_mm256_mullo_epi32
在HSW和BDW上也是不好的:它对于p0来说是2 uop(10c延迟),所以它对于一个端口来说是2 uop。
在SKL上,它为p01的2 uop(10c延迟),因此它可以维持与VPMOVZXBD相同的每时钟吞吐量。但它是一个额外的1 uop,使其更容易出现瓶颈。
(对于支持AVX2的所有Intel CPU,VPSHUFB为1 uop,端口5为1c延迟。)
答案 1 :(得分:2)
您可以将打包的字节加载到寄存器中,
call __m256i _mm256_cvtepu8_epi32 (__m128i a)
转换为32位值,然后乘以0x00010101将灰度复制为R,G和B.
答案 2 :(得分:1)
您可以使用一个vbroadcasti128
和两个vpshufb
转换16个像素。如果广播直接从内存加载,则广播不需要端口5,因此改组可以充分利用该端口(它将仍然是该端口或存储回内存的瓶颈)。
void gray2rgba(char const* input, char* output, size_t length)
{
length &= size_t(-16); // lets just care about sizes multiples of 16 here ...
__m256i shuflo = _mm256_setr_epi32(
0x80000000, 0x80010101, 0x80020202, 0x80030303,
0x80040404, 0x80050505, 0x80060606, 0x80070707
);
__m256i shufhi = _mm256_setr_epi32(
0x80080808, 0x80090909, 0x800a0a0a, 0x800b0b0b,
0x800c0c0c, 0x800d0d0d, 0x800e0e0e, 0x800f0f0f
);
for(size_t i=0; i<length; i+=16)
{
__m256i in = _mm256_broadcastsi128_si256(*reinterpret_cast<const __m128i*>(input+i));
__m256i out0 = _mm256_shuffle_epi8(in, shuflo);
__m256i out1 = _mm256_shuffle_epi8(in, shufhi);
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+4*i), out0);
_mm256_storeu_si256(reinterpret_cast<__m256i*>(output+4*i+32), out1);
}
}
Godbolt演示:https://godbolt.org/z/dUx6GZ