二进制操作比memmove更快吗?

时间:2013-09-04 11:16:01

标签: c performance embedded arm memcpy

我正在写一个数字滤波器,我需要保留最后的X值并将它们加在一起。

现在有两种可能的方法。我使用memmove移动整个数组以为下一个值腾出空间,并在我的求和算法中将数组的正确索引作为硬编码值。

memmove(&Fifo[0], &Fifo[1], 12 * 4); // Shift array to the left

Result += Factor[1] * (Fifo[5] + Fifo[7]);
Result += Factor[2] * (Fifo[4] + Fifo[8]);
Result += Factor[3] * (Fifo[3] + Fifo[9]);
Result += Factor[4] * (Fifo[2] + Fifo[10]);
Result += Factor[5] * (Fifo[1] + Fifo[11]);
Result += Factor[6] * (Fifo[0] + Fifo[12]);

或者,我不复制任何内存,而是递增计数器,并使用模运算(如循环缓冲区)计算每个索引。

i++; // Increment the index

Result += Factor[1] * (Fifo[(i + 5) % 13] + Fifo[(i + 7) % 13]);
Result += Factor[2] * (Fifo[(i + 4) % 13] + Fifo[(i + 8) % 13]);
Result += Factor[3] * (Fifo[(i + 3) % 13] + Fifo[(i + 9) % 13]);
Result += Factor[4] * (Fifo[(i + 2) % 13] + Fifo[(i + 10) % 13]);
Result += Factor[5] * (Fifo[(i + 1) % 13] + Fifo[(i + 11) % 13]);
Result += Factor[6] * (Fifo[(i + 0) % 13] + Fifo[(i + 12) % 13]);

由于它是一个嵌入式ARM cpu,我想知道什么会更有效率。由于我假设CPU必须在内部移动至少一个32位值来进行模运算,所以只是移动整个数组的速度和计算正确的索引一样快吗?

5 个答案:

答案 0 :(得分:2)

如果你需要知道哪个更快,你需要做基准测试。如果你想 知道原因,你需要检查装配。

话虽如此,也有一半的解决方案可能足够好: 使用大于所需的缓冲区,并在缓冲区已满时执行memmove。 这样你只需要跟踪起始偏移量,而不必担心 关于循环缓冲区带来的问题。你必须使用更多内存。

因此,如果您希望拥有5个元素并使用10个元素的缓冲区,那么您只需要 每5次插入memmove。 (当你可以进行10次插入时,第一次通过除外)

答案 1 :(得分:2)

我在Cortex M0(LPC11C14)上完成了15针FIR滤波器(Savitzky-Golay用于测量线电压)。

我发现在我的情况下,复制比使用16号循环缓冲区并使用模运算符计算索引要慢一些。请注意,16是2的幂,这使得除法非常便宜。

我尝试了几种变体并使用端口引脚来测量执行时间,我建议你也这样做。

答案 2 :(得分:1)

假设32位值,ARM上的Modulo可以在2个汇编指令中执行,但移动内存也是如此(1将其放入寄存器,1将其取出)。所以这里没有明确的答案;它将取决于它周围的代码。

我的直觉是你应该采用循环缓冲方式。

答案 3 :(得分:0)

还有第三种方法既不需要memmove也不需要modulo涉及两个开关块。我懒得输入它,但想法是你计算偏移量,使用第一个开关来计算缓冲区的一半,然后重新计算偏移量并使用第二个开关计算另一半缓冲。你基本上进入第二个开关,第一个'左'。请注意,在一个开关块中,必须恢复指令顺序。

答案 4 :(得分:0)

我的直觉说,memmove可能会导致各种内存冲突并阻止内部绕过,因为你加载并存储到同一区域,甚至可能是相同的缓存行。有些处理器只是放弃优化它并推迟所有内存操作,有效地序列化它们(嵌入式CPU可能很简单,无论如何都可以做到这一点,但我说的是一般情况 - 在x86甚至是cortex a15你可能得到更大的惩罚)