替代在C ++中使用%运算符和/运算符

时间:2011-11-15 06:09:38

标签: c++ modulo processing-efficiency

有人告诉我,模运算符“%”和除运算符“/”在嵌入式C ++中效率很低。

我怎样才能实现以下表达式:

a = b % c;

我知道这可以使用以下逻辑来实现:

a = b - c;
while (a >= c) {
  a = a - c;
}

但我的问题是,与%operator?

相比,这段代码涉及while循环是否足够有效

谢谢, 基尔提

6 个答案:

答案 0 :(得分:18)

分区和模数确实是昂贵的硬件操作,无论你做什么(这与硬件架构比与语言或编译器更相关),可能比添加慢十倍。

然而,在当前的笔记本电脑或服务器以及高端微控制器上,cache未命中通常比分部慢得多!

当除数为常数时,GCC编译器通常能够优化它们。

您的朴素循环通常比使用硬件除法指令(或库程序执行它,如果不是由硬件提供)慢得多。我相信你在避免分裂和错误方面做错了。用你的循环替换它。

您可以调整算法-e.g.通过两倍的力量 - 但我不建议使用您的代码。请记住过早优化是邪恶的所以首先尝试让您的程序正确,然后对其进行分析以找到问题点。

答案 1 :(得分:7)

没有什么比%运营商更有效率。如果有更好的方法,那么任何合理的编译器都会自动转换它。如果您被告知%/效率低下,那只是因为这些操作很困难 - 如果您需要执行模数,那么就这样做。

当有更好的方法时,可能会出现特殊情况 - 例如,mod的2的幂可以写成二进制或 - 但这些可能是由编译器优化的。

答案 2 :(得分:5)

该代码几乎肯定会慢于您的处理器/编译器决定执行divide / mod。通常,基本算术运算符很难获得快捷方式,因为mcu / cpu设计者和编译器程序员非常擅长为几乎所有应用程序优化它。

嵌入式设备中的一个常见快捷方式(每个周期/字节可以产生差异)是保持所有内容都以base-2为基础,使用位移运算符来执行乘法和除法,以及按位和(&)执行模数。

示例:

unsigned int x = 100;
unsigned int y1 = x << 4;   // same as x * 2^4 = x*16
unsigned int y2 = x >> 6;   // same as x / 2^6 = x/64
unsigned int y3 = x & 0x07; // same as x % 8

答案 3 :(得分:1)

如果在编译时知道除数,则可以将操作转换为乘以倒数的乘法,并进行一些移位,加法和其他快速操作。这在任何现代处理器上都会更快,即使它实现了硬件划分。嵌入式目标通常具有高度优化的除法/模数例程,因为标准需要这些操作。

答案 4 :(得分:1)

如果您仔细分析了代码并发现模运算符是内循环中的主要成本,那么可能有一些优化可能有所帮助。您可能已经熟悉使用算术左移(对于32位值)确定整数符号的技巧:

sign = ( x >> 31 ) | 1;

这会扩展字的符号位,因此负值会产生-1而正值为0.然后设置位0,使正值产生1。

如果我们只是按小于模数的数量递增值,则可以使用相同的技巧来包装结果:

val += inc;
val -= modulo & ( static_cast< int32_t >( ( ( modulo - 1 ) - val ) ) >> 31 );

或者,如果您按小于模数的值递减,则相关代码为:

int32_t signedVal = static_cast< int32_t >( val - dec );
val = signedVal + ( modulo & ( signedVal >> 31 ) );

我添加了static_cast运算符,因为我传入的是uint32_t,但是你可能觉得它们没必要。

与简单的%运算符相比,这有用吗?这取决于您的编译器和CPU架构。我发现在VS2012下编译时,我的i3处理器上的一个简单循环运行速度提高了60%,但是在Raspberry Pi的ARM11芯片上和GCC编译时,我只有20%的改进。

答案 5 :(得分:0)

如果2的幂或mul为其他的加上移位组合,则可以通过移位来实现除常数的除法。

http:// masm32.com/board/index.php?topic=9937.0从第一篇文章下载x86汇编版本以及C源代码。为您生成此代码。