在C语言中,这两种方法中哪一种更有效?那这样怎么样:

pow(x,3)

x*x*x // etc?

推荐答案

UPDATE 2021

我修改了基准代码,如下所示:

  • std::chrono用于定时测量,而不是升压
  • 使用C++11 <random>代替rand()
  • 避免可能被吊出的重复操作.基本参数是不断变化的.

我用GCC 10-O2得到了以下结果(单位:秒):

exp     c++ pow     c pow       x*x*x...
2       0.204243    1.39962     0.0902527   
3       1.36162     1.38291     0.107679    
4       1.37717     1.38197     0.106103    
5       1.3815      1.39139     0.117097

GCC 10-O3与GCC 10-O2几乎相同.

GCC 10-O2-ffast数学:

exp     c++ pow     c pow       x*x*x...
2       0.203625    1.4056      0.0913414   
3       0.11094     1.39938     0.108027    
4       0.201593    1.38618     0.101585    
5       0.102141    1.38212     0.10662

GCC 10-O3-FAST-MATH:

exp     c++ pow     c pow       x*x*x...
2       0.0451995   1.175       0.0450497   
3       0.0470842   1.20226     0.051399    
4       0.0475239   1.18033     0.0473844   
5       0.0522424   1.16817     0.0522291

随着12-O2的叮当声:

exp     c++ pow     c pow       x*x*x...
2       0.106242    0.105435    0.105533    
3       1.45909     1.4425      0.102235    
4       1.45629     1.44262     0.108861    
5       1.45837     1.44483     0.1116

铿锵12-O3与铿锵12-O2几乎相同.

使用clang 12-O2-ffast-MATH:

exp     c++ pow     c pow       x*x*x...
2       0.0233731   0.0232457   0.0231076   
3       0.0271074   0.0266663   0.0278415   
4       0.026897    0.0270698   0.0268115   
5       0.0312481   0.0296402   0.029811    

Clang12-O3-ffast数学与Clang12-O2-ffast数学几乎相同.

这台机器是Linux 5.4.0-73-generic x86_64上的Intel Core i7-7700K.

结论:

  • 使用GCC 10(无ffast数学),x*x*x...always
  • 在GCC 10-O2-ffast数学中,对于odd个指数,std::pow的速度与x*x*x...的速度一样快
  • 在GCC 10-O3-ffast数学中,所有测试用例中std::pow的速度都是x*x*x...的速度,大约是-O2的两倍.
  • 有了GCC 10,Cpow(double, double)总是慢得多
  • 使用Clang 12(no-ffast-ath),对于大于2的指数,x*x*x...更快
  • 使用clang 12-ffast-ath,所有方法都会产生类似的结果
  • 对于叮当声12,对于积分指数,pow(double, double)的速度和std::pow的速度一样快
  • 编写基准而不让编译器超过你是hard.

我最终会在我的机器上安装一个更新版本的GCC,并在安装后更新结果.

以下是更新后的基准代码:

#include <cmath>
#include <chrono>
#include <iostream>
#include <random>

using Moment = std::chrono::high_resolution_clock::time_point;
using FloatSecs = std::chrono::duration<double>;

inline Moment now()
{
    return std::chrono::high_resolution_clock::now();
}

#define TEST(num, expression) \
double test##num(double b, long loops) \
{ \
    double x = 0.0; \
\
    auto startTime = now(); \
    for (long i=0; i<loops; ++i) \
    { \
        x += expression; \
        b += 1.0; \
    } \
    auto elapsed = now() - startTime; \
    auto seconds = std::chrono::duration_cast<FloatSecs>(elapsed); \
    std::cout << seconds.count() << "\t"; \
    return x; \
}

TEST(2, b*b)
TEST(3, b*b*b)
TEST(4, b*b*b*b)
TEST(5, b*b*b*b*b)

template <int exponent>
double testCppPow(double base, long loops)
{
    double x = 0.0;

    auto startTime = now();
    for (long i=0; i<loops; ++i)
    {
        x += std::pow(base, exponent);
        base += 1.0;
    }
    auto elapsed = now() - startTime;

    auto seconds = std::chrono::duration_cast<FloatSecs>(elapsed); \
    std::cout << seconds.count() << "\t"; \

    return x;
}

double testCPow(double base, double exponent, long loops)
{
    double x = 0.0;

    auto startTime = now();
    for (long i=0; i<loops; ++i)
    {
        x += ::pow(base, exponent);
        base += 1.0;
    }
    auto elapsed = now() - startTime;

    auto seconds = std::chrono::duration_cast<FloatSecs>(elapsed); \
    std::cout << seconds.count() << "\t"; \

    return x;
}

int main()
{
    using std::cout;
    long loops = 100000000l;
    double x = 0;
    std::random_device rd;
    std::default_random_engine re(rd());
    std::uniform_real_distribution<double> dist(1.1, 1.2);
    cout << "exp\tc++ pow\tc pow\tx*x*x...";

    cout << "\n2\t";
    double b = dist(re);
    x += testCppPow<2>(b, loops);
    x += testCPow(b, 2.0, loops);
    x += test2(b, loops);

    cout << "\n3\t";
    b = dist(re);
    x += testCppPow<3>(b, loops);
    x += testCPow(b, 3.0, loops);
    x += test3(b, loops);

    cout << "\n4\t";
    b = dist(re);
    x += testCppPow<4>(b, loops);
    x += testCPow(b, 4.0, loops);
    x += test4(b, loops);

    cout << "\n5\t";
    b = dist(re);
    x += testCppPow<5>(b, loops);
    x += testCPow(b, 5.0, loops);
    x += test5(b, loops);

    std::cout << "\n" << x << "\n";
}

Old Answer, 2010

对于Small i,我使用以下代码测试了x*x*...pow(x,i)之间的性能差异:

#include <cstdlib>
#include <cmath>
#include <boost/date_time/posix_time/posix_time.hpp>

inline boost::posix_time::ptime now()
{
    return boost::posix_time::microsec_clock::local_time();
}

#define TEST(num, expression) \
double test##num(double b, long loops) \
{ \
    double x = 0.0; \
\
    boost::posix_time::ptime startTime = now(); \
    for (long i=0; i<loops; ++i) \
    { \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
        x += expression; \
    } \
    boost::posix_time::time_duration elapsed = now() - startTime; \
\
    std::cout << elapsed << " "; \
\
    return x; \
}

TEST(1, b)
TEST(2, b*b)
TEST(3, b*b*b)
TEST(4, b*b*b*b)
TEST(5, b*b*b*b*b)

template <int exponent>
double testpow(double base, long loops)
{
    double x = 0.0;

    boost::posix_time::ptime startTime = now();
    for (long i=0; i<loops; ++i)
    {
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
        x += std::pow(base, exponent);
    }
    boost::posix_time::time_duration elapsed = now() - startTime;

    std::cout << elapsed << " ";

    return x;
}

int main()
{
    using std::cout;
    long loops = 100000000l;
    double x = 0.0;
    cout << "1 ";
    x += testpow<1>(rand(), loops);
    x += test1(rand(), loops);

    cout << "\n2 ";
    x += testpow<2>(rand(), loops);
    x += test2(rand(), loops);

    cout << "\n3 ";
    x += testpow<3>(rand(), loops);
    x += test3(rand(), loops);

    cout << "\n4 ";
    x += testpow<4>(rand(), loops);
    x += test4(rand(), loops);

    cout << "\n5 ";
    x += testpow<5>(rand(), loops);
    x += test5(rand(), loops);
    cout << "\n" << x << "\n";
}

结果如下:

1 00:00:01.126008 00:00:01.128338 
2 00:00:01.125832 00:00:01.127227 
3 00:00:01.125563 00:00:01.126590 
4 00:00:01.126289 00:00:01.126086 
5 00:00:01.126570 00:00:01.125930 
2.45829e+54

请注意,我会累计每次功率计算的结果,以确保编译器不会将其优化.

如果我使用std::pow(double, double)loops = 1000000l版本,我会得到:

1 00:00:00.011339 00:00:00.011262 
2 00:00:00.011259 00:00:00.011254 
3 00:00:00.975658 00:00:00.011254 
4 00:00:00.976427 00:00:00.011254 
5 00:00:00.973029 00:00:00.011254 
2.45829e+52

这是运行Ubuntu 9.10 64位的英特尔酷睿双核处理器.使用带-O2优化的GCC 4.4.1编译.

所以在C语言中,是的,x*x*xpow(x, 3)快,因为没有pow(double, int)过载.在C++中,它将大致相同.(假设我测试中的方法是正确的.)


这是对Markm comments 的回应:

即使发出了using namespace std指令,如果pow的第二个参数是int,则将调用来自<cmath>std::pow(double, int)重载,而不是来自<math.h>::pow(double, double).

此测试代码确认了以下行为:

#include <iostream>

namespace foo
{

    double bar(double x, int i)
    {
        std::cout << "foo::bar\n";
        return x*i;
    }


}

double bar(double x, double y)
{
    std::cout << "::bar\n";
    return x*y;
}

using namespace foo;

int main()
{
    double a = bar(1.2, 3); // Prints "foo::bar"
    std::cout << a << "\n";
    return 0;
}

C++相关问答推荐

segfault在C中使用getline()函数

球体—立方体重叠:无、部分或全部?

ATTiny1606定时器TCA 0中断未触发

数据包未从DPDK端口传输到内核端口

从组播组地址了解收到的数据包长度

ATmega328P EEPROM未写入

我在这里正确地解释了C操作顺序吗?

_泛型控制表达式涉及数组碰撞警告的L值转换错误?

cairo 剪辑区域是否存在多个矩形?

为 struct 中的数组动态分配内存时出错

链接到底是如何工作的,我在这里到底做错了什么

类型定义 struct 与简单的类型定义 struct

不使用任何预定义的C函数进行逐位运算

Zlib:解压缩大文件导致";无效代码长度设置";错误

哪些C++功能可以在外部C块中使用

未为同一文件中的函数执行DirectFunctionCall

从管道读取数据时丢失

在链表中插入一个值

获取 struct 中匿名 struct 的大小

段错误try 访问静态字符串,但仅有时取决于构建环境