Mat*_*aud 7 c++ x86 sse simd avx
我正在寻找一些SSE / AVX建议,以优化将RGB通道与其alpha通道预乘的例程:RGB * alpha / 255(+我们保留原始的alpha通道)。
for (int i = 0, max = width * height * 4; i < max; i+=4) {
data[i] = static_cast<uint16_t>(data[i] * data[i+3]) / 255;
data[i+1] = static_cast<uint16_t>(data[i+1] * data[i+3]) / 255;
data[i+2] = static_cast<uint16_t>(data[i+2] * data[i+3]) / 255;
}
Run Code Online (Sandbox Code Playgroud)
您会在我当前的实现下面找到它,但是我认为它可能会更快,而且我正在浪费宝贵的CPU周期。我在quick-bench.com上进行了测试,结果显示出令人鼓舞的效果,但是我应该做些什么改变才能使其快速发展呢?
谢谢
--------更新09/06/2019 --------
根据@chtz和@Peter Cordes的评论,我建立了一个存储库来评估不同的解决方案,这里是结果。您认为会更好吗?
Run on (8 X 3100 MHz CPU s)
CPU Caches:
L1 Data 32K (x4)
L1 Instruction 32K (x4)
L2 Unified 262K (x4)
L3 Unified 8388K (x1)
Load Average: 1.24, 1.60, 1.68
-----------------------------------------------------------------------------
Benchmark Time CPU Iterations UserCounters...
-----------------------------------------------------------------------------
v1_plain_mean 1189884 ns 1189573 ns 1000 itr=840.865/s
v1_plain_median 1184059 ns 1183786 ns 1000 itr=844.747/s
v1_plain_stddev 20575 ns 20166 ns 1000 itr=13.4227/s
v1_simd_x86_mean 297866 ns 297784 ns 1000 itr=3.3616k/s
v1_simd_x86_median 294995 ns 294927 ns 1000 itr=3.39067k/s
v1_simd_x86_stddev 9863 ns 9794 ns 1000 itr=105.51/s
Thanks Dot and Beached (discord #include)
v2_plain_mean 323541 ns 323451 ns 1000 itr=3.09678k/s
v2_plain_median 318932 ns 318855 ns 1000 itr=3.13623k/s
v2_plain_stddev 13598 ns 13542 ns 1000 itr=122.588/s
Thanks Peter Cordes (stackoverflow)
v3_simd_x86_mean 264323 ns 264247 ns 1000 itr=3.79233k/s
v3_simd_x86_median 260641 ns 260560 ns 1000 itr=3.83788k/s
v3_simd_x86_stddev 12466 ns 12422 ns 1000 itr=170.36/s
Thanks chtz (stackoverflow)
v4_simd_x86_mean 266174 ns 266109 ns 1000 itr=3.76502k/s
v4_simd_x86_median 262940 ns 262916 ns 1000 itr=3.8035k/s
v4_simd_x86_stddev 11993 ns 11962 ns 1000 itr=159.906/s
Run Code Online (Sandbox Code Playgroud)
--------更新10/06/2019 --------
我添加了AVX2版本,并使用了chtz的技巧。使用255作为color_odd中的alpha值,我能够删除_mm_blendv_epi8
和改进基准。
谢谢彼得和赫兹
v3_simd_x86_mean 246171 ns 246107 ns 100 itr=4.06517k/s
v3_simd_x86_median 245191 ns 245167 ns 100 itr=4.07885k/s
v3_simd_x86_stddev 5423 ns 5406 ns 100 itr=87.13/s
// AVX2
v5_simd_x86_mean 158456 ns 158409 ns 100 itr=6.31411k/s
v5_simd_x86_median 158248 ns 158165 ns 100 itr=6.3225k/s
v5_simd_x86_stddev 2340 ns 2329 ns 100 itr=92.1406/s
Run Code Online (Sandbox Code Playgroud)
如果可以,您可以使用 SSSE3, _mm_shuffle_epi8
让您创建__m128i alpha
向量,而不是 AND/shift/OR。
pshufb
将设置洗牌控制向量元素的高位的字节为零。(随机播放吞吐量很容易成为 Intel Haswell 及更高版本的瓶颈,因此使用立即移位或 AND 对于其他操作仍然有好处,您可以使用一条指令完成它。)
pblendvb
在 Skylake 及更高版本上,使用 SSE4.1合并 alpha 而不是 AND/ANDN/OR可能是一种胜利。(在 Haswell 上,2 个 uopspblendvb
只能在端口 5 上运行。这实际上可能没问题,因为有足够的其他 uops,这不会造成 shuffle 瓶颈。)
在 Skylake 上,非 VEXpblendvb
是在任何端口上运行的单微指令指令。(VEX 版本对于任何端口都是 2 uops,所以它仍然严格优于 AND/ANDN/OR,但不如 SSE 版本。虽然 SSE 版本使用隐式 XMM0 输入,所以它需要额外的指令,除非您movdqa
的循环仅使用pblendvb
相同的混合蒙版。或者如果您展开,那么它可能可以分摊movdqa
设置 XMM0。)
另外,_mm_srli_epi16
到 7 时,and_mm_slli_epi16(color_odd, 8)
可能只是一个班次,可能还有一个 AND。或者pblendvb
避免像 OR 之前那样清除垃圾。
我不确定你是否可以使用_mm_mulhrs_epi16
乘法和移位,但可能不行。这不是正确的转变,并且+1
“四舍五入”也不是您想要的。
显然,AVX2 版本的每条指令可以完成两倍的工作量,为主循环在 Haswell / Skylake 上提供 2 倍的加速。在 Ryzen 上可能有些中性,其中 256b 指令解码为 2 uop。(或者更多用于穿越车道的洗牌,但你没有这些。)
最坏的情况下,清理工作必须运行更多次,但这仍然可以忽略不计。
归档时间: |
|
查看次数: |
278 次 |
最近记录: |