Following to a profiling on my C code, it appears that pow
function is used a lot.
Some of my pow
functions have integer exponent and other non integer exponent. I am only
interested for the ones with integer exponent.
To gain in performance, I am looking a way to define a macro like this kind :
#define pow(x,n) ({\
double product;\
if (typeid(n).name() == "i") {\
for(int i = 0; i < n-1; i )\
product *= x;}\
else\
product = pow(x,n);\
product;\
})
But I don't get the gain expected regarding the runtime. I think this is due to the else
part in my macro where I call the classical pow
function.
I don't know how to determine in advance the type of exponent before macro was "written" during the pre-processing.
Ideally, I would like this macro only to be applied if exponent is an integer, but it seems my attempt is not pertinent.
EDIT :
From your suggestions, I tried 3 options :
First option : Just add overload inline functions with base which is integer
or double
:
// First option
inline int pow(int x, int n){
// using simple mechanism for repeated multiplication
int product = 1;
for(int i = 0; i < n; i ){
product *= x;
}
return product;
}
inline int pow(double x, int n){
// using simple mechanism for repeated multiplication
double product = 1;
for(int i = 0; i < n; i ){
product *= x;
}
return product;
}
Result : runtime = 1m 08s
Second option : Define a macro that calls via inline my_pow
function if exponent n
is not integer :
// Second option
inline double my_pow(double x, double n){
return pow(x,n);
}
#define pow(x,n) ({\
double product = 1;\
if (typeid(n) == typeid(int)) {\
for(int i = 0; i < n; i )\
product *= x;}\
else product = my_pow(x,n);\
product;\
})
Result : runtime = 51.86s
Third option : suggestion given in answer with template<typename type_t>
template<typename type_t>
inline double pow(const double x, type_t n)
{
// this is compile time checking of types
// don't use the rtti thing you are trying to do
//if constexpr (is_floating_point_v<type_t>)
if (typeid(n) != typeid(n))
{
return pow(x, n);
}
else
{
double value = 1;
for (type_t i = 0; i < n; i) value *= x;
return value;
}
}
Result : runtime = 52.84s
So finally, from these first tests, the best option would be the second one where I use macro combined with a function that calls the general case of pow
function (both integer and floating exponent).
Could anyone suggest a more efficient solution or basically give his opinion about this best second option ?
CodePudding user response:
If you only need to switch between floating point types or not you can use templates instead of macros.
#include <cassert>
#include <cmath>
#include <type_traits>
namespace my_math
{
template<typename type_t>
inline double pow(const double x, type_t n)
{
// this is compile time checking of types
// don't use the rtti thing you are trying to do
if constexpr (std::is_floating_point_v<type_t>)
{
return std::pow(x, n);
}
else
{
double value = 1;
for (type_t i = 0; i < n; i) value *= x;
return value;
}
};
}
int main()
{
assert(my_math::pow(2, 0) == 1);
assert(my_math::pow(2.0, 1) == 2.0);
assert(my_math::pow(3.0, 2.0) == 9.0);
assert(my_math::pow(4.0f, 3.0f) == 64.0f);
return 0;
}
CodePudding user response:
Use the c standard https://www.cplusplus.com/reference/ctgmath/ for macro pow version. or use constexpr statement for a pow function instead of a macro.
CodePudding user response:
Once you are sure your pow()
is used, hereby a proposal to make your pow()
function even better.
This idea might be difficult to implement, but in case you are regularly taking high powers, it might be worth-wile, let me show you an example:
You want to calculate pow(a,17)
.
Using your system, you need 16 multiplications.
Now let's turn 17 into binary, you get 10001, which means that, after some calculations, you can write pow(a,17)
as:
square(square(square(square(a))))*a, where square(a) = a*a
This leaves you with just 5 multiplications and might cause a performance increase. In fact, it goes from O(n)
to O(log n)
(where n
is the exponent).