硬件启发循环。废话?

时间:2012-05-25 19:33:42

标签: c performance loops embedded verilog

前几天我在Verilog学到了一个很酷的技巧。当你需要反复做某事时。您可以使用移位寄存器来计算增量数。只需将1从LSB移到MSB,当它到达MSB时就完成了。

在C中它会是这样的:

for(j=0b1; !(j & (1<<16)); j=j<<1)
{
/*do a thing 16 times*/
}

我知道它因位宽而限制使用,但它不涉及任何添加,所以它很快。 所以我的问题:有没有用过这个?在C语言或任何其他高级语言中使用是否值得?

可能在资源有限的嵌入式系统中。

由于

8 个答案:

答案 0 :(得分:8)

这非常值得。它使代码更清晰,更难阅读,性能差异可以忽略不计。

您的编译器可以比您更好地执行这些类型的优化。出于性能原因,这样的短循环甚至可能会展开。但是,如果您编写循环,编译器可能无法轻易解决这个问题,那么您甚至可能会降低程序速度。

这实际上是微优化的一个例子,几乎肯定不会对你的程序的运行时间产生明显的影响。

答案 1 :(得分:5)

在我看来,大多数评论/回答的人并不真正理解提问者在谈论什么。 Verilog语言用于硬件设计,硬件设计与软件设计完全不同,没有CPU周期或类似的东西。但是,简短的答案仍然是:不。答案很长:

确保换班比加法简单得多。对于移位,从FF(触发器)到FF的逻辑要少得多。另外,必须将进位从LSB位传播到MSB位,这意味着log2(N)逻辑电平(N是计数器将达到的最高值)。 另一方面,移位寄存器将使用N个FF,而加法器仅使用log2(N)FF。 因此,性能/区域交易也很大程度上依赖于N.一些关于加法器的“独立”信息: http://en.wikipedia.org/wiki/Adder_%28electronics%29 无法找到类似的转移文章,但一旦你理解了加法器,移位器应该是显而易见的。

在RTL中设计状态机时,这可能很重要。但是你提出的代码实际上与上面的代码无关。 verilog中的'for'循环意味着所有'工作'将在单个循环中完成。所以实际上会有N个逻辑。此循环与实现无关。它甚至可能只会混淆verilog编译器吐出一些奇怪的东西并影响模拟(其中CPU周期很重要,而且上面的答案都是有效的)。对工具有更多经验的人可以对此发表评论。

答案 2 :(得分:2)

(根据Stefan的回答,我假设您在询问受Verilog版本启发的C版本,而不是在Verilog中这样做。)

在许多体系结构中,这实际上更糟糕,因为位移需要额外的指令,而循环变量的添加完全是免费的。

完全?

是。因为在许多体系结构中,单个指令会递减计数器,如果它非零,则递减计数器 - 这些指令所花费的时间与任何其他比较和分支指令一样多。然而,如果您正在进行转换,则需要额外的指令周期。如果你的平台没有“比较平等和分支”指令,那就更糟了 - 而不是所有的指令都做到了;有些会让你在两条指令中减去并与零比较。

即使在没有递减比较分支指令的RISC平台上,倒计时循环也可能更快,因为你可以简单地减去(一条指令)并使用branch-if-nonero指令 - 而在你的循环中,你在branch-if-zero之前需要一个移位(一条指令)和一个按位和(一条指令)。而且假设你甚至有一个如果为零的分支。

此外,对于一个简单的for (i = 0; i < N; i++)循环,编译器将其转换为“倒计时到0”循环是微不足道的,如果它更快 - 你很少需要自己做那些聪明。< / p>

答案 3 :(得分:1)

在真正的CPU中,添加是您可以做的最快的事情之一;一个bitshift 更快。而且你将使编译器更难以有效地进行优化。

答案 4 :(得分:1)

更快?你确定吗?至少在MIPS架构上,位移与加法一样长。如果不是最常见的面向消费者的处理器架构,我会感到惊讶。

此外,正如奥列克西指出的那样,这很难理解。可能不值得一个不存在的速度增益。

答案 5 :(得分:1)

增量是一个非常特殊的例子。在大多数处理器中,当然大多数RISC处理器中,移位和增量在执行时间上是相同的。事实上,在大多数架构中,也不再需要添加。

当您保持循环代码惯用时,优化器可以简单地展开循环并在任何情况下更快地渲染它。如果使循环机制“异常”,优化器可能无法对其进行优化。

答案 6 :(得分:1)

  

它不涉及任何添加,所以它很快

哪种CPU架构比添加更快?另外,是什么让你认为特定架构的编译器不能自动添加到自动移位优化,如果它会变得更快?

  

这有什么用吗?

出于优化目的,没有任何使用它。

出于其他目的,是的,这样的代码通常用于屏蔽字节的各个位。我认为最常见的两种方法是:

uint8_t mask; 

for(mask = 0x01; mask != 0x00; mask<<=1)
{
  do_something (data & mask);
}

for(i=0; i<8; i++)
{
  do_something (data & (1<<i));
}

答案 7 :(得分:0)

一般情况下,如果您想要始终循环特定次数&gt; 0并最小化循环开销,然后我认为这将是“最好的”:

unsigned i = 16;

do {
// do something here
} while (--i);



You might get the same result with:

unsigned i = 0x8000;

do {
// do something here
} while (i>>=1);

此时你必须看看装配。

相关问题