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
.
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);
.
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.
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?
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.
Edit 2 : Okay, it appears some of you want me to post more code. Here it comes:
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
:
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):
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.
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.
As was pointed out, it turned out my algorithm was unstable, couldn't blame anything on the computer or C++ ;)