用于灰度到ARGB转换的c++ SSE2或AVX2特性
C++ SSE2 or AVX2 intrinsics for grayscale to ARGB conversion
我想知道是否有一个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
Intel开发人员手册说:
VPMOVZXBD ymm1, xmm2/m64
0在低8位扩展8个打包的8位整数xmm2/m64到8个打包的32位整数的字节数ymm1 .
我不确定这是预期的行为还是我仍然错过了一些东西。
谢谢。
更新:@chtz的答案是一个更好的主意,使用便宜的128->256广播负载代替vpmovzx
来馈送vpshufb
,节省shuffle-port带宽。
按照Mark的建议从PMOVZX开始。
但在此之后,PSHUFB (_mm256_shuffle_epi8
)将比PMULLD快得多,除了它与PMOVZX竞争shuffle端口。(它在车道内运行,所以你仍然需要PMOVZX)。
因此,如果您只关心吞吐量,而不关心延迟,那么_mm256_mullo_epi32
是好的。但是,如果延迟很重要,或者如果您的吞吐量瓶颈不是每个向量2个shuffle-port指令,那么在每个像素内复制字节的PSHUFB应该是最好的。
实际上,即使对于吞吐量,_mm256_mullo_epi32
在HSW和BDW上也很糟糕:对于p0,它是2个上限(10c延迟),所以它是一个端口的2个上限。
在SKL上,它为p01提供2个上限(10c延迟),因此它可以维持与VPMOVZXBD相同的每时钟吞吐量。但这是一个额外的上限,使它更有可能成为瓶颈。
(在所有支持AVX2的Intel cpu上,VPSHUFB为1 up, 1c延迟,端口5)
您可以将打包的字节加载到寄存器中,call __m256i _mm256_cvtepu8_epi32 (__m128i a)
转换为32位值,然后乘以0x00010101将灰度复制为R,G和b
可以用一个vbroadcasti128
和两个vpshufb
转换16个像素。广播不需要端口5,如果它直接从内存加载,所以shuffle可以充分利用该端口(它仍然会在该端口上瓶颈,或者在存储回内存时)。
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 Demo: https://godbolt.org/z/dUx6GZ
- 使用 SSE2 和 AVX2 编译库
- AVX2 整数乘以有符号 8 位元素,产生有符号 16 位结果?
- AVX2:计算 512 个浮点数组的点积
- 使用 AVX2 C++进行选择性加载
- 使用 AVX2 指令左移 128 位数字
- AVX2 代码比没有 AVX2 的代码慢
- 在AVX2中再现_mm256_sllv_epi16和_mm256-slv_epi8
- 是否启用了 SSE2 指令?
- AVX2收集指令使用详细信息
- 防止gcc破坏我的AVX2内部复制到REP MOVS
- AVX2列填充计数算法分别针对每个位列
- AVX2 SIMD Instrinsics 16 位到 8 位,反之亦然
- 当我在支持 avx2 的机器上编译并在另一台仅支持 avx 的机器上运行二进制文件时会发生什么?
- 使用 AVX2 将 8 位从 32 位值 (__m256i) 解压缩到__m256的最快方法
- SSE2 函数如何从它应该所在的标头中丢失?
- SSE 整数 2^n 的 2 次方,对于没有 AVX2 的 32 位整数
- SSE2优化用于从RGB565转换为RGB888(无alpha通道)
- SSE2包装的8位整数签名乘数(高半):将M128i(16x8位)分解为两个M128i(每个8x16),然后重新包装
- 最佳无分支有条件选择两个SSE2填充双打
- 用于灰度到ARGB转换的c++ SSE2或AVX2特性