Hyr*_*axK 8 c++ optimization assembly arm neon
我正在为双Cortex-A9处理器优化两个单维阵列的元素乘法.Linux正在运行,我正在使用GCC 4.5.2编译器.
所以以下是我的C++内联汇编程序函数.src1,src2和dst是16字节对齐的.
更新:可测试代码:
void Multiply(
const float* __restrict__ src1,
const float* __restrict__ src2,
float* __restrict__ dst,
const unsigned int width,
const unsigned int height)
{
int loopBound = (width * height) / 4;
asm volatile(
".loop: \n\t"
"vld1.32 {q1}, [%[src1]:128]! \n\t"
"vld1.32 {q2}, [%[src2]:128]! \n\t"
"vmul.f32 q0, q1, q2 \n\t"
"vst1.32 {q0}, [%[dst]:128]! \n\t"
"subs %[lBound], %[lBound], $1 \n\t"
"bge .loop \n\t"
:
:[dst] "r" (dst), [src1] "r" (src1), [src2] "r" (src2),
[lBound] "r" (loopBound)
:"memory", "d0", "d1", "d2", "d3", "d4", "d5
);
}
//The following function describes how to test the element wise multiplication
void Test()
{
const unsigned int width = 1024, height = 1024;
float* src1 __attribute__((aligned(16))) = new float[width * height];
float* src2 __attribute__((aligned(16))) = new float[width * height];
float* dst __attribute__((aligned(16))) = new float[width * height];
for(unsigned int i = 0; i < (width * height); i++)
{
src1[i] = (float)rand();
src2[i] = (float)rand();
}
Multiply(src1, src2, dst, width, height);
std::cout << dst[0] << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
1024*1024值的计算需要~0.016 s.(两个线程 - 每个线程计算一半的数组).天真地解释,一次迭代的计算需要122个周期.这似乎有点慢.但瓶颈在哪里?
我甚至尝试了pld在L2缓存中预加载元素的命令,通过计算每次迭代最多20个值并重新排序指令来"展开"循环,以使处理器不等待内存.我没有得到那么多的加速(最快0.001秒).
你对加快计算有什么建议吗?
我对NEON不太了解。但是,我认为您存在导致性能问题的数据依赖性。我建议您用一些负载来启动循环,然后将它们放置在乘法和存储之间。我认为商店可能会阻塞直到乘法完成。
asm volatile(
"vld1.32 {q1}, [%[src1]:128]! \n\t"
"vld1.32 {q2}, [%[src2]:128]! \n\t"
".loop: \n\t"
"vmul.f32 q0, q1, q2 \n\t"
"vld1.32 {q1}, [%[src1]:128]! \n\t"
"vld1.32 {q2}, [%[src2]:128]! \n\t"
"vst1.32 {q0}, [%[dst]:128]! \n\t"
"subs %[lBound], %[lBound], $1 \n\t"
"bge .loop \n\t"
:
:[dst] "r" (dst), [src1] "r" (src1), [src2] "r" (src2),
[lBound] "r" (loopBound)
:"memory", "d0", "d1", "d2", "d3", "d4", "d5
);
Run Code Online (Sandbox Code Playgroud)
这样您应该能够将负载与乘法并行。您将需要过度分配源数组或更改循环索引并进行最终的乘法和存储。如果 NEON 操作不影响条件代码,您也可以重新排序子项并将其放置在更早的位置。
编辑:事实上,Cortex A-9 媒体处理引擎文档建议交叉使用 ARM 和 NEON 指令,因为它们可以并行执行。此外,NEON 指令似乎设置FPSCR而不是 ARM CPSR,因此重新排序subs会减少执行时间。您还可以缓存对齐循环。