简体   繁体   English

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

[英]Define a macro which defines a pow function only in the case where exponent is an integer

Following to a profiling on my C++ code, it appears that pow function is used a lot.在对我的 C++ 代码进行分析之后,似乎经常使用pow函数。

Some of my pow functions have integer exponent and other non integer exponent.我的一些pow函数具有整数指数和其他非整数指数。 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.我认为这是由于我的宏中的else部分我调用了经典的pow函数。

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 :根据您的建议,我尝试了 3 个选项:

First option : Just add overload inline functions with base which is integer or double :第一个选项:只需添加带有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;
}

Result : runtime = 1m 08s结果:运行时间 = 1m 08s

Second option : Define a macro that calls via inline my_pow function if exponent n is not integer :第二个选项:如果指数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.86s结果:运行时间 = 51.86s

Third option : suggestion given in answer with template<typename type_t>第三个选项:使用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.84s结果:运行时间 = 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).所以最后,从这些第一个测试中,最好的选择是第二个,我将宏与调用pow函数的一般情况(整数和浮点指数)的函数结合使用。

Is there a more efficient solution or is the second option the best?是否有更有效的解决方案,或者第二种选择是最好的?

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

Instead of crafting hand-tuned solution, try using compiler optimizations -O3 -ffast-math or -O2 -ffast-math不要手工调整解决方案,尝试使用编译器优化-O3 -ffast-math-O2 -ffast-math

Take a look at this answer.看看这个答案。 https://stackoverflow.com/a/2940800/1550940 https://stackoverflow.com/a/2940800/1550940

and

If you are using double precision (double), std::pow(x, n) will be slower than the handcrafted equivalent unless you use -ffast-math, in which case, there is absolutely no overhead.如果您使用双精度 (double),除非您使用 -ffast-math,否则 std::pow(x, n) 将比手工制作的等价物慢,在这种情况下,绝对没有开销。

https://baptiste-wicht.com/posts/2017/09/cpp11-performance-tip-when-to-use-std-pow.html https://baptiste-wicht.com/posts/2017/09/cpp11-performance-tip-when-to-use-std-pow.html

Use the c++ standard https://www.cplusplus.com/reference/ctgmath/ for macro pow version.对宏 pow 版本使用 c++ 标准https://www.cplusplus.com/reference/ctgmath/ or use constexpr statement for a pow function instead of a macro.或者对 pow 函数使用 constexpr 语句而不是宏。

Once you are sure your pow() is used, hereby a proposal to make your pow() function even better.一旦你确定你的pow()被使用了,特此提出一个让你的pow()函数更好的建议。

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) .您想计算pow(a,17)
Using your system, you need 16 multiplications.使用你的系统,你需要 16 次乘法。

Now let's turn 17 into binary, you get 10001, which means that, after some calculations, you can write pow(a,17) as:现在让我们把 17 变成二进制,你得到 10001,这意味着,经过一些计算,你可以将pow(a,17)写成:

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

This leaves you with just 5 multiplications and might cause a performance increase.这让您只剩下 5 次乘法,并且可能会导致性能提升。 In fact, it goes from O(n) to O(log n) (where n is the exponent).事实上,它从O(n)O(log n) (其中n是指数)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM