定义一个宏,它只在指数为整数的情况下定义pow函数



对我的c++代码进行分析后,发现pow函数被大量使用。

我的一些pow函数有一个整数指数和另一个非整数指数。我只对整数指数感兴趣。

为了提高性能,我正在寻找这样定义宏的方法:

#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;
})

但是我没有得到预期的关于运行时的增益。我认为这是由于else部分在我的宏,我调用经典的pow函数。

如何在"编写"宏之前提前确定指数的类型?在预处理过程中?

理想情况下,我希望这个宏只应用于指数是整数的情况,但似乎我的尝试是不相关的。

根据你的建议,我尝试了三个选项:

第一个选项:只添加重载内联函数与基础是integerdouble:

// 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;
}

结果:运行时间= 1分08秒

第二个选项:定义一个宏,如果指数n不是整数,则通过内联my_pow函数调用:

// 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.86 sec

第三个选项:以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(int))
{
return pow(x, n);
}
else
{
double value = 1;
for (type_t i = 0; i < n; ++i) value *= x;
return value;
}
}

Result: runtime = 52.84 sec

因此,最后,从这些第一个测试中,最好的选择是第二个,我使用宏与调用pow函数的一般情况的函数相结合(整数和浮点指数)。

是否有更有效的解决方案,或者第二个选择是最好的?

如果你只需要在浮点类型之间切换,你可以使用模板来代替宏。

#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;
}

一旦你确定你的pow()被使用,这里有一个建议,使你的pow()功能更好。

这个想法可能很难实现,但如果你经常使用高功率,这可能是值得的,让我给你看一个例子:

你想计算pow(a,17).
使用你的系统,你需要16次乘法。

现在让我们把17变成二进制,你得到10001,这意味着,经过一些计算,你可以把pow(a,17)写成:

square(square(square(square(a))))*a, where square(a) = a*a

这样只剩下5次乘法,可能会提高性能。事实上,它是从O(n)O(log n)(其中n是指数)。

编辑

让我告诉你,你可以做到这一点:想象你需要计算一个数字n的25次方。然后,首先,你需要知道你需要正方形的次数,使用简单的公式:

a = round_down(log(25) / log(2)) = 4
因此,您需要从0到4的所有正方形,并创建以下数组(sqr(n)代表n的正方形):
[1, n, sqr(n), sqr(sqr(n)), sqr(sqr(sqr(n))), sqr(sqr(sqr(sqr(n))))] with meanings:
0, 1, 2,      4,           8,                16

你需要最后一部分(16次幂),你剩下9,它比8大。
你需要8,你只剩下1,它比4小。你不需要4。
你不需要2。
你需要1,你只剩下0,在这里循环停止。

So: n^25 = n * sqr(sqr(sqr(n))) * sqr(sqr(sqr(sqr(n))))

(我承认,这个解释不是百分之百的,但是你明白我的意思)

与其手工调整解决方案,不如尝试使用编译器优化:-O3 -ffast-math-O2 -ffast-math

看看这个答案。

哪个更有效率?用幂的平方还是用自身乘以它?

如果您使用双精度(double), std::pow(x, n)将比手工制作的等效对象慢,除非您使用- fast-math,在这种情况下,绝对没有开销。

c++ 11性能提示:何时使用std::pow?

使用c++标准https://www.cplusplus.com/reference/ctgmath/作为宏版本。或者为pow函数使用constexpr语句,而不是宏。

相关内容

  • 没有找到相关文章

最新更新