Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何优化;u[0]*v[0]&x2B;u[2]*v[2]";带有SSE或GLSL的代码行_C++_C_Optimization_Sse_Glm Math - Fatal编程技术网

C++ 如何优化;u[0]*v[0]&x2B;u[2]*v[2]";带有SSE或GLSL的代码行

C++ 如何优化;u[0]*v[0]&x2B;u[2]*v[2]";带有SSE或GLSL的代码行,c++,c,optimization,sse,glm-math,C++,C,Optimization,Sse,Glm Math,我有以下功能(来自开源项目): 我对它进行了VS2010 CPU性能测试,结果表明,在所有重铸的代码中,此函数中的代码行u[0]*v[0]+u[2]*v[2]是CPU最热的 我如何优化CPU(通过SSE或例如)这条生产线 编辑:调用出现的上下文: bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) { float v0

我有以下功能(来自开源项目):

我对它进行了VS2010 CPU性能测试,结果表明,在所有重铸的代码中,此函数中的代码行
u[0]*v[0]+u[2]*v[2]
是CPU最热的

我如何优化CPU(通过SSE或例如)这条生产线

编辑:调用出现的上下文:

bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) {
    float v0[3], v1[3], v2[3];
    dtVsub(v0, c,a);
    dtVsub(v1, b,a);
    dtVsub(v2, p,a);

    const float dot00 = dtVdot2D(v0, v0);
    const float dot01 = dtVdot2D(v0, v1);
    const float dot02 = dtVdot2D(v0, v2);
    const float dot11 = dtVdot2D(v1, v1);
    const float dot12 = dtVdot2D(v1, v2);

    // Compute barycentric coordinates
    const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
    const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
    const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

您可以使用SSE指令实现单点产品,但结果不会比现在编写的代码快(甚至可能慢)。重写可能会破坏有助于当前版本的编译器优化

为了从使用SSE或CUDA重写中获得任何好处,您必须优化要求该点积的循环。这对于CUDA尤其如此,因为执行一个点积的开销将是巨大的。只有将数千个向量发送到GPU以计算数千个点积,才能看到加速。同样的想法也适用于CPU上的SSE,但您可能会看到在较小数量的操作上有所改进。不过,它仍将不止是一个点积


最容易尝试的方法可能是
g++-ftree矢量化
。GCC将能够内联您的小函数,然后尝试为您优化循环(事实上,它可能已经是了,但没有SSE指令)。树矢量器将尝试自动执行您手动执行的操作。它并不总是成功的。

在纸上尝试了一些东西之后,我想到了一些可能对你有用的东西。它是SSE中功能的适当并行化/矢量化实现

然而,它确实需要数据重组,因为我们将同时对4个三角形进行并行处理

我将把它分成几个步骤,在这里或那里使用指令名,但请使用C内部函数(_-mm_-load_-ps(),_-mm_-sub_-ps()等,它们在VC的xmmintrin.h中)——当我谈到寄存器时,这只是指u-m128

第一步

我们根本不需要Y坐标,所以我们设置指向X和Z对的指针。每次调用至少提供4对(即总共4个三角形)我将每个X和Z对称为顶点。

第二步

使用MOVAPS(要求指针与16位对齐)将每个指针指向的前两个顶点加载到寄存器中

从A加载的寄存器如下所示:[a0.x,a0.z,a1.x,a1.z]

第三步

现在,使用一条减法指令,您可以一次计算两个顶点的增量(v0、v1、v2)

不仅计算前两个三角形的v0、v1和v2,还计算后两个三角形的v0、v1和v2! 正如我所说的,每个输入总共应该提供4个顶点,或8个浮点。只需对该数据重复步骤2和3即可

现在我们有2对vx寄存器,每对寄存器包含2个三角形的结果。我将它们称为vx_0(第一对)和vx_1(第二对),其中x是从0到2

第四步

Dot产品。为了并行化重心计算(稍后),我们需要在一个寄存器中计算4个三角形的每个点积的结果

例如,在计算dot01时,我们也会这样做,但同时计算4个三角形。每个v寄存器包含2个向量的结果,因此我们首先将它们相乘

假设u和v——点积函数中的参数——现在是v0_0和v1_0(用于计算dot01):

将u和v相乘得到:[(v0_0.x0)*(v1_0.x0),(v0_0.z0)*(v1_0.z0),(v0_0.x1)*(v1_0.x1),(v0_0.z1)*(v1_0.z1)]

由于.x0/.z0和.x1/.z1的原因,这看起来可能会令人困惑,但看看在步骤2:a0,a1加载了什么

如果到目前为止这仍然感觉模糊,拿起一张纸,从头开始写

接下来,对于相同的点积,对v0_1和v1_1进行乘法(,即第二对三角形)

现在我们有2个寄存器,每个寄存器有2个X&Z对(总共4个顶点),相乘并准备相加形成4个单独的点积。SSE3有一条指令来完成这项工作,它被称为HADDPS:

xmm0=[A,B,C,D] xmm1=[E,F,G,H]

HADDPS xmm0,xmm1执行以下操作:

xmm0=[A+B,C+D,E+F,G+H]

它将从第一个寄存器中取出X&Z对,从第二个寄存器中取出,将它们相加,并将它们存储在目标寄存器的第一、第二、第三和第四个组件中。因此,在这一点上,我们得到了所有4个三角形的特殊点积

**现在对所有dot产品重复此过程:dot00等**

第五步

最后一次计算(根据我所提供的代码所见)是重心。这是代码中的100%标量计算。但您的输入现在不是标量点积结果(即单个浮点),而是SSE向量/寄存器,4个三角形中的每一个都有点积

因此,如果您通过使用对所有4个浮点运算的并行SSE操作将其完全矢量化,最终将得到1个寄存器(或结果),其中包含4个高度,每个三角形1个高度

因为我的午休时间已经过了,所以我不打算详细说明这一点,但考虑到我给出的设置/想法,这是最后一步,应该不难理解

我知道这个想法有点牵强,需要上面的代码中的一些爱,也许还需要一些用铅笔和纸的高质量时间,但它会很快(如果你愿意,你甚至可以在以后添加OpenMP)

祝你好运:)

(请原谅我的模糊解释,如果需要,我可以快速调用函数be=)

更新

我已经编写了一个实现,但它并没有像我预期的那样进行,主要是因为Y组件超出了您最初粘贴的代码部分
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) {
    float v0[3], v1[3], v2[3];
    dtVsub(v0, c,a);
    dtVsub(v1, b,a);
    dtVsub(v2, p,a);

    const float dot00 = dtVdot2D(v0, v0);
    const float dot01 = dtVdot2D(v0, v1);
    const float dot02 = dtVdot2D(v0, v2);
    const float dot11 = dtVdot2D(v1, v1);
    const float dot12 = dtVdot2D(v1, v2);

    // Compute barycentric coordinates
    const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
    const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
    const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Copied and modified from xnamathvector.inl
XMFINLINE XMVECTOR XMVector2DotXZ
(
    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[2] * V2.vector4_f32[2];

    return Result;

#elif defined(_XM_SSE_INTRINSICS_)
    // Perform the dot product on x and z
    XMVECTOR vLengthSq = _mm_mul_ps(V1,V2);
    // vTemp has z splatted
    XMVECTOR vTemp = _mm_shuffle_ps(vLengthSq,vLengthSq,_MM_SHUFFLE(2,2,2,2));
    // x+z
    vLengthSq = _mm_add_ss(vLengthSq,vTemp);
    vLengthSq = _mm_shuffle_ps(vLengthSq,vLengthSq,_MM_SHUFFLE(0,0,0,0));
    return vLengthSq;
#else // _XM_VMX128_INTRINSICS_
#endif // _XM_VMX128_INTRINSICS_
}

bool dtClosestHeightPointTriangle(FXMVECTOR p, FXMVECTOR a, FXMVECTOR b, FXMVECTOR c, float& h)
{
    XMVECTOR v0 = XMVectorSubtract(c,a);
    XMVECTOR v1 = XMVectorSubtract(b,a);
    XMVECTOR v2 = XMVectorSubtract(p,a);

    XMVECTOR dot00 = XMVector2DotXZ(v0, v0);
    XMVECTOR dot01 = XMVector2DotXZ(v0, v1);
    XMVECTOR dot02 = XMVector2DotXZ(v0, v2);
    XMVECTOR dot11 = XMVector2DotXZ(v1, v1);
    XMVECTOR dot12 = XMVector2DotXZ(v1, v2);

    // Compute barycentric coordinates
    XMVECTOR invDenom = XMVectorDivide(XMVectorReplicate(1.0f), XMVectorSubtract(XMVectorMultiply(dot00, dot11), XMVectorMultiply(dot01, dot01)));

    XMVECTOR u = XMVectorMultiply(XMVectorSubtract(XMVectorMultiply(dot11, dot02), XMVectorMultiply(dot01, dot12)), invDenom);
    XMVECTOR v = XMVectorMultiply(XMVectorSubtract(XMVectorMultiply(dot00, dot12), XMVectorMultiply(dot01, dot02)), invDenom);
}