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.