2024年5月25日发(作者:)

首先在VS2013, Release模式下面跑了一下C++代码,时间是208ms。基本重现了

C++部分的结果。这个速度的确不能算快。在我们推测具体原因以前,一定一定要先profile,

看看每一行所花费的时间是多少(不成熟的优化是万恶之源)。在VS里这个非常简单(要

求VS Ultimate,但一般订阅了MSDN AA的学校都有免费的VS Ultimate)。只要点一个

按钮就好了。

我们可以看到,rand()占用了56.3%的时间,是性能的瓶颈。此外,整数的减法也占用

了33.4%的时间。

知道了原因以后就好办了。第一步,放狗搜索c++ slow rand。出来一大坨结果。其中

一个是

Need a fast random generator for c++

里面提供了一个快速的rand()实现。把代码粘进去。

unsigned int g_seed = 0;

unsigned int fastrand() {

g_seed = (214013 * g_seed + 2531011);

return (g_seed >> 16) & 0x7FFF;

}

一个诡异的地方是这是个整数运算应该极其快,而且不应该有类型转换在里面。但是

profiler说50%的时间都在float到long的类型转换上面(注意图片右上角)。进一步检查

发现,rand_max怎么是double。改成int,结果不变,时间变成43ms,进一步提速2倍。

再profile得到: 一个诡异的地方是这是个整数运算应该极其快,而且不应该有类型转换在

里面。但是profiler说50%的时间都在float到long的类型转换上面(注意图片右上角)。

进一步检查发现,rand_max怎么是double。改成int,结果不变,时间变成43ms,进一步

提速2倍。再profile得到:

这时候我们可以看到时间非常平均,也没有明显瓶颈了。优化结束。

考虑到我们机器的不同,我的43ms大约相当于你的机器的31ms,相比于vba的570ms

有了18倍的性能差异,感觉还是比较合理的。

几点讨论:

• 其实在这个回答里,核心并不是程序优化的具体技巧,而是拿到一个问题如何思考和

利用工具的通用方法。比如即使我们不知道profiler这个东西,通过搜索"代码 每一行 时间

"也可以很快知道有这样的工具叫做profiler,并且学会怎么使用。即使不知道rand这个函

数怎么加速,通过搜索引擎也可以找到别人写好的现成代码。另一方面是发现瓶颈之后也不

要着急自己修复,如果不是特别一目了然的话,先看看别人是怎么做的。站在巨人的肩膀上,

事半功倍。所以关键在于时刻知道自己想要的是什么,和分析-调研-实验的思维习惯。

• 具体关于程序优化,我们绝大多数人没有那么牛的经验,一眼就能看出问题在哪里。

所以遇到性能问题,第一反应应当是用profiler看看瓶颈到底在哪。而且一个经验是这个瓶

颈往往是很难猜的——比如这个例子直接看代码第一反应往往是用代数和工程方法去优化

算半径的那部分。但就算这部分做到极致,rand速度提不上去,最多也只能把时间降到原

来的一半,事倍功半。以前我写代码也会在写的时候用各种奇技淫巧提升速度,但后来发现

总体上程序的速度并没有得到提升。因为程序80%的时间其实花在20%的代码里,剩下80%

的代码就算花个两个月优化到速度无穷快,也还是白瞎。所以一个兼顾开发和运行效率的方

法是,先怎么方便开发怎么写,然后用profiler找到瓶颈再有针对性地优化。

• 前两点不仅可以节省时间,可能更重要的是,如果你面对的不仅是一个工程,而且是

老板,你要说服老板你这么做的原因。这些profile的结果、别人的讨论、你自己的实验结

果,都会1) 说服老板你这么做是对的,2) 给老板留下深刻印象:你干了很多事,脑子清楚。

以后升迁啥的都有帮助。

• 上面用的是Windows平台的VS,方便好用但也非常贵。如果是Linux平台下可以用

gprof(不晓得有没有GUI版本的,望指教)。Mac下可以用XCode。但基本思路都是一样

的。