我想知道是否要执行 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}
我是 vector 扩展的完全初学者,所以我什至不确定如何处理这个问题,或者是否有可能。
如有任何帮助,我们将不胜感激。谢谢!
更新1
感谢您的回答。我仍然有一个问题:
我把这个小例子放在一起,用 VS2015 在 x64 上编译。
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
Zero extend 8 packed 8-bit integers in the low 8 bytes of xmm2/m64 to 8 packed 32-bit integers in ymm1.
我不确定这是有意为之还是我仍然遗漏了什么。
谢谢。
最佳答案
更新:@chtz 的回答是一个更好的主意,使用廉价的 128->256 广播负载而不是 vpmovzx
来提供 vpshufb
,从而节省随机端口带宽。
按照 Mark 的建议从 PMOVZX 开始。
但在此之后,PSHUFB (_mm256_shuffle_epi8
) 将比 PMULLD 快得多,只是它与 PMOVZX 竞争 shuffle 端口。 (而且它在 channel 内运行,因此您仍然需要 PMOVZX)。
因此,如果您只关心吞吐量,而不关心延迟,那么 _mm256_mullo_epi32
不错。但是,如果延迟很重要,或者如果您的吞吐量瓶颈不是每个 vector 2 个洗牌端口指令,那么 PSHUFB 复制每个像素内的字节应该是最好的。
实际上,即使对于吞吐量,_mm256_mullo_epi32
在 HSW 和 BDW 上也很糟糕:p0 为 2 uops(10c 延迟),因此一个端口为 2 uops。
在 SKL 上,p01 为 2 微指令(10c 延迟),因此它可以维持与 VPMOVZXBD 相同的每个时钟吞吐量。但这是一个额外的 1 uop,使其更容易成为瓶颈。
(VPSHUFB 是 1 uop,1c 延迟,对于端口 5,在所有支持 AVX2 的英特尔 CPU 上。)
关于用于灰度到 ARGB 转换的 C++ SSE2 或 AVX2 内在函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40497227/