我遇到了一组带有以下“内核”作为性能阻止程序的代码。由于我可以访问最新的 Intel(R) Xeon Phi(TM) CPU 7210 (KNL),因此我希望使用 AVX512 内在函数加速它。
for( int y = starty; y <= endy; y++)
{
// hence data[][] is "unsigned char" while result[] is "int"
for( int x = startx; x <= endx; x++)
{
if( (data[y][x]&0x1) == 0 )
result[x] += data[y][x];
}
}
在分析了代码的行为后,我发现内循环的长度大多小于 16,所以我写了以下内容
register int xlen = xend - xstart + 1;
__m512i zero5 = _mm512_setzero_si512();
__m256i zero2 = _mm512_castsi512_si256(zero5);
__m128i zero1 = _mm512_castsi512_si128(zero5);
__m256i mask2 = _mm256_set1_epi8(0x1);
__m128i mask1 = _mm256_castsi256_si128(mask2);
register __m512i psprof0 = zero5;
for( int i = 0; i < (16-xlen)&(~0x1); i += 2 ) mask1 = _mm_srli_si128(mask1, 2);
if( (16-xlen)&(0x1) ) mask1 = _mm_srli_si128(mask1, 1);
#pragma vector nontemporal
#pragma prefetch data
for( int y = starty; y <= endy; y++ )
{
__m128i pixel16 = _mm_loadu_si128((__m128i*)&data[y][startx]);
// if ( _mm_testc_si128(pixel16, mask1) ) continue;
__m128i mask16 = _mm_andnot_si128(pixel16, mask1);
__m128i pixel16n = _mm_sign_epi8(pixel16, mask16);
psprof0 = _mm512_add_epi32(psprof0, _mm512_cvtepu8_epi32(pixel16n));
}
_mm512_storeu_si512(&result[startx], psprof0);
这里有几个问题:
- 由于_mm_srli_si128不接受非立即参数,我必须在那里使用循环,请问有什么办法可以消除它?
- _mm_testc_si128(pixel16, mask1)大多对性能没有帮助,这当然是由于data[][]的分布;但是,它“计算 a 的按位非,然后与 b 进行 AND,如果结果为零,则将 CF 设置为 1,否则将 CF 设置为 0”,有什么方法可以得到“ANDNOT”的结果,以便我这样做不需要再计算_mm_andnot_si128?
- 由于内环长度大多小于 16,它可能不太适合 AVX512;但是,通过加载 data[y][x] 和 data[y+1][x],然后将它们组合成一个 __m256i,将 y 间隔展开 2 是否值得?但是,由于 KNL (AVX512BW) 上还没有 8bit int 到 16bit int 的转换,它可能比当前版本更令人沮丧。
- 一般来说,任何提高 KNL 上这一小段代码性能的建议/建议都非常感谢 :) (它已经在 OpenMP 循环区域内,因此现在可能不可用)
上述第 3 点:
static inline __m256i vec_256_combine_128(__m128i a, __m128i b)
{
// combine two __m128i into one __m256i
return _mm256_insertf128_si256(_mm256_castsi128_si256(a), b, 1);
}
static inline __m128i vec_256_add_128(__m256i a)
{
// add lower 128bit and higher 128bit of __m256i consists of epi16
return _mm_add_epi16(_mm256_castsi256_si128(a), _mm256_extracti128_si256(a, 1));
}
for( int y = starty; y <= endy; y += 2 )
{
__m128i pixel16a = _mm_load_si128((__m128i*)&pEdgeImage[y][sx]);
__m128i pixel16b = _mm_load_si128((__m128i*)&pEdgeImage[y+1][sx]);
if ( y == ye )
pixel16b = zero1;
__m256i pixel16 = vec_256_combine_128(pixel16a, pixel16b);
if ( _mm256_testc_si256(pixel16, mask1) ) continue;
__m256i mask16 = _mm256_andnot_si256(pixel16, mask1);
__m256i pixel16n = _mm256_sign_epi8(pixel16, mask16);
__m256i pixel16lo = _mm256_unpacklo_epi8(pixel16n, zero2);
__m256i pixel16hi = _mm256_unpackhi_epi8(pixel16n, zero2);
psprof0 = _mm256_add_epi16(psprof0, vec_256_combine_128(vec_256_add_128(pixel16lo), vec_256_add_128(pixel16hi)));
}