Gcc 复数乘法非常慢

Gcc 复数乘法非常慢,gcc,c++-standard-library,Gcc,C++ Standard Library,我注意到,使用重载的*操作符将两个std::complex值相乘要比写出操作慢得多。我看到了50倍的差异。这完全是荒谬的。我知道运算符需要检查输入中的NaN,因为定义了多么复杂的无穷大。这真的能解释50倍的时差吗 我使用的是带有标志的GCC 5.4.0-O3-mavx-mavx2-msse2-mfma-mbmi 以下是测试代码: #include <iostream> #include <complex> #include <chrono> #include

我注意到,使用重载的
*
操作符将两个
std::complex
值相乘要比写出操作慢得多。我看到了50倍的差异。这完全是荒谬的。我知道运算符需要检查输入中的NaN,因为定义了多么复杂的无穷大。这真的能解释50倍的时差吗

我使用的是带有标志的GCC 5.4.0
-O3-mavx-mavx2-msse2-mfma-mbmi

以下是测试代码:

#include <iostream>
#include <complex>
#include <chrono>
#include <vector>

int main( void ) {
  size_t N = 10000;
  std::vector< std::complex< double >> inbuf( N );
  for( size_t k = 0; k < N; ++k ) {
     inbuf[ k ] = std::complex< double >( std::rand(), std::rand() ) / ( double )RAND_MAX - 0.5;
  }

  std::complex< double > c2 = { 0, 0 };
  auto t0 = std::chrono::steady_clock::now();
  for( size_t i = 0; i < 10000; ++i ) {
     for( size_t j = 0; j < N - 1; ++j ) {
        double re = inbuf[ j ].real() * inbuf[ j + 1 ].real() - inbuf[ j ].imag() * inbuf[ j + 1 ].imag();
        double im = inbuf[ j ].real() * inbuf[ j + 1 ].imag() + inbuf[ j ].imag() * inbuf[ j + 1 ].real();
        c2.real( c2.real() + re );
        c2.imag( c2.imag() + im );
     }
  }
  auto t1 = std::chrono::steady_clock::now();
  double time = ( std::chrono::duration< float >( t1 - t0 ) ).count();
  std::cout << c2 << " using manual *: " << time << std::endl;

  c2 = { 0, 0 };
  t0 = std::chrono::steady_clock::now();
  for( size_t i = 0; i < 10000; ++i ) {
     for( size_t j = 0; j < N - 1; ++j ) {
        c2 += inbuf[ j ] * inbuf[ j + 1 ];
     }
  }
  t1 = std::chrono::steady_clock::now();
  time = ( std::chrono::duration< float >( t1 - t0 ) ).count();
  std::cout << c2 << " using stdlib *: " << time << std::endl;
  return 0;
}

编辑:由于评论中的人给出了不同的结果,我用不同的编译选项做了更多的测试。原来是
-mfma
-mavx
开关导致“stdlib”版本非常慢。
-mfma
开关使“手动”版本的性能提高了约25%,但使“stdlib”版本的性能降低了约13倍:

cris@carrier:~/tmp/tests> g++ complex_test.cpp -o complex_test -O3 -std=c++11
cris@carrier:~/tmp/tests> ./complex_test                                     
(-2.45689e+07,-134386) using manual *:0.138276
(-2.45689e+07,-134386) using stdlib *:0.412056
cris@carrier:~/tmp/tests> g++ complex_test.cpp -o complex_test -O3 -mfma -std=c++11 
cris@carrier:~/tmp/tests> ./complex_test                                                  
(-2.45689e+07,-134386) using manual *:0.106551
(-2.45689e+07,-134386) using stdlib *:5.37662

我也试过clang-800(Mac OS),但没有看到这种极端的速度减慢。Mac上的g++-5与Linux上的g++-5的功能相同。也许我发现了一个编译器bug?

< p>我遇到了同样的问题,显然是在VisualC++中复杂的计算性能缓慢。在我的例子中,我做了VisualC++中调试模式的时间测量。切换到释放模式后,与双重计算相比,时间是合理的(考虑到复杂的操作包括多个双重计算)。

测试理论的一种方法是在手动方法中包含使用
std::isnan
的测试。我很想知道你的时间。在生产代码中进行NAN测试(并采取适当措施)时,我确实遇到了一些问题,最终我使用Intel Intrinsic对其进行了优化。在没有-ffast math或其他功能的情况下,复数乘法会从libgcc调用函数_muldc3。这增加了在调用之前将值放入正确寄存器以及调用本身的开销。函数_muldc3似乎以明显的方式计算实部和虚部,然后检查isnan(实部)和&isnan(imag),除非触发,否则它将完成并返回。与-ffast数学案例相比,一个巨大的开销并不令人惊讶,尽管系数50似乎比我预期的要高。PS:请不要跳过测试用例的部分(如#include)。事实上,我在这里看到的比例要小得多(gcc-7为8,gcc-5为2.5)。你有可能在windows上吗?很有趣。是否包含一个幼稚的
isnan
检查,它仍然导致执行时间的因子为2。0乘以无穷大和无穷大减去无穷大产生NaN。当乘法是内联完成的(与函数调用相反),进一步的优化(如向量化)是可能的,这也有助于解释更高的比率(我无法重现)。
cris@carrier:~/tmp/tests> g++ complex_test.cpp -o complex_test -O3 -std=c++11
cris@carrier:~/tmp/tests> ./complex_test                                     
(-2.45689e+07,-134386) using manual *:0.138276
(-2.45689e+07,-134386) using stdlib *:0.412056
cris@carrier:~/tmp/tests> g++ complex_test.cpp -o complex_test -O3 -mfma -std=c++11 
cris@carrier:~/tmp/tests> ./complex_test                                                  
(-2.45689e+07,-134386) using manual *:0.106551
(-2.45689e+07,-134386) using stdlib *:5.37662