I am writing a scientific program which uses common values of sines in its main algorithm, namely sin(M_PI/N)
for N = 1, 2, 3, 4, 5, 6
.
我写的是一个科学的程序,它在其主要的算法中使用sines的共同值,即sin(M_PI/N), N = 1 2 3 4 5 6。
Since I want my program to be as fast as possible, I thought : let's store these values in a vector instead of having them computed over and over again. It looks like this:
由于我希望我的程序尽可能快,我想:让我们将这些值存储在一个向量中,而不是让它们反复计算。它看起来像这样:
sin_pi_over_n_.clear();
sin_pi_over_n_.push_back(0.0);
sin_pi_over_n_.push_back(1.0);
sin_pi_over_n_.push_back(sqrt(3.0)/2.0);
sin_pi_over_n_.push_back(sqrt(2.0)/2.0);
sin_pi_over_n_.push_back(sqrt(2.0)*0.25*sqrt(5.0-sqrt(5.0)));
sin_pi_over_n_.push_back(0.5);
so now in my main algorithm I write s = sin_pi_over_n_[n-1];
instead of s = sin(M_PI/n);
.
现在在我的主算法中,我写s = sin_pi_over_n_[n-1];而不是s = sin(m_/ n);
But to my great surprise, the program turned out to be almost twice as slow! I thought, really, does it take that long to read a value in a vector? But then I realized that wasn't the problem: if I write instead
但令我惊讶的是,这个程序竟然慢了一倍!我想,真的,要花这么长时间才能读懂向量的值吗?但后来我意识到这不是问题所在:如果我写的话
sin_pi_over_n_.push_back(sin(M_PI/1.0));
sin_pi_over_n_.push_back(sin(M_PI/2.0));
sin_pi_over_n_.push_back(sin(M_PI/3.0));
sin_pi_over_n_.push_back(sin(M_PI/4.0));
sin_pi_over_n_.push_back(sin(M_PI/5.0));
sin_pi_over_n_.push_back(sin(M_PI/6.0));
then the program is fast again! I then thought: something's wrong with my values of sines. But the crazy thing is, even if I only replace the last line sin_pi_over_n_.push_back(sin(M_PI/6.0));
by sin_pi_over_n_.push_back(0.5);
then the program is slow again! Is it about double precision? I kinda doubt it: if I ask std::cout << abs(sin(M_PI/6.0) - 0.5) << std::endl;
, I get 0
in my terminal.
那么程序又快了!然后我想:我的正弦值有问题。但疯狂的是,即使我只替换最后一行sin_pi_over_n_.push_back(sin(M_PI/6.0));sin_pi_over_n_.push_back(0.5);然后程序又变慢了!是关于双精度吗?我有点怀疑:如果我问std::cout < / abs(sin(M_PI/6.0) - 0.5) << std::endl;在我的终端中,我得到0。
Oops : I just realized ; if I ask std::cout << sin(M_PI/6.0) - 0.5 << std::endl;
(without the abs
), I then get -5.55112e-17
. I am still going to go ahead and post that question, because this behavior seems incredible to me. How can I possibly optimize my program's speed if such unpredictable phenomenons have such a great impact on the performance?
我刚意识到;如果我问std::cout < sin(M_PI/6.0) - 0.5 < std::endl;(没有abs),我得到-5.55112e-17。我仍然会继续发布这个问题,因为这种行为在我看来是不可思议的。如果这种不可预测的现象对性能有如此大的影响,我怎么可能优化程序的速度呢?
Thanks for your insights!
谢谢你的见解!
Edit : Maybe I haven't made myself clear enough. In my program, I have a class Algo
. When I execute my program, some function, say my_function
, is called an enormous amount of times. In this function, one line is: s = sin(M_PI/n);
. I thought I'd replace this line by s = sin_pi_over_n_[n-1];
, where sin_pi_over_n_[n-1]
is a vector that is stored as a member variable of the class Algo
and that I fill once and for all in the constructor of Algo
. Hope that makes things clearer.
编辑:可能我说得不够清楚。在我的项目中,我有一堂课。当我执行我的程序时,一些函数,比如my_function,被称为大量的次数。在这个函数中,一行是:s = sin(m_/ n);我想我应该用s = sin_pi_over_n_[n-1]来替换这一行;sin_pi_over_n_[n-1]是一个向量,它存储为Algo类的一个成员变量,我在Algo的构造函数中对它进行了一次彻底的填充。希望这能让事情变得更清楚。
Edit 2 : Okay, it appears some of you want me to post more code. Here it comes:
编辑2:好的,看起来有些人想让我发布更多的代码。这里谈到:
Class Algo
:
类算法:
class Algo : public QThread
{
Q_OBJECT
public:
Algo() {}
void reset_algo(...)
public slots:
void run();
private:
void create_sines();
double super_total_neighbors_angle(const unsigned int &index, double &error);
double super_radius_update(const unsigned int &index, double &error);
// etc
std::vector<double> radii_;
std::vector<double> sin_pi_over_n_;
std::vector<unsigned int> neighbors_lists_sizes_;
// etc
};
Member function create_sines
:
成员函数create_sines:
void Algo::create_sines()
{
sin_pi_over_n_.clear();
/*sin_pi_over_n_.push_back(0.0);
sin_pi_over_n_.push_back(1.0);
sin_pi_over_n_.push_back(0.5*sqrt(3.0));
sin_pi_over_n_.push_back(0.5*sqrt(2.0));
sin_pi_over_n_.push_back(0.25*sqrt(2.0)*sqrt(5.0-sqrt(5.0)));
sin_pi_over_n_.push_back(0.5);*/
sin_pi_over_n_.push_back(sin(M_PI/1.0));
sin_pi_over_n_.push_back(sin(M_PI/2.0));
sin_pi_over_n_.push_back(sin(M_PI/3.0));
sin_pi_over_n_.push_back(sin(M_PI/4.0));
sin_pi_over_n_.push_back(sin(M_PI/5.0));
sin_pi_over_n_.push_back(sin(M_PI/6.0));
return;
}
Member function super_radius_update
(which I renamed my_function
above):
成员函数super_radius_update(我将其重命名为my_function):
inline double Algo::super_radius_update(const unsigned int &index, double &error)
{
int n = neighbors_lists_sizes_[index];
double s = sin(super_total_neighbors_angle(index, error)*0.5/n);
double rv = radii_[index]*s/(1-s);
//s = sin(M_PI/n);
s = sin_pi_over_n_[n-1];
return (1-s)*rv/s;
}
I'm afraid it's hard for me to send a minimal complete example that you could run, because the entire class is a bit long and complicated. I could try, but I would sill have to post a big amount of code, and it would take me quite some time to extract...
我恐怕很难给您提供一个您可以运行的最小完整示例,因为整个类都有点长且复杂。我可以尝试一下,但是我仍然需要发布大量的代码,而且我需要相当长的时间来提取……
I'm using Qt creator 4.8 64 bits on a Linux Ubuntu 12.04 64 bits laptop.
我使用Qt创建者4.8 64位在Linux Ubuntu 12.04 64位笔记本电脑上。
Edit 3 or 4 : I apologize for all the edits. There's an important piece of info I did not tell you : the program is going to run (basically call the function super_radius_update
) until some error falls under some tolerance. As a consequence, what is making the program slower or faster is not the time of execution of super_radius_update
but the number of calls of that function, I believe. Using such or such value for sin(M_PI/N)
is going to have an impact on how quickly the tolerance is reached by the error.
编辑3或4:我为所有的编辑道歉。有一条重要的信息我没有告诉你:程序将会运行(基本上调用函数super_radius_update),直到出现一些错误。因此,使程序变慢或变快的不是执行super_radius_update的时间,而是这个函数的调用数量。对于sin(m_/ N)使用这样或那样的值将会影响到误差达到容错的速度。
2 个解决方案
#1
1
double rv = radii_[index]*s/(1-s);
return (1-s)*rv/s;
This code has singularities at s = 0
and s = 1
, both of which occur, and will be numerically unstable due to the singularities. You probably triggered the unstabilities with the different computations of sin_pi_over_n_[0]
and sin_pi_over_n_[1]
.
这段代码在s = 0和s = 1处有奇点,这两个都发生了,并且由于奇点的存在,在数值上不稳定。您可能使用sin_pi_over_n_[0]和sin_pi_over_n_[1]的不同计算触发了不稳定性。
While this doesn't explain the different behaviour for different sin_pi_over_n_[5]
calculations, similar unstabilities are likely.
虽然这并不能解释不同的sin_pi_over_n_[5]计算的不同行为,但类似的不稳定性是可能的。
#2
0
As was pointed out, it turned out my algorithm was unstable, couldn't blame anything on the computer or C++ ;)
正如我所指出的,我的算法是不稳定的,不能责怪计算机或c++;
#1
1
double rv = radii_[index]*s/(1-s);
return (1-s)*rv/s;
This code has singularities at s = 0
and s = 1
, both of which occur, and will be numerically unstable due to the singularities. You probably triggered the unstabilities with the different computations of sin_pi_over_n_[0]
and sin_pi_over_n_[1]
.
这段代码在s = 0和s = 1处有奇点,这两个都发生了,并且由于奇点的存在,在数值上不稳定。您可能使用sin_pi_over_n_[0]和sin_pi_over_n_[1]的不同计算触发了不稳定性。
While this doesn't explain the different behaviour for different sin_pi_over_n_[5]
calculations, similar unstabilities are likely.
虽然这并不能解释不同的sin_pi_over_n_[5]计算的不同行为,但类似的不稳定性是可能的。
#2
0
As was pointed out, it turned out my algorithm was unstable, couldn't blame anything on the computer or C++ ;)
正如我所指出的,我的算法是不稳定的,不能责怪计算机或c++;