使用SSE指令点产品性能

时间:2016-06-17 10:48:08

标签: performance assembly optimization x86 simd

通过SSE 4.1指令集的dpps指令或使用一系列addpsshufps和{来计算两个向量的点积是否更快来自SSE 1的{1}}

1 个答案:

答案 0 :(得分:5)

答案可能非常符合上下文,并且完全取决于它在更大的代码流中使用的位置和方式以及您正在使用的硬件。

从历史上看,当英特尔推出新指令时,他们并未专注于太多硬件领域。如果它被充分利用和使用,他们会在后代中使用更多的硬件。因此,与原始ALU性能方面的SSE2方式相比,Penryn上的_mm_dp_ps并不特别令人印象深刻。另一方面,它确实需要更少的I-cache指令,因此当更紧凑的编码表现更好时,它可能会有所帮助。

_mm_dp_ps的真正问题是作为SSE ​​4.1的一部分,你不能指望它在每一台现代PC上得到支持(Valve的Steam硬件调查将其约为85%对于游戏玩家而言)。因此,您最终必须编写受保护的代码路径而不是直线代码,这通常比使用该指令所带来的好处要多。

有用的是,如果您为保证支持它的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返回这些标量' splatted'跨越返回向量。

请参阅DirectXMath: SSE4.1 and SSE4.2

更新虽然不如SSE / SSE2支持普遍存在,但您可能需要SSE3支持,因为您不能使用/arch:AVX/arch:AVX2构建尝试:

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添加和改组解决方案中至少是点积,并不是很明显。