c++中使用% operator和/ operator的替代方法

Alternative to using % operator and / Operator in C++

本文关键字:operator 方法 c++      更新时间:2023-10-16

在嵌入式c++中,模运算符"%"和除法运算符"/"是非常低效的。

如何实现以下表达式:

a = b % c;

我理解这可以使用以下逻辑来实现:

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

但是我的问题是,与%操作符相比,这段涉及while循环的代码是否足够有效?

谢谢,医生说

除法和取模确实是代价高昂的硬件操作,无论你做什么(这更多地与硬件架构有关,而不是语言或编译器),可能比加法慢十倍。

然而,在当前的笔记本电脑或服务器上,以及在高端微控制器上,缓存丢失通常比除法慢得多!

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

初始循环通常比使用硬件除法指令(或库例程,如果不是硬件提供的话)要慢得多。我认为你回避分裂是错误的。用你的循环替换它

你可能会调整你的算法-例如,通过2的幂-但我不建议使用你的代码。请记住,过早的优化是邪恶的,所以首先尝试使您的程序正确,然后分析它以找到问题点。

没有比%运算符更有效的了。如果有更好的方法,那么任何合理的编译器都会自动转换它。当你被告知%/是低效的,那只是因为它们是困难的操作-如果你需要执行模,那么就这样做。

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

该代码几乎肯定会比处理器/编译器决定执行除法/mod的代码慢。一般来说,对于基本的算术运算符来说,快捷方式是很难找到的,因为mcu/cpu设计人员和编译器程序员非常擅长为几乎所有的应用程序优化快捷方式。

在嵌入式设备(其中每个周期/字节都可以产生差异)中,一个常见的快捷方式是将所有内容保持为基数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

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

如果您仔细分析了代码并发现模运算符是内部循环中的主要成本,那么有一个优化可能会有所帮助。您可能已经熟悉了使用算术左移(对于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%,但是在树莓派的ARM11芯片上使用GCC编译时,我只得到了20%的改进。

除常数可以通过移位实现,如果是2的幂,或者对于其他的是多加移位组合。

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