通过 SSE4.1 计算两个短(4 元素)向量的点积更快
dpps
还是使用一系列 mulps
/ shufps
/ addps
上交所1的指示?
(对于大向量,当然最好将
mulps
/addps
放入向量累加器并在最后进行一个水平求和,有足够的求和向量来隐藏 FP 延迟)
答案可能与上下文密切相关,并且完全取决于它在更大的代码流中的使用位置和方式,以及您所使用的硬件。
从历史上看,当英特尔推出新指令时,他们并没有专门为其投入太多硬件区域。如果它得到足够的采用和使用,他们会在后代中投入更多的硬件。因此,就原始 ALU 性能而言,与 SSE2 方式相比,Penryn 上的
_mm_dp_ps
并不是特别令人印象深刻。另一方面,它确实需要更少的指令缓存中的指令,因此当更紧凑的编码性能更好时,它可能会有所帮助。
_mm_dp_ps
的真正问题是作为 SSE 4.1 的一部分,你不能指望它在每台现代 PC 上都得到支持(Valve 的 Steam 硬件调查显示,对于游戏玩家来说,它的支持率约为 85%)。因此,您最终必须编写受保护的代码路径而不是直线代码,这通常比使用指令所获得的好处要高。
2024 年 2 月更新:SSE2/SSE3 在 Steam 上的利用率为 100%。 SSSE3、SSE4.1、SSE 4.2 为 99%。甚至 AVX 和 AVX2 也处于 90 年代上层。如今,使用
构建游戏是合理的,检查启动时是否支持 SSE/SSE2/SSE3/SSSE3/SSE4.1/SSE4.2/AVX,并且不支持缺乏任何支持的机器其中。/arch:AVX
如果您正在为保证支持它的 CPU 制作二进制文件,那么它很有用。例如,如果您使用
/arch:AVX
(甚至是 /arch:AVX2
)进行构建,无论是因为您的目标是 Xbox One 等固定平台,还是正在构建 EXE/DLL 的多个版本,您可以假设 SSE 4.1 将受到支持好吧。
这实际上就是 DirectXMath 的作用:
inline XMVECTOR XMVector4Dot( FXMVECTOR V1, FXMVECTOR V2 )
{
#if defined(_XM_NO_INTRINSICS_)
XMVECTOR Result;
Result.vector4_f32[0] =
Result.vector4_f32[1] =
Result.vector4_f32[2] =
Result.vector4_f32[3] = V1.vector4_f32[0] * V2.vector4_f32[0] + V1.vector4_f32[1] * V2.vector4_f32[1] + V1.vector4_f32[2] * V2.vector4_f32[2] + V1.vector4_f32[3] * V2.vector4_f32[3];
return Result;
#elif defined(_M_ARM) || defined(_M_ARM64)
float32x4_t vTemp = vmulq_f32( V1, V2 );
float32x2_t v1 = vget_low_f32( vTemp );
float32x2_t v2 = vget_high_f32( vTemp );
v1 = vpadd_f32( v1, v1 );
v2 = vpadd_f32( v2, v2 );
v1 = vadd_f32( v1, v2 );
return vcombine_f32( v1, v1 );
#elif defined(__AVX__) || defined(__AVX2__)
return _mm_dp_ps( V1, V2, 0xff );
#elif defined(_M_IX86) || defined(_M_X64)
XMVECTOR vTemp2 = V2;
XMVECTOR vTemp = _mm_mul_ps(V1,vTemp2);
vTemp2 = _mm_shuffle_ps(vTemp2,vTemp,_MM_SHUFFLE(1,0,0,0));
vTemp2 = _mm_add_ps(vTemp2,vTemp);
vTemp = _mm_shuffle_ps(vTemp,vTemp2,_MM_SHUFFLE(0,3,0,0));
vTemp = _mm_add_ps(vTemp,vTemp2);
return _mm_shuffle_ps(vTemp,vTemp,_MM_SHUFFLE(2,2,2,2));
#else
#error Unsupported platform
#endif
}
这当然假设您将在其他向量运算中使用点积的“标量”结果。按照惯例,DirectXMath 返回这样的标量“分布”在返回向量上。
参见 DirectXMath:SSE4.1 和 SSE4.2
更新: 虽然不像 SSE/SSE2 支持那么普遍,但对于不使用
/arch:AVX
或 /arch:AVX2
构建的情况,您可能需要 SSE3 支持,并尝试:
inline XMVECTOR XMVector4Dot(FXMVECTOR V1, FXMVECTOR V2)
{
XMVECTOR vTemp = _mm_mul_ps(V1,V2);
vTemp = _mm_hadd_ps( vTemp, vTemp );
return _mm_hadd_ps( vTemp, vTemp );
}
也就是说,目前尚不清楚
hadd
在大多数情况下至少在点积方面优于 SSE/SSE2 添加和洗牌解决方案。