我正在处理使用大量数学函数的算法,最近我们在 Solaris 平台的 Ubuntu 系统上移植了 g++ 4.8.2 下的代码。
令人惊讶的是,一些算法比以前花费了很多时间。背后的原因是std::tan()
函数比做的时间长两倍std::sin()/std::cos()
。
用 sin/cos 代替 tan 大大减少了相同结果的计算时间。我想知道为什么会有这样的差异。是因为标准库的实现吗?tan 函数不应该更有效吗?
我写了一个程序来检查函数的时间:
#include <cmath>
#include <iostream>
#include <chrono>
int main(int argc, char * argv[])
{
using namespace std::chrono;
auto start_tan = system_clock::now();
for (int i = 0; i < 50000; ++i)
{
const double & a = static_cast<double>(i);
const double & b = std::tan(a);
}
auto end_tan = system_clock::now();
auto elapsed_time_tan = end_tan - start_tan;
std::cout << "tan : ";
std::cout << elapsed_time_tan.count() << std::endl;
auto start_sincos = system_clock::now();
for (int i = 0; i < 50000; ++i)
{
const double & a = static_cast<double>(i);
const double & b = std::sin(a) / std::cos(a);
}
auto end_sincos = system_clock::now();
auto elapsed_time_sincos = end_sincos - start_sincos;
std::cout << "sincos : " << elapsed_time_sincos.count() << std::endl;
}
事实上,在输出中我有以下没有优化的时间:
tan : 8319960
sincos : 4736988
并通过优化 (-O2) :
tan : 294
sincos : 120
如果有人对此行为有任何想法。
编辑
我根据@Basile Starynkevitch 的回复修改了程序:
#include <cmath>
#include <iostream>
#include <chrono>
int main(int argc, char * argv[])
{
using namespace std::chrono;
if (argc != 2)
{
std::cout << "Need one and only argument : the number of iteration." << std::endl;
return 1;
}
int nb_iter = std::atoi(argv[1]);
std::cout << "Number of iteration programmed : " << nb_iter << std::endl;
double tan_sum = 0.0;
auto start_tan = system_clock::now();
for (int i = 0; i < nb_iter; ++i)
{
const double & a = static_cast<double>(i);
const double b = std::tan(a);
tan_sum += b;
}
auto end_tan = system_clock::now();
auto elapsed_time_tan = end_tan - start_tan;
std::cout << "tan : " << elapsed_time_tan.count() << std::endl;
std::cout << "tan sum : " << tan_sum << std::endl;
double sincos_sum = 0.0;
auto start_sincos = system_clock::now();
for (int i = 0; i < nb_iter; ++i)
{
const double & a = static_cast<double>(i);
const double b = std::sin(a) / std::cos(a);
sincos_sum += b;
}
auto end_sincos = system_clock::now();
auto elapsed_time_sincos = end_sincos - start_sincos;
std::cout << "sincos : " << elapsed_time_sincos.count() << std::endl;
std::cout << "sincos sum : " << sincos_sum << std::endl;
}
现在结果我-O2
只得到了类似的时间:
tan : 8345021
sincos : 7838740
但仍然有区别-O2 -mtune=native
,但确实更快:
tan : 5426201
sincos : 3721938
我不会使用-ffast-math
,因为我需要保持 IEEE 合规性。