c – 如何优化“u [0] * v [0] u [2] * v [2]”与SSE或GLSL的代码行

我有以下功能(从开源项目 “recast navigation”):
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
///  @param[in]     u       A vector [(x,y,z)]
///  @param[in]     v       A vector [(x,z)]
/// @return The dot product on the xz-plane.
///
/// The vectors are projected onto the xz-plane,so the y-values are ignored.
inline float dtVdot2D(const float* u,const float* v)
{
    return u[0]*v[0] + u[2]*v[2];
}

我通过VS2010 cpu性能测试运行它,并告诉我,在这个功能的所有重写代码代码中,u [0] * v [0] u [2] * v [2]是cpu最热的.

cpu如何优化(例如通过SSE或GLSL like GLM (if it is easier or faster and appropriate in such case))这行?

编辑:呼叫出现的上下文:

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中功能的适当并行化/矢量化实现.

然而,它需要数据重组,因为我们将一次对4个三角形进行并行处理.

我会分步骤,并在这里和那里使用指令名称,但请使用C内在函数(_mm_load_ps(),_mm_sub_ps()等,它们在VC中的xmmintrin.h) – 当我说这个注册这个只是__m128.

步骤1.

我们根本不需要Y坐标,所以我们设置了X和Z对的指针.每次呼叫至少提供4对(即4个三角形).我将每个X和Z对称为一个顶点.

第2步.

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

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

步骤3.

现在,使用单个减法指令,可以一次计算2个顶点的增量(v0,v1,v2).

计算v0,v1和v2不仅对于前2个三角形,而且对于后者2!
如我所说,每个输入应该共提供4个顶点,或8个浮点数.只需重复步骤2和3的数据.

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

步骤4.

点产品.为了平行重心计算(稍后),我们需要在1个单个寄存器中的每个4个三角形的每个点积的结果.

所以在这里你可以计算dot01,我们会做同样的事情,但是一次就是4个三角形.每个v-register包含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对从我们的第一个寄存器,从第二个寄存器,将它们添加在一起,并将它们存储在目标寄存器的第一个,第二个,第三个和第四个组件中. Ergo:在这一点上,我们已经有了所有4个三角形的特殊点阵

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

步骤5.

最后的计算(根据提供的代码我可以看到)是重心的东西.这是您的代码中的100%标量计算.但是现在您的输入不是标量点产品结果(即单个浮点数),它们是SSE向量/寄存器,其中4个三角形中的每一个都具有点积.

因此,如果您使用对所有4个浮点运算的并行SSE操作进行向量化,则最终将最终得到4个高度的1个寄存器(或结果),每个三角形1个.

因为我的午餐休息已经过去了,因为我不会拼出这一个,但是鉴于我给出的设置/想法是最后一步,不应该很难弄清楚.

我知道这个想法有点延伸,需要一些喜欢上面的代码,也许有一些质量好的铅笔和纸张,但是它会很快(如果你愿意,你甚至可以添加OpenMP ).

祝你好运 :)

(和原谅我的模糊的解释,我可以鞭打的功能,如果需要=))

UPDATE

我已经写了一个实现,并没有像我预期的那样去,主要是因为Y组件涉及到你最初粘贴在你的问题中的代码段(我查看了).我在这里做的不仅仅是要求您将所有点重新排列到XZ对,并且每4进给一次,而且还会为每个4个三角形的Y值提供3个指针(点A,B和C).从本地角度看,这是最快的.我仍然可以修改它,要求从被调查者的最后的侵入性更改,但请让我知道什么是可取的.

然后一个免责声明:这段代码是直截了当的(我发现与SSE编译器工作相当),他们可以重新组织,看到合适,x86 / x64 cpu也占据了他们的份额).另外命名,这不是我的风格,它不是任何人,只是做它你认为合适.

希望它有帮助,如果不是,我会很乐意再次来过.如果这是一个商业项目,我也可以选择让我在船上我猜;)

无论如何,我把它放在了pastebin:http://pastebin.com/20u8fMEb

相关文章

/** C+⬑ * 默认成员函数 原来C++类中,有6个默认成员函数: 构造函数 析构函数 拷贝...
#pragma once // 1. 设计一个不能被拷贝的类/* 解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运...
C类型转换 C语言:显式和隐式类型转换 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译...
//异常的概念/*抛出异常后必须要捕获,否则终止程序(到最外层后会交给main管理,main的行为就是终止) try...
#pragma once /*Smart pointer 智能指针;灵巧指针 智能指针三大件//1.RAII//2.像指针一样使用//3.拷贝问...
目录<future>future模板类成员函数:promise类promise的使用例程:packaged_task模板类例程...