我知道在没有先将所有 ymm 寄存器的上半部分清零的情况下从 AVX 指令切换到 SSE 指令的现有惩罚,但在我的机器(i7-3939K 3.2GHz)上的特殊情况下,似乎有一个非常即使我在 AVX 代码部分之前和之后明确使用 _mm256_zeroupper ,也会因为相反的方式(SSE 到 AVX)而受到很大的惩罚。
我已经编写了用于在 32768 个元素宽的 2 个缓冲区上转换 32 位浮点数和 32 位定点整数的函数。我将 SSE2 内在版本直接移植到 AVX 以一次执行 8 个元素,而不是 SSE 的 4 个,期望看到显着的性能提升,但不幸的是,相反的事情发生了。
所以,我有两个功能:
void ConvertPcm32FloatToPcm32Fixed(int32* outBuffer, const float* inBuffer, uint sampleCount, bool bUseAvx)
{
const float fScale = (float)(1U<<31);
if (bUseAvx)
{
_mm256_zeroupper();
const __m256 vScale = _mm256_set1_ps(fScale);
const __m256 vVolMax = _mm256_set1_ps(fScale-1);
const __m256 vVolMin = _mm256_set1_ps(-fScale);
for (uint i = 0; i < sampleCount; i+=8)
{
const __m256 vIn0 = _mm256_load_ps(inBuffer+i); // Aligned load
const __m256 vVal0 = _mm256_mul_ps(vIn0, vScale);
const __m256 vClamped0 = _mm256_min_ps( _mm256_max_ps(vVal0, vVolMin), vVolMax );
const __m256i vFinal0 = _mm256_cvtps_epi32(vClamped0);
_mm256_store_si256((__m256i*)(outBuffer+i), vFinal0); // Aligned store
}
_mm256_zeroupper();
}
else
{
const __m128 vScale = _mm_set1_ps(fScale);
const __m128 vVolMax = _mm_set1_ps(fScale-1);
const __m128 vVolMin = _mm_set1_ps(-fScale);
for (uint i = 0; i < sampleCount; i+=4)
{
const __m128 vIn0 = _mm_load_ps(inBuffer+i); // Aligned load
const __m128 vVal0 = _mm_mul_ps(vIn0, vScale);
const __m128 vClamped0 = _mm_min_ps( _mm_max_ps(vVal0, vVolMin), vVolMax );
const __m128i vFinal0 = _mm_cvtps_epi32(vClamped0);
_mm_store_si128((__m128i*)(outBuffer+i), vFinal0); // Aligned store
}
}
}
void ConvertPcm32FixedToPcm32Float(float* outBuffer, const int32* inBuffer, uint sampleCount, bool bUseAvx)
{
const float fScale = (float)(1U<<31);
if (bUseAvx)
{
_mm256_zeroupper();
const __m256 vScale = _mm256_set1_ps(1/fScale);
for (uint i = 0; i < sampleCount; i+=8)
{
__m256i vIn0 = _mm256_load_si256(reinterpret_cast<const __m256i*>(inBuffer+i)); // Aligned load
__m256 vVal0 = _mm256_cvtepi32_ps(vIn0);
vVal0 = _mm256_mul_ps(vVal0, vScale);
_mm256_store_ps(outBuffer+i, vVal0); // Aligned store
}
_mm256_zeroupper();
}
else
{
const __m128 vScale = _mm_set1_ps(1/fScale);
for (uint i = 0; i < sampleCount; i+=4)
{
__m128i vIn0 = _mm_load_si128(reinterpret_cast<const __m128i*>(inBuffer+i)); // Aligned load
__m128 vVal0 = _mm_cvtepi32_ps(vIn0);
vVal0 = _mm_mul_ps(vVal0, vScale);
_mm_store_ps(outBuffer+i, vVal0); // Aligned store
}
}
}
所以我启动了一个计时器,运行 ConvertPcm32FloatToPcm32Fixed 然后 ConvertPcm32FixedToPcm32Float 直接转换回来,结束计时器。函数的 SSE2 版本总共执行 15-16 微秒,但 AVX 版本需要 22-23 微秒。有点困惑,我进一步挖掘,我发现了如何加速 AVX 版本,以便它们比 SSE2 版本更快,但这是作弊。我只是在启动计时器之前运行 ConvertPcm32FloatToPcm32Fixed,然后启动计时器,然后再次运行 ConvertPcm32FloatToPcm32Fixed,然后运行 ConvertPcm32FixedToPcm32Float,停止计时器。好像 SSE 对 AVX 有很大的惩罚,如果我首先通过试运行“启动”AVX 版本,AVX 执行时间会下降到 12 微秒,而对 SSE 等价物做同样的事情时,只会将时间减少 1 微秒至 14 微秒,使 AVX 成为这里的边际赢家,但前提是我作弊。我认为也许 AVX 在缓存中的表现不如 SSE,但使用 _mm_prefetch 也无济于事。
我在这里错过了什么吗?