SSE 中高效的 min() 函数
Efficient min() function in SSE
我有以下循环,它取数组中每个条目的平方根:
#include <mmintrin.h>
float array[SIZE];
for (int i = 0; i < SIZE; i += 4)
{
__m128 fourFloats, fourRoots;
fourFloats = _mm_load_ps(&array[i]);
fourRoots = _mm_sqrt_ps(fourFloats);
float results[4];
_mm_store_ps(results, fourRoots);
// This is bottleneck
array[i] = results[0] > 63.0F ? 63.0F : floor(results[0]);
array[i+1] = results[1] > 63.0F ? 63.0F : floor(results[1]);
array[i+2] = results[2] > 63.0F ? 63.0F : floor(results[2]);
array[i+3] = results[3] > 63.0F ? 63.0F : floor(results[3]);
// This is slower
// array[i] = (int) std::min(floor(results[0]), 63.0F);
}
根据我的分析器 (Zoom),平方根不需要花费大量时间,但结果的四次裁剪中的每一次都需要大约 20% 的时间,即使 [=11= 】 优化上。有没有更有效的方法来实现循环?请注意 _mm_store_ps()
被 gcc
优化了。
我尝试了优化的 table 平方根查找,因为 97% 的输入 array
值都在 512 以下,但这没有帮助。请注意,对于我的完整应用程序(一个持续 运行 图像识别应用程序),此例程占用的总处理器时间不到四分之一。
__m128d _mm_max_ps(__m128d a, __m128d b);
Performs an SIMD compare of the packed single-precision floating-point values in the first source operand and the second source operand and returns the maximum value for each pair of values to the destination operand.
和
__m128d _mm_min_ps(__m128d a, __m128d b);
Performs an SIMD compare of the packed single-precision floating-point values in the first source operand and the second source operand and returns the minimum value for each pair of values to the destination operand.
使用具有四个 63.0f 值的 XMM 寄存器作为第二个操作数。
鉴于您可以使用非常现代的 CPU 我会从这个开始:
float array[SIZE];
for(int i = 0; i < SIZE; i += 8)
{
__m256 eightFloats, eightRoots;
eightFloats = _mm256_load_ps(&array[i]);
eightRoots = _mm256_sqrt_ps(eightFloats);
float results[8];
eightRoots = _mm256_floor_ps(eightRoots);
_mm256_store_ps(results, eightRoots);
...
}
如果允许使用最高级的 SIMD 指令,甚至会选择 512 版本。
总结这两个答案,这是我最终决定满足我的全部要求的代码,array[i] = std::min(floor(sqrt(array[i])), (float) 0x3f);
float array[SIZE];
const float clipValue = (float) 0x3f;
const float clipArray[8] = {clipValue, clipValue, clipValue, clipValue,
clipValue, clipValue, clipValue, clipValue};
__m256 eightClips = _mm256_load_ps(clipArray);
for(int i = 0; i < SIZE; i += 8)
{
__m256 eightFloats = _mm256_load_ps(&array[i]);
__m256 eightRoots = _mm256_sqrt_ps(eightFloats);
__m256 eightFloors = _mm256_floor_ps(eightRoots);
__m256 eightMins = _mm256_min_ps(eightFloors, eightClips);
_mm256_store_ps(&array[i], eightMins);
}
我的目标是垂直应用程序中的特定硬件,因此可以使用兼容 AVX 的处理器。
我有以下循环,它取数组中每个条目的平方根:
#include <mmintrin.h>
float array[SIZE];
for (int i = 0; i < SIZE; i += 4)
{
__m128 fourFloats, fourRoots;
fourFloats = _mm_load_ps(&array[i]);
fourRoots = _mm_sqrt_ps(fourFloats);
float results[4];
_mm_store_ps(results, fourRoots);
// This is bottleneck
array[i] = results[0] > 63.0F ? 63.0F : floor(results[0]);
array[i+1] = results[1] > 63.0F ? 63.0F : floor(results[1]);
array[i+2] = results[2] > 63.0F ? 63.0F : floor(results[2]);
array[i+3] = results[3] > 63.0F ? 63.0F : floor(results[3]);
// This is slower
// array[i] = (int) std::min(floor(results[0]), 63.0F);
}
根据我的分析器 (Zoom),平方根不需要花费大量时间,但结果的四次裁剪中的每一次都需要大约 20% 的时间,即使 [=11= 】 优化上。有没有更有效的方法来实现循环?请注意 _mm_store_ps()
被 gcc
优化了。
我尝试了优化的 table 平方根查找,因为 97% 的输入 array
值都在 512 以下,但这没有帮助。请注意,对于我的完整应用程序(一个持续 运行 图像识别应用程序),此例程占用的总处理器时间不到四分之一。
__m128d _mm_max_ps(__m128d a, __m128d b);
Performs an SIMD compare of the packed single-precision floating-point values in the first source operand and the second source operand and returns the maximum value for each pair of values to the destination operand.
和
__m128d _mm_min_ps(__m128d a, __m128d b);
Performs an SIMD compare of the packed single-precision floating-point values in the first source operand and the second source operand and returns the minimum value for each pair of values to the destination operand.
使用具有四个 63.0f 值的 XMM 寄存器作为第二个操作数。
鉴于您可以使用非常现代的 CPU 我会从这个开始:
float array[SIZE];
for(int i = 0; i < SIZE; i += 8)
{
__m256 eightFloats, eightRoots;
eightFloats = _mm256_load_ps(&array[i]);
eightRoots = _mm256_sqrt_ps(eightFloats);
float results[8];
eightRoots = _mm256_floor_ps(eightRoots);
_mm256_store_ps(results, eightRoots);
...
}
如果允许使用最高级的 SIMD 指令,甚至会选择 512 版本。
总结这两个答案,这是我最终决定满足我的全部要求的代码,array[i] = std::min(floor(sqrt(array[i])), (float) 0x3f);
float array[SIZE];
const float clipValue = (float) 0x3f;
const float clipArray[8] = {clipValue, clipValue, clipValue, clipValue,
clipValue, clipValue, clipValue, clipValue};
__m256 eightClips = _mm256_load_ps(clipArray);
for(int i = 0; i < SIZE; i += 8)
{
__m256 eightFloats = _mm256_load_ps(&array[i]);
__m256 eightRoots = _mm256_sqrt_ps(eightFloats);
__m256 eightFloors = _mm256_floor_ps(eightRoots);
__m256 eightMins = _mm256_min_ps(eightFloors, eightClips);
_mm256_store_ps(&array[i], eightMins);
}
我的目标是垂直应用程序中的特定硬件,因此可以使用兼容 AVX 的处理器。