gpt4 book ai didi

c++ - 为什么我的直接四元数乘法比 SSE 快?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:31:20 28 4
gpt4 key购买 nike

我经历了几个不同的四元数乘法实现,但我很惊讶地发现引用实现是迄今为止我最快的实现。这是有问题的实现:

inline static quat multiply(const quat& lhs, const quat& rhs)
{
return quat((lhs.w * rhs.x) + (lhs.x * rhs.w) + (lhs.y * rhs.z) - (lhs.z * rhs.y),
(lhs.w * rhs.y) + (lhs.y * rhs.w) + (lhs.z * rhs.x) - (lhs.x * rhs.z),
(lhs.w * rhs.z) + (lhs.z * rhs.w) + (lhs.x * rhs.y) - (lhs.y * rhs.x),
(lhs.w * rhs.w) - (lhs.x * rhs.x) - (lhs.y * rhs.y) - (lhs.z * rhs.z));
}

我已经尝试了一些其他实现,一些使用 SSE,一些不使用。这是一个此类 SSE 实现的示例,基本上是从 Bullet Physics 使用的库中复制的:

inline static __m128 multiplynew(__m128 lhs, __m128 rhs)
{
__m128 qv, tmp0, tmp1, tmp2, tmp3;
__m128 product, l_wxyz, r_wxyz, xy, qw;
vec4 sw;

tmp0 = _mm_shuffle_ps(lhs, lhs, _MM_SHUFFLE(3, 0, 2, 1));
tmp1 = _mm_shuffle_ps(rhs, rhs, _MM_SHUFFLE(3, 1, 0, 2));
tmp2 = _mm_shuffle_ps(lhs, lhs, _MM_SHUFFLE(3, 1, 0, 2));
tmp3 = _mm_shuffle_ps(rhs, rhs, _MM_SHUFFLE(3, 0, 2, 1));
qv = _mm_mul_ps(_mm_splat_ps(lhs, 3), rhs);
qv = _mm_madd_ps(_mm_splat_ps(rhs, 3), lhs, qv);
qv = _mm_madd_ps(tmp0, tmp1, qv);
qv = _mm_nmsub_ps(tmp2, tmp3, qv);
product = _mm_mul_ps(lhs, rhs);
l_wxyz = _mm_sld_ps(lhs, lhs, 12);
r_wxyz = _mm_sld_ps(rhs, rhs, 12);
qw = _mm_nmsub_ps(l_wxyz, r_wxyz, product);
xy = _mm_madd_ps(l_wxyz, r_wxyz, product);
qw = _mm_sub_ps(qw, _mm_sld_ps(xy, xy, 8));

sw.uiw = 0xffffffff;
return _mm_sel_ps(qv, qw, sw);
}

在启用优化的 Release模式下,我的简单引用实现比 bullet 的 SSE 实现快 70%-90%。在没有优化的 Debug模式下,它的运行速度提高了 3 倍。

我的第一个问题是,为什么会发生这种情况?

我的第二个问题是,有什么方法可以优化我的四元数-四元数乘法程序吗?我不想处理汇编,但我在其他地方经常使用 sse intrinsics。

(顺便说一句,如果重要的话,我的四元数的数据存储定义为 union { __m128 data; struct { float x, y, z, w; }; float f[4]; };)

我看了下反汇编。这是 multiply 的反汇编(快速的非 sse 反汇编):

00EC9940  movaps      xmm3,xmmword ptr [esp+0D0h]  
00EC9948 movaps xmm2,xmmword ptr [esp+0C0h]
00EC9950 movaps xmm4,xmm3
00EC9953 mulss xmm4,xmm5
00EC9957 movaps xmm0,xmm2
00EC995A mulss xmm0,xmm6
00EC995E mulss xmm3,xmm1
00EC9962 addss xmm4,xmm0
00EC9966 movss xmm0,dword ptr [esp+40h]
00EC996C mulss xmm0,xmm1
00EC9970 addss xmm4,xmm0
00EC9974 movss xmm0,dword ptr [esp+0F0h]
00EC997D mulss xmm0,xmm7
00EC9981 subss xmm4,xmm0
00EC9985 movss xmm0,dword ptr [esp+0F0h]
00EC998E mulss xmm0,xmm6
00EC9992 addss xmm3,xmm0
00EC9996 movaps xmm0,xmm2
00EC9999 movaps xmm2,xmmword ptr [esp+40h]
00EC999E mulss xmm0,xmm7
00EC99A2 addss xmm3,xmm0
00EC99A6 movaps xmm0,xmm2
00EC99A9 mulss xmm0,xmm5
00EC99AD mulss xmm2,xmm6
00EC99B1 subss xmm3,xmm0
00EC99B5 movss xmm0,dword ptr [esp+0D0h]
00EC99BE mulss xmm0,xmm7
00EC99C2 addss xmm2,xmm0
00EC99C6 movss xmm0,dword ptr [esp+0F0h]
00EC99CF mulss xmm0,xmm5
00EC99D3 addss xmm2,xmm0
00EC99D7 movss xmm0,dword ptr [esp+0C0h]
00EC99E0 mulss xmm0,xmm1
00EC99E4 movss xmm1,dword ptr [esp+0D0h]
00EC99ED mulss xmm1,xmm6
00EC99F1 subss xmm2,xmm0
00EC99F5 movss xmm0,dword ptr [esp+0C0h]
00EC99FE mulss xmm0,xmm5
00EC9A02 movaps xmm5,xmmword ptr [esp+50h]
00EC9A07 unpcklps xmm4,xmm2
00EC9A0A subss xmm1,xmm0
00EC9A0E movss xmm0,dword ptr [esp+0F0h]
00EC9A17 mulss xmm0,xmm5
00EC9A1B subss xmm1,xmm0
00EC9A1F movss xmm0,dword ptr [esp+40h]
00EC9A25 mulss xmm0,xmm7
00EC9A29 subss xmm1,xmm0
00EC9A2D unpcklps xmm3,xmm1
00EC9A30 unpcklps xmm4,xmm3
00EC9A33 movaps xmm5,xmm4
00EC9A36 movaps xmmword ptr [esp+30h],xmm5
00EC9A3B dec eax
00EC9A3C je SDL_main+58Ah (0EC9A5Ah)

下面是 multiplynew 的反汇编(慢 sse 的):

00329BF3  movaps      xmm6,xmm5  
00329BF6 mulps xmm6,xmm1
00329BF9 movaps xmm0,xmm5
00329BFC mov dword ptr [esp+6Ch],0FFFFFFFFh
00329C04 shufps xmm0,xmm5,93h
00329C08 movaps xmm1,xmm5
00329C0B mulps xmm4,xmm0
00329C0E movaps xmm0,xmmword ptr [esp+110h]
00329C16 movaps xmm3,xmm6
00329C19 shufps xmm1,xmm5,0FFh
00329C1D mulps xmm1,xmmword ptr [esp+40h]
00329C22 movaps xmm7,xmmword ptr [esp+60h]
00329C27 addps xmm3,xmm4
00329C2A mulps xmm0,xmm5
00329C2D subps xmm6,xmm4
00329C30 shufps xmm3,xmm3,4Eh
00329C34 addps xmm1,xmm0
00329C37 movaps xmm0,xmm5
00329C3A shufps xmm0,xmm5,0C9h
00329C3E subps xmm6,xmm3
00329C41 mulps xmm0,xmmword ptr [esp+120h]
00329C49 shufps xmm5,xmm5,0D2h
00329C4D mulps xmm5,xmmword ptr [esp+0C0h]
00329C55 andps xmm6,xmmword ptr [esp+60h]
00329C5A addps xmm1,xmm0
00329C5D subps xmm1,xmm5
00329C60 andnps xmm7,xmm1

我测试速度的方式是使用:

timer.update();
for (uint i = 0; i < 1000000; ++i)
{
temp1 = quat::multiply(temp1, q1);
}
timer.update();
printf("1M calls to multiplyOld took %fs.\n", timer.getDeltaTime());

(timer.getDeltaTime() 返回上次调用 timer.update() 与之前调用 timer.update() 之间耗时,以秒为单位。)

为什么我的非 sse 版本运行速度更快,尽管有更多指令......?我是不是读错了反汇编程序?

编辑:当我在 x64 中编译时,我发现 sse 版本比非 sse 版本运行得更快。

最佳答案

使用 SIMD 的最佳方法是一次将四(八)个独立四元数与 SSE(AVX)相乘。然而,这通常是不切实际的。在那种情况下,我建议查看 Agner Fog 的 vectorclass .在目录special他有一个文件 quaterinon.h .我转换了乘法函数以匹配您的代码。这只需要 SSE2 ( #include <emmintrin.h> )。

inline static __m128 multiplynew(__m128 a, __m128 b) {  
__m128 a1123 = _mm_shuffle_ps(a,a,0xE5);
__m128 a2231 = _mm_shuffle_ps(a,a,0x7A);
__m128 b1000 = _mm_shuffle_ps(b,b,0x01);
__m128 b2312 = _mm_shuffle_ps(b,b,0x9E);
__m128 t1 = _mm_mul_ps(a1123, b1000);
__m128 t2 = _mm_mul_ps(a2231, b2312);
__m128 t12 = _mm_add_ps(t1, t2);
const __m128i mask =_mm_set_epi32(0,0,0,0x80000000);
__m128 t12m = _mm_xor_ps(t12, _mm_castsi128_ps(mask)); // flip sign bits
__m128 a3312 = _mm_shuffle_ps(a,a,0x9F);
__m128 b3231 = _mm_shuffle_ps(b,b,0x7B);
__m128 a0000 = _mm_shuffle_ps(a,a,0x00);
__m128 t3 = _mm_mul_ps(a3312, b3231);
__m128 t0 = _mm_mul_ps(a0000, b);
__m128 t03 = _mm_sub_ps(t0, t3);
return _mm_add_ps(t03, t12m);
}

这是汇编输出(GCC 4.8 MASM 风格):

multiplynew(float __vector, float __vector):
movaps xmm2, xmm0
movaps xmm3, xmm0
movaps xmm5, xmm1
movaps xmm4, xmm1
shufps xmm2, xmm0, 229
shufps xmm4, xmm1, 158
shufps xmm3, xmm0, 122
shufps xmm5, xmm1, 1
mulps xmm3, xmm4
movaps xmm4, xmm1
mulps xmm2, xmm5
shufps xmm4, xmm1, 123
addps xmm2, xmm3
movaps xmm3, xmm0
shufps xmm3, xmm0, 159
shufps xmm0, xmm0, 0
xorps xmm2, XMMWORD PTR .LC0[rip]
mulps xmm3, xmm4
mulps xmm0, xmm1
subps xmm0, xmm3
addps xmm0, xmm2
ret

关于c++ - 为什么我的直接四元数乘法比 SSE 快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22215217/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com