我正在写一个数字滤波器,我需要保留最后的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位值来进行模运算,所以只是移动整个数组的速度和计算正确的索引一样快吗?
答案 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你可能得到更大的惩罚)