游戏开发者联盟

基础数学计算的成本

开方的速度到底是多快?需要刻意避免开方么?

速度测试

本例使用的是float,单精度浮点数。(双精度的会略微高些,最多一倍)
于是写个简单的程序测试一下,进行3亿次计算,注意不要让编译器优化掉计算部分:

以下是结果,依次是加法,乘法,除法,标准库开方,雷神3开方:
[2020-06-30 09:21:16.338] [debu] [ 20800] :9.0072e+15 add 232
[2020-06-30 09:21:16.573] [debu] [ 20800] :inf multiply 232
[2020-06-30 09:21:17.347] [debu] [ 20800] :0 div 771
[2020-06-30 09:21:18.471] [debu] [ 20800] :5.49756e+11 std::sqrt 1109
[2020-06-30 09:21:18.834] [debu] [ 20800] :1.98178e+19 magic sqrt 362
最后的数字是执行时间的毫秒数。

结论

从结果看,现代cpu的速度非常快,+-*的成本最低,除法和开方基本一样,魔法开方的成本略低。
所以程序照常写就行,没必要刻意回避开方。

如果真的要优化的话,尽量使用+-*以及位运算,避免除法,在精度许可的情况下,用魔法开方代替std::sqrt.

参考数据

以下是浮点计算的延迟和成本(单位:时钟周期)

  • 单精度浮点加/减(ADDSS / SUBSS):延迟3,实际执行1
  • 双精度浮点加法(ADDSD / SUBSD):延迟3,实际执行1
  • 单精度浮点乘法(MULSS):延迟5,实际执行0.5
  • 双精度浮点乘法(MULSD):延迟5,实际执行0.5
  • 单精度浮点除法(DIVSS):延迟10–13,实际执行7
  • 双精度浮点除法(DIVSD):延迟10–20,实际执行8–14
  • 单精度浮点平方根(SQRTSS):延迟11,实际执行7
  • 双精度浮点平方根(SQRTSD):延迟16,实际执行8–14
  • 单精度近似倒数平方根(大约11个有效位精度,RSQRTSS):延迟3,实际执行1

存储成本:

  • L1缓存加载:4
  • L2缓存加载:12
  • L3非共享缓存加载:34
  • L3共享缓存加载:〜60
  • 主内存加载:60个周期+ 100ns
  • L1 TLB失误:8
  • L2 TLB遗失:12(一位评论者正确地指出,这不包括分辨率的成本,在最坏的情况下,该成本可能为半毫秒)
  • 分支预测错误:15–20

开方的速度至少都和2级缓存的速度相当。

参数资料

In computer programming, why do you avoid the square root?