pow(NAN)非常慢

das*_*sin 8 c++ performance nan pow

pow()NaN值的灾难性表现是什么原因?据我所知,如果使用SSE而不是x87 FPU进行浮点数学运算,NaN不应该对性能产生影响.

这似乎适用于基本操作,但不适用于pow().我将双倍的乘法和除法比作平方,然后取平方根.如果我用下面的代码编译g++ -lrt,我得到以下结果:

multTime(3.14159): 20.1328ms
multTime(nan): 244.173ms
powTime(3.14159): 92.0235ms
powTime(nan): 1322.33ms
Run Code Online (Sandbox Code Playgroud)

正如所料,涉及NaN的计算需要相当长的时间.g++ -lrt -msse2 -mfpmath=sse然而,编译会导致以下时间:

multTime(3.14159): 22.0213ms
multTime(nan): 13.066ms
powTime(3.14159): 97.7823ms
powTime(nan): 1211.27ms
Run Code Online (Sandbox Code Playgroud)

NaN的乘法/除法现在要快得多(实际上比实数更快),但是平方和取平方根仍然需要很长时间.

测试代码(在VMWare中使用32位OpenSuSE 10.2上的gcc 4.1.2编译,CPU是Core i7-2620M)

#include <iostream>
#include <sys/time.h>
#include <cmath>

void multTime( double d )
{
   struct timespec startTime, endTime;
   double durationNanoseconds;

   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &startTime);

   for(int i=0; i<1000000; i++)
   {
      d = 2*d;
      d = 0.5*d;
   }

   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &endTime);
   durationNanoseconds = 1e9*(endTime.tv_sec - startTime.tv_sec) + (endTime.tv_nsec - startTime.tv_nsec);
   std::cout << "multTime(" << d << "): " << durationNanoseconds/1e6 << "ms" << std::endl;
}

void powTime( double d )
{
   struct timespec startTime, endTime;
   double durationNanoseconds;

   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &startTime);

   for(int i=0; i<1000000; i++)
   {
      d = pow(d,2);
      d = pow(d,0.5);
   }

   clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &endTime);
   durationNanoseconds = 1e9*(endTime.tv_sec - startTime.tv_sec) + (endTime.tv_nsec - startTime.tv_nsec);
   std::cout << "powTime(" << d << "): " << durationNanoseconds/1e6 << "ms" << std::endl;
}

int main()
{
   multTime(3.14159);
   multTime(NAN);

   powTime(3.14159);
   powTime(NAN);
}
Run Code Online (Sandbox Code Playgroud)

编辑:

不幸的是,我对这个主题的了解非常有限,但我想glibc pow()从不在32位系统上使用SSE,而是在某些程序集中使用SSE sysdeps/i386/fpu/e_pow.S.__ieee754_pow_sse2在最近的glibc版本中有一个函数,但它在sysdeps/x86_64/fpu/multiarch/e_pow.c,因此可能仅适用于x64.但是,所有这些都可能与此无关,因为pow()它也是一个gcc内置函数.如需简单修复,请参阅Z boson的回答.

Bre*_*ale 8

"如果使用SSE而不是x87 FPU进行浮点数学计算,NaN不应该对性能产生影响."

我不确定这是从您引用的资源中得出的结果.无论如何,pow是一个C库函数.它不是作为指令实现的,即使在x87上也是如此.所以这里有两个独立的问题 - SSE如何处理NaN值,以及pow函数实现如何处理NaN值.

如果pow函数实现对特殊值使用不同的路径,例如+/-Inf,或者NaN,您可能希望NaNbase或exponent的值快速返回值.另一方面,实现可能不会将此作为单独的情况处理,并且仅依赖于浮点运算将中间结果作为NaN值传播.

从"Sandy Bridge"开始,减少或消除了与非正规相关的许多性能损失.然而,并非所有,因为作者描述了惩罚mulps.因此,期望并非所有涉及的算术运算NaNs都是"快速" 是合理的.有些架构甚至可能会回复到微代码来处理NaNs不同的环境.