gpt4 book ai didi

android - 如何使用 ARM NEON 优化循环 4D 矩阵 vector 乘法?

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

我正在使用 ARM NEON 汇编程序优化 4D(128 位)矩阵 vector 乘法。

如果我将矩阵和 vector 加载到 NEON 寄存器并对其进行转换,我将不会获得很大的性能提升,因为切换到 NEON 寄存器需要 20 个周期。此外,我为每个乘法重新加载矩阵,尽管它没有改变。

有足够的寄存器空间来一次对更多 vector 执行转换。这正在提高性能。

但是..

我想知道如果我在汇编程序中对所有顶点(增加指针)进行循环,这个操作会有多快。但是我刚开始使用 Neon 汇编程序,但不知道该怎么做。有人可以帮助我吗?

我想要实现的目标:

  1. 加载矩阵和第一个 vector
  2. 存储循环计数“count”和..
  3. -- 循环开始--
  4. 执行乘加(进行转换)
  5. 将q0写入vOut
  6. 将指针 vIn 和 vOut 增加 4(128 位)
  7. 将 vIn 加载到 q5。
  8. -- 循环结束--

循环的现有 C 版本:

void TransformVertices(ESMatrix* m, GLfloat* vertices, GLfloat* normals, int count)
{
GLfloat* pVertex = vertices;
int i;

// iterate trough vertices only one at a time
for (i = 0; i < count ; i ++)
{
Matrix4Vector4Mul( (float *)m, (float *)pVertex, (float *)pVertex);
pVertex += 4;
}

//LoadMatrix( (const float*) m);

//// two at a time
//for (i = 0; i < count ; i += 2)
//{
// Matrix4Vector4Mul2( (float *)m, (float *)pVertex, (float *)(pVertex + 4));
// pVertex += 8;
//}
}

以下 NEON 版本的代码仅进行一次转换:

void Matrix4Vector4Mul (const float* m, const float* vIn, float* vOut)
{
asm volatile
(

"vldmia %1, {q1-q4 } \n\t"
"vldmia %2, {q5} \n\t"

"vmul.f32 q0, q1, d10[0] \n\t"
"vmla.f32 q0, q2, d10[1] \n\t"
"vmla.f32 q0, q3, d11[0] \n\t"
"vmla.f32 q0, q4, d11[1] \n\t"

"vstmia %0, {q0}"

: // no output
: "r" (vOut), "r" (m), "r" (vIn)
: "memory", "q0", "q1", "q2", "q3", "q4", "q5"
);

}

C 版本的转换:

void Matrix4Vector4Mul (const float* m, const float* vIn, float* vOut)
{
Vertex4D* v1 = (Vertex4D*)vIn;
Vertex4D vOut1;
Vertex4D* l0;
Vertex4D* l1;
Vertex4D* l2;
Vertex4D* l3;

// 4x4 Matrix with members m00 - m33
ESMatrix* m1 = (ESMatrix*)m;

l0 = (Vertex4D*)&m1->m00;
vOut1.x = l0->x * v1->x;
vOut1.y = l0->y * v1->x;
vOut1.z = l0->z * v1->x;
vOut1.w = l0->w * v1->x;

l1 = (Vertex4D*)&m1->m10;
vOut1.x += l1->x * v1->y;
vOut1.y += l1->y * v1->y;
vOut1.z += l1->z * v1->y;
vOut1.w += l1->w * v1->y;

l2 = (Vertex4D*)&m1->m20;
vOut1.x += l2->x * v1->z;
vOut1.y += l2->y * v1->z;
vOut1.z += l2->z * v1->z;
vOut1.w += l2->w * v1->z;

l3 = (Vertex4D*)&m1->m30;
vOut1.x += l3->x * v1->w;
vOut1.y += l3->y * v1->w;
vOut1.z += l3->z * v1->w;
vOut1.w += l3->w * v1->w;

*(vOut) = vOut1.x;
*(vOut + 1) = vOut1.y;
*(vOut + 2) = vOut1.z;
*(vOut + 3) = vOut1.w;
}

性能:(转换 > 90 000 个顶点 | Android 4.0.4 SGS II)

C-Version:    190 FPS 
NEON-Version: 162 FPS ( .. slower -.- )

--- LOAD Matrix only ONCE (seperate ASM) and then perform two V's at a time ---

NEON-Version: 217 FPS ( + 33 % NEON | + 14 % C-Code )

最佳答案

您是否尝试过使用编译器标志?

-mcpu=cortex-a9 -mtune=cortex-a9 -mfloat-abi=softfp -mfpu=neon -O3

在这种情况下对我来说做得很好(gcc 4.4.3,随 Android NDK 8b 一起分发)。尝试通过定义内部函数静态和内联以及将矩阵(m[X][0] 东西)移动到静态全局变量或只是将 Matrix4Vector4Mul 合并到循环中并创建矩阵局部变量而不是在函数中传递它来尝试拥有紧凑的源代码- gcc 在那里并不聪明。

当我这样做时,我得到主循环的下方。

  a4:   ed567a03    vldr    s15, [r6, #-12]
a8: ee276aa0 vmul.f32 s12, s15, s1
ac: ee676aa8 vmul.f32 s13, s15, s17
b0: ed564a04 vldr s9, [r6, #-16]
b4: ee277a88 vmul.f32 s14, s15, s16
b8: ed165a02 vldr s10, [r6, #-8]
bc: ee677a80 vmul.f32 s15, s15, s0
c0: ed565a01 vldr s11, [r6, #-4]
c4: e2833001 add r3, r3, #1
c8: ee046a89 vmla.f32 s12, s9, s18
cc: e1530004 cmp r3, r4
d0: ee446aaa vmla.f32 s13, s9, s21
d4: ee047a8a vmla.f32 s14, s9, s20
d8: ee447aa9 vmla.f32 s15, s9, s19
dc: ee056a22 vmla.f32 s12, s10, s5
e0: ee456a01 vmla.f32 s13, s10, s2
e4: ee057a21 vmla.f32 s14, s10, s3
e8: ee457a02 vmla.f32 s15, s10, s4
ec: ee056a8b vmla.f32 s12, s11, s22
f0: ee456a83 vmla.f32 s13, s11, s6
f4: ee057aa3 vmla.f32 s14, s11, s7
f8: ee457a84 vmla.f32 s15, s11, s8
fc: ed066a01 vstr s12, [r6, #-4]
100: ed466a04 vstr s13, [r6, #-16]
104: ed067a03 vstr s14, [r6, #-12]
108: ed467a02 vstr s15, [r6, #-8]
10c: e2866010 add r6, r6, #16
110: 1affffe3 bne a4 <TransformVertices+0xa4>

有 4 个加载、4 个乘法、12 个乘法和累加以及 4 个存储,这与您在 Matrix4Vector4Mul 中所做的相匹配。

如果您对编译器生成的代码仍然不满意,请传递编译器“-S”以获取程序集输出并将其用作进一步改进的起点,而不是从头开始。

您还应该检查 vertices 缓存行大小是否对齐(Cortex-A9 为 32 字节)以获得良好的数据流。

对于矢量化,有 gcc 选项,例如 -ftree-vectorizer-verbose=9 来打印矢量化的信息。还可以在 gcc 文档 this one 中搜索以了解如何引导 gcc 或需要修改哪些内容才能使乘法矢量化。这听起来可能需要深入研究很多,但从长远来看,它比“手动矢量化”更有成效。

关于android - 如何使用 ARM NEON 优化循环 4D 矩阵 vector 乘法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12976539/

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