22

游戏性能优化杂谈(八)

 3 years ago
source link: https://zhuanlan.zhihu.com/p/268898846
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我认为性能优化可以分为宏观层面的优化,与微观层面的优化。前者多与开发技术及流程的选型有关,因此应该在项目的前期就开始考虑,而且是越早考虑费用效益比越好。而后者则是对于成品性能热点区域针对性的优化,通常在项目的中后期进行,且效果相对于前者来说一般较为有限(但是并非没有意义)。

前几篇主要谈了CPU端宏观优化的一些思路,本篇谈谈CPU端微观优化的一些常见方法。

大部分游戏其实在CPU端并不是计算密集型的应用,而更加倾向于内存访问密集型。因此,能否妥善利用CPU当中的高速缓存,对于游戏的性能影响十分巨大。

当代CPU当中的缓存,除了获取指令专用的缓存之外,对于数据通常有L1至L3三级高速缓存。虽然不同的CPU厂家以及型号在缓存的大小和组织方式方面存在差异,但是大致上L1为每个CPU核心独享,大小为十几K到几十K,延迟为1~数个CPU时钟周期;L2为4~8个核心所共享,大小为数百K到数M,延迟为数十个CPU时钟周期;L3为CPU整体上一个,一般作为victom cache,接收从L2当中被换出的数据,CPU核心不直接访问。

这些高速缓存与内存(RAM)之间的数据交换通常是以一定单位进行的,比如64字节/128字节。这个单位被称为是cacheline。高速缓存与内存之间的数据线也并非只有32或者64条,而是高达数百条。也就是说,两者可以一次交换数个字节。但是这些字节必须在空间上连续。

这也就是说,虽然RAM是支持随机访问的,但是只有符合一定模式(pattern)的访问,才能获得最大限的cache加速效果。这种差异对于事务性的应用程序来说可能微乎其微,但是对于1秒钟要循环几十次甚至上百次的游戏引擎内循环来说,就显得十分重要。

另外一个要注意的就是cache污染的问题。这不仅仅发生在被多核心共享的L2和L3这个层级,甚至发生在单核心独享的L1层级。甚至,因为L1的大小通常非常有限,所以更容易发生。原因在于单核心上的线程切换执行,以及“超线程”技术,就是一个物理核心在理论上可以看作两个或以上逻辑核心的技术。

因此在排布线程的时候,要注意不同线程的内存访问模式。当线程不多的时候,尽量一线程一核心,不要使用“超线程”。当线程数量远大于核心数的时候,要将线程分优先级,将核心线程(比如主线程、渲染线程)绑定到专用核心,将其它次要线程以低优先级绑定到其它核心,尽量避免其干扰核心线程(因为核心线程是critical path,即它的执行时间决定了整体的性能)

同时要注意线程在CPU核心间的迁移。线程在共享L2的两个核心间迁移的成本,是与在两个使用不同L2核心间迁移的成本完全不同的。重要线程最好是做到不迁移,辅助线程则根据其内存访问模式进行分组绑定。

目前最新一代的CPU在分支预测方面也有了长足的进步,导入了一些类似深度学习的技术。这种技术对于游戏引擎这样高速“短”循环的程序十分有效。所以这方面现在倒是不用花太大力气,努力将程序写得更加简洁易懂更好。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK