我正在为双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;
}
1024 * 1024值的计算需要~0.016 s。 (两个线程 - 每个线程计算一半的数组)。天真地解释,一次迭代的计算需要122个周期。这似乎有点慢。但瓶颈在哪里?
我甚至尝试使用pld
命令预加载L2缓存中的元素,通过计算每次迭代最多20个值来“展开”循环,并重新排序指令以使处理器不等待内存。我的速度没有那么快(最快加快0.001秒)。
您对加快计算有什么建议吗?
答案 0 :(得分:1)
我对NEON的了解并不多。但是,我认为您有数据依赖性会导致性能问题。我建议你用一些加载来填充循环,然后将它们放在 multiply 和 store 之间。我认为 store 可能会阻塞,直到 multiply 完成。
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
);
这样你就可以用乘法并行加载。您将需要过度分配源数组或更改循环索引并进行最终的乘法和存储。如果NEON操作不影响条件代码,您也可以重新订购 subs 并将其放置得更早。
编辑实际上, Cortex A-9媒体处理引擎文档建议交错使用ARM和NEON指令,因为它们可以并行执行。此外,NEON指令似乎设置 FPSCR 而不是ARM CPSR ,因此重新排序subs
会减少执行时间。您也可以缓存对齐循环。