【发布时间】:2011-06-19 12:14:24
【问题描述】:
我正在尝试规范化 4d 矢量。
我的第一个方法是使用 SSE 内在函数——它为我的向量算术提供了 2 倍的速度提升。 这是基本代码:(v.v4 是输入)(使用 GCC)(所有这些都是内联的)
//find squares
v4sf s = __builtin_ia32_mulps(v.v4, v.v4);
//set t to square
v4sf t = s;
//add the 4 squares together
s = __builtin_ia32_shufps(s, s, 0x1B);
t = __builtin_ia32_addps(t, s);
s = __builtin_ia32_shufps(s, s, 0x4e);
t = __builtin_ia32_addps(t, s);
s = __builtin_ia32_shufps(s, s, 0x1B);
t = __builtin_ia32_addps(t, s);
//find 1/sqrt of t
t = __builtin_ia32_rsqrtps(t);
//multiply to get normal
return Vec4(__builtin_ia32_mulps(v.v4, t));
我检查了反汇编,它看起来像我所期望的那样。我看不出有什么大问题。
无论如何,然后我尝试使用近似值:(我从谷歌得到这个)
float x = (v.w*v.w) + (v.x*v.x) + (v.y*v.y) + (v.z*v.z);
float xhalf = 0.5f*x;
int i = *(int*)&x; // get bits for floating value
i = 0x5f3759df - (i>>1); // give initial guess y0
x = *(float*)&i; // convert bits back to float
x *= 1.5f - xhalf*x*x; // newton step, repeating this step
// increases accuracy
//x *= 1.5f - xhalf*x*x;
return Vec4(v.w*x, v.x*x, v.y*x, v.z*x);
它的运行速度比 SSE 版本稍快! (大约快 5-10%)它的结果也非常准确 - 找到长度时我会说 0.001! 但是.. 由于类型双关语,GCC 给了我蹩脚的严格别名规则。
所以我修改它:
union {
float fa;
int ia;
};
fa = (v.w*v.w) + (v.x*v.x) + (v.y*v.y) + (v.z*v.z);
float faHalf = 0.5f*fa;
ia = 0x5f3759df - (ia>>1);
fa *= 1.5f - faHalf*fa*fa;
//fa *= 1.5f - faHalf*fa*fa;
return Vec4(v.w*fa, v.x*fa, v.y*fa, v.z*fa);
现在修改后的版本(没有警告)运行速度变慢了!!它的运行速度几乎是 SSE 版本运行速度的 60%(但结果相同)!这是为什么呢?
所以这里有问题:
- 我的 SSE 实现是否正确?
- SSE 真的比正常的 fpu 操作慢吗?
- 为什么第三个代码这么慢?
【问题讨论】:
-
了解您使用的 CPU 会有所帮助。例如。旧的 x86 CPU(Core 2 之前)的 SSE 功能非常差。
-
@Pepe:较旧的 x86 CPU 只有 64 位 SSE 执行单元(128 位操作被分解为两个 64 位微操作),因此您通常不会看到浮点 SIMD 与标量相比有多少速度提升代码。在较新的 CPU 上尝试您的代码 - 例如Core 2 Duo,Core i5/i7,具有完整的 128 位 SSE 单元。
-
@Suma:是的,我很困惑,并认为 shuf 是循环携带。我修复了它,现在它是准确的,但仍然没有更快!我将在另一台计算机上尝试。
-
我认为您应该能够使用两次随机播放和两个加法来对所有四个分量求和:1,2,3,4 随机播放到 2,2,4,4 加到 1+2,x , 3+4,x 洗牌到 3+4,x,3+4,x,加到 1+2+3+4,x,x,x。然后你可以使用 scalar builtin_ia32_rsqrtss,将它打乱到所有槽中,最后相乘。
标签: c++ normalization profile sse approximation