我一直在英特尔Core Duo上分析我们的一些核心数学,并且在研究平方根的各种方法时,我注意到一些奇怪的事情:使用SSE标量操作,采用倒数平方根并乘以它更快获取sqrt,而不是使用本机sqrt操作码!
我正在测试它的循环类似于:
inline float TestSqrtFunction( float in );
void TestFunc()
{
#define ARRAYSIZE 4096
#define NUMITERS 16386
float flIn[ ARRAYSIZE ]; // filled with random numbers ( 0 .. 2^22 )
float flOut [ ARRAYSIZE ]; // filled with 0 to force fetch into L1 cache
cyclecounter.Start();
for ( int i = 0 ; i < NUMITERS ; ++i )
for ( int j = 0 ; j < ARRAYSIZE ; ++j )
{
flOut[j] = TestSqrtFunction( flIn[j] ); …Run Code Online (Sandbox Code Playgroud) 我正在谷歌搜索过去一小时的问题,但只有泰勒系列或一些示例代码的要点太慢或根本不编译.好吧,我发现谷歌的答案大多是"Google it,它已经被问到了",但遗憾的是它不是 ......
我在低端Pentium 4上分析我的游戏,发现大约85%的执行时间浪费在计算窦,cosinus和平方根(来自Visual Studio中的标准C++库)上,这似乎与CPU密切相关(在我的I7上,相同的功能只有5%的执行时间,并且游戏更快了waaaaaaaaaa).我不能优化这三个函数,也不能在一次传递中计算正弦和余弦(相互依赖),但我不需要太精确的模拟结果,所以我可以使用更快的逼近.
那么,问题是:在C++中计算float的正弦,余弦和平方根的最快方法是什么?
编辑 查找表更加痛苦,因为在现代CPU上产生的Cache Miss比Taylor系列更昂贵.这些天CPU很快,而缓存则不然.
我犯了一个错误,我虽然需要为Taylor系列计算几个阶乘,我现在看到它们可以实现为常量.
所以更新的问题是:对于平方根还有任何快速优化吗?
EDIT2
我使用平方根计算距离,而不是规范化 - 不能使用快速反平方根算法(如评论中所指出:http://en.wikipedia.org/wiki/Fast_inverse_square_root
EDIT3
我也无法在平方距离上操作,我需要精确的距离进行计算
有谁知道为什么GCC/Clang不会在下面的代码示例中优化函数test1,只是在使用fast-math选项时只使用RCPPS指令?是否有另一个编译器标志会生成此代码?
typedef float float4 __attribute__((vector_size(16)));
float4 test1(float4 v)
{
return 1.0f / v;
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处查看已编译的输出:https://goo.gl/jXsqat
假设有必要计算打包浮点数据的倒数或倒数平方根.两者都可以轻松完成:
__m128 recip_float4_ieee(__m128 x) { return _mm_div_ps(_mm_set1_ps(1.0f), x); }
__m128 rsqrt_float4_ieee(__m128 x) { return _mm_div_ps(_mm_set1_ps(1.0f), _mm_sqrt_ps(x)); }
Run Code Online (Sandbox Code Playgroud)
这种方法效果很好但很慢:根据指南,它们在Sandy Bridge上进行了14次和28次循环(吞吐量).对应的AVX版本在Haswell上几乎占用相同的时间.
另一方面,可以使用以下版本:
__m128 recip_float4_half(__m128 x) { return _mm_rcp_ps(x); }
__m128 rsqrt_float4_half(__m128 x) { return _mm_rsqrt_ps(x); }
Run Code Online (Sandbox Code Playgroud)
它们只需要一到两个时间周期(吞吐量),从而大大提升了性能.但是,它们非常接近:它们产生的结果相对误差小于1.5*2 ^ -12.鉴于单精度浮点数的机器epsilon是2 ^?24,我们可以说这种近似具有大约一半的精度.
似乎可以添加Newton-Raphson迭代以产生具有单精度的结果(可能不像IEEE标准所要求的那样精确),参见GCC,ICC,LLVM上的讨论.理论上,相同的方法可用于双精度值,产生半精度或单精度或双精度.
我有兴趣为float和double数据类型以及所有(half,single,double)精度实现此方法的实现.处理特殊情况(除以零,sqrt(-1),inf/nan等)不是必需的.此外,我不清楚这些例程中的哪一个比普通的IEEE编译解决方案更快,哪个更慢.
以下是对答案的一些小限制,请:
欢迎任何性能评估,测量和讨论.
以下是具有一次NR迭代的单精度浮点数的版本:
__m128 recip_float4_single(__m128 x) {
__m128 res = _mm_rcp_ps(x);
__m128 muls …Run Code Online (Sandbox Code Playgroud) sse ×3
c++ ×2
performance ×2
simd ×2
assembly ×1
avx ×1
fast-math ×1
math ×1
optimization ×1
trigonometry ×1
x86 ×1