我有以下循环,它取数组中每个条目的平方根:
#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%的时间,即使-O2
优化也是如此上。有没有更有效的方法来实现循环?请注意_mm_store_ps()
优化了gcc
。
我尝试了对平方根的优化表查找,因为97%的输入array
值低于512,但这没有帮助。请注意,此例程占用整个应用程序总处理器时间的不到四分之一,这是一个不断运行的图像识别应用程序。
答案 0 :(得分:2)
__ m128d _mm_max_ps(__ m128d a,__ m128d b);
对第一个源操作数和第二个源操作数中的压缩单精度浮点值执行SIMD比较,并将每对值的最大值返回到目标操作数。
和
__ m128d _mm_min_ps(__ m128d a,__ m128d b);
对第一个源操作数和第二个源操作数中的压缩单精度浮点值执行SIMD比较,并将每对值的最小值返回到目标操作数。
使用带有四个63.0f值的XMM寄存器作为第二个操作数。
答案 1 :(得分:2)
鉴于您可以使用非常现代的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个版本。
答案 2 :(得分:1)
总结这两个答案,这是我最终决定的完整要求的代码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兼容的处理器。