3

我正在编写一个数字滤波器,我需要保留最后的 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 位值才能进行模运算,难道仅仅移动整个数组就与计算正确的索引一样快吗?

4

5 回答 5

3

如果您需要知道哪个更快,则需要进行基准测试。如果您想知道原因,您需要检查程序集。

话虽如此,也有一个足够好的中途解决方案:使用大于需要的缓冲区,并且仅memmove在缓冲区已满时才这样做。这样,您只需跟踪起始偏移量,而不必担心循环缓冲区带来的问题。但是,您必须使用更多内存。

因此,如果您希望拥有 5 个元素并为 10 个元素使用缓冲区,则只需memmove每 5 次插入进行一次。(除了第一遍,你可以做 10 次插入)

于 2013-09-04T11:42:41.480 回答
2

我已经在 Cortex M0 (LPC11C14) 上为尺寸为 15 的 FIR 滤波器(用于测量线路电压的 Savitzky-Golay)完成了该操作。

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

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

于 2013-09-22T08:03:07.723 回答
1

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

我的直觉告诉你应该采用循环缓冲方法。

于 2013-09-04T11:43:32.587 回答
0

第三种方式既不需要 memmove 也不需要涉及两个 switch 块的模。我懒得打字了,但想法是你计算偏移量,使用第一个开关计算缓冲区的“一半”,然后重新计算偏移量并使用第二个开关计算另一半缓冲。您基本上输入第二个开关,第一个开关“离开”。请注意,在一个 switch 块中,指令顺序必须恢复。

于 2013-09-04T12:39:09.467 回答
0

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

于 2013-09-04T13:21:30.133 回答