« 不变量及运算优化 | 返回首页

矩阵 decompose 的一点优化

我们的游戏引擎中,有个重要的功能是将一个矩阵分解成 S 缩放,R 旋转,T 位移三个分量。这里 T 直接取矩阵的第四行即可,代价比较高的是 S 和 R 的分解,其中 R 又取决于 S 的提取。

但是,游戏中大量的矩阵中是不包含缩放的,即 S 分量大多是 (1,1,1) 。一旦不用缩放,又可以简化 R 提取的操作。所以我打算对传统算法做一点优化。在提取 S 的时候多加一次判断,看值是否接近 1 。

计算 S 的方法是将矩阵的前三行当作三个 vector 3 分别取 length 。length 其实是取 dot 然后计算 sqrt。由于大多数情况预测 dot 值很可能为 1 ,那么当 dot 接近 1 的时候,就不必再开方了。

我很好奇判断一个数字是否接近 1 有没有什么技巧可以提高性能,所以我写了三个版本测试。

static inline int
equal_one(float f) {
  union {
    float f;
    uint32_t n;
  } u;
  u.f = f;
  return ((u.n + 0x1f) & ~0x3f) == 0x3f800000;  // float 1
}

#define EPSILON 0.000001f 

static inline int
equal_one_float1(float f) {
  float  v = f - 1.0f;
  return fabs(v) < EPSILON;
}

static inline int
equal_one_float2(float f) {
  float  v = f - 1.0f;
  return -EPSILON < v && v < EPSILON;
}

后两个版本很常规,区别在于要不要调用 fabs 取绝对值。第一个版本则是针对 float 的 ieee 754 的二进制表示做的,是一个整数运算的版本。

我在 PC 上做了简单的性能测试,第一版和第二版性能几乎一致,而第三版要慢 3 倍。我认为原因在于第三版多了一次比较操作(有分支情况)。而针对 ieee 754 的 float 做 fabs 本质上仅仅是将高位置为 0 ,甚至比整数版 abs 还要快。现代编译器一般都会做此优化。

暂时没有在 arm CPU 上测试。不过如果不开 NEON 的话,arm 的浮点运算将全部导致函数调用,肯定不如第一个版本好。

第一版的缺点是扩展性不足,一旦从 float 换成 double 就要重新编写。

Comments

i love this blog
thanks!!

看了下源码,skynet的sharetable.loadfile接口加载文件会不受skynet.codecache影响。

云大,skynet的sharetable.loadfile接口加载文件会受skynet.codecache影响吗?在线等。

http://evanw.github.io/float-toy/
刚好看到这个好玩的东西。

&#128077;

Post a comment

非这个主题相关的留言请到:留言本