114 c c++ optimization
这两种方法在C中的效率更高?怎么样:
pow(x,3)
Run Code Online (Sandbox Code Playgroud)
与
x*x*x // etc?
Run Code Online (Sandbox Code Playgroud)
Emi*_*ier 78
我使用此代码测试了x*x*...vs pow(x,i)for small 之间的性能差异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";
}
Run Code Online (Sandbox Code Playgroud)
结果是:
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
Run Code Online (Sandbox Code Playgroud)
请注意,我累积每个pow计算的结果,以确保编译器不会优化它.
如果我使用该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
Run Code Online (Sandbox Code Playgroud)
这是在运行Ubuntu 9.10 64bit的Intel Core Duo上.使用gcc 4.4.1编译并使用-o2优化.
所以在C中,是的x*x*x会更快pow(x, 3),因为没有pow(double, int)过载.在C++中,它将大致相同.(假设我的测试方法是正确的.)
这是对An Markm的评论的回应:
即使using namespace std发出了指令,如果第二个参数pow为a int,那么将调用std::pow(double, int)重载<cmath>而不是::pow(double, double)来自<math.h>.
此测试代码确认了该行为:
#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;
}
Run Code Online (Sandbox Code Playgroud)
sbi*_*sbi 30
这是一个错误的问题.正确的问题是:"对于我的代码的人类读者,哪一个更容易理解?"
如果速度很重要(稍后),请不要问,但要测量.(在此之前,衡量一下优化它是否会产生明显的差异.)在此之前,编写代码以便最容易阅读.
编辑
只是为了明确这一点(虽然它应该已经存在):突破性的加速通常来自于使用更好的算法,改善数据的局部性,减少动态内存的使用,预先计算结果等等.它们很少来自微优化单个函数调用,并且在他们这样做的情况下,他们在很少的地方这样做,这只能通过仔细(和耗时)的分析找到,更多的时候通过非常不直观的方式来加速事情(比如插入noop 对一个平台的优化有时是对另一个平台的优化(这就是为什么你需要衡量,而不是要求,因为我们不完全了解/拥有你的环境).
让我再次强调这一点:即使在这样的事情重要的几个应用程序,他们没有在他们使用的最重要的地方,而这是非常不可能的,你会发现,他们通过查看代码问题的地方.你确实需要首先确定热点,否则优化代码只是浪费时间.
即使单个操作(如计算某个值的平方)占用应用程序执行时间的10%(IME非常罕见),即使优化它也可节省该操作所需时间的50%(IME是甚至更多,更罕见的是,你仍然使应用程序只花费少5%的时间.
您的用户需要一个秒表才能注意到这一点.(我想在大多数情况下,大多数用户都不会注意到加速度低于20%的任何东西.这就是你需要找到的四个这样的点.)
Pup*_*ppy 16
x*x或者x*x*x会比自己更快pow,因为pow必须处理一般情况,而x*x具体而言.此外,您可以忽略函数调用等.
但是,如果您发现自己是这样的微优化,则需要获取分析器并进行一些严格的分析.压倒性的可能性是你永远不会注意到两者之间的任何差异.
我也想知道性能问题,并希望编译器根据@EmileCormier的答案对其进行优化.但是,我担心他展示的测试代码仍然允许编译器优化掉std :: pow()调用,因为每次调用都使用相同的值,这将允许编译器存储结果和在循环中重复使用它 - 这可以解释所有情况下几乎相同的运行时间.所以我也看了一下.
这是我使用的代码(test_pow.cpp):
#include <iostream>
#include <cmath>
#include <chrono>
class Timer {
public:
explicit Timer () : from (std::chrono::high_resolution_clock::now()) { }
void start () {
from = std::chrono::high_resolution_clock::now();
}
double elapsed() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - from).count() * 1.0e-6;
}
private:
std::chrono::high_resolution_clock::time_point from;
};
int main (int argc, char* argv[])
{
double total;
Timer timer;
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += std::pow (i,2);
std::cout << "std::pow(i,2): " << timer.elapsed() << "s (result = " << total << ")\n";
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += i*i;
std::cout << "i*i: " << timer.elapsed() << "s (result = " << total << ")\n";
std::cout << "\n";
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += std::pow (i,3);
std::cout << "std::pow(i,3): " << timer.elapsed() << "s (result = " << total << ")\n";
total = 0.0;
timer.start();
for (double i = 0.0; i < 1.0; i += 1e-8)
total += i*i*i;
std::cout << "i*i*i: " << timer.elapsed() << "s (result = " << total << ")\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是使用以下编译的:
g++ -std=c++11 [-O2] test_pow.cpp -o test_pow
Run Code Online (Sandbox Code Playgroud)
基本上,不同之处在于std :: pow()的参数是循环计数器.正如我所担心的那样,性能上的差异是显而易见的.如果没有-O2标志,我系统上的结果(Arch Linux 64位,g ++ 4.9.1,Intel i7-4930)是:
std::pow(i,2): 0.001105s (result = 3.33333e+07)
i*i: 0.000352s (result = 3.33333e+07)
std::pow(i,3): 0.006034s (result = 2.5e+07)
i*i*i: 0.000328s (result = 2.5e+07)
Run Code Online (Sandbox Code Playgroud)
通过优化,结果同样引人注目:
std::pow(i,2): 0.000155s (result = 3.33333e+07)
i*i: 0.000106s (result = 3.33333e+07)
std::pow(i,3): 0.006066s (result = 2.5e+07)
i*i*i: 9.7e-05s (result = 2.5e+07)
Run Code Online (Sandbox Code Playgroud)
所以看起来编译器至少尝试优化std :: pow(x,2)情况,但不是std :: pow(x,3)情况(它需要比std :: pow长约40倍) (x,2)案例).在所有情况下,手动扩展表现更好 - 但特别是对于电源3情况(快60倍).如果在紧密循环中运行整数幂大于2的std :: pow(),这绝对值得记住......
最有效的方法是考虑乘法的指数增长。检查 p^q 的代码:
template <typename T>
T expt(T p, unsigned q){
T r =1;
while (q != 0) {
if (q % 2 == 1) { // if q is odd
r *= p;
q--;
}
p *= p;
q /= 2;
}
return r;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
95428 次 |
| 最近记录: |