我在C中计算模数的成本最小化。 说我有一个数字x和n是将x除以的数字
当n == 65536(恰好是2 ^ 16)时:
mod = x%n(由GCC制作的11份装配说明)
或
mod = x& 0xffff等于mod = x& 65535(4个装配说明)
所以,GCC并没有在这个程度上对其进行优化。
在我的情况下,n不是x ^(int),但是最大素数小于2 ^ 16,即65521
正如我在n == 2 ^ 16中所展示的那样,逐位运算可以优化计算。当n == 65521计算模数时,我可以执行哪些按位操作。
答案 0 :(得分:26)
首先,在确定GCC产生的结论之前,确保您正在查看优化代码(并确保确实需要优化此特定表达式)。最后 - 不要指望得出结论;可能预期11指令序列的性能优于包含div指令的较短序列。
此外,您无法得出结论,因为可以使用简单的位掩码计算x mod 65536
,因此可以通过这种方式实现任何mod操作。考虑如何轻松除以十进制10而不是除以任意数字。
尽管如此,你可以使用Henry Warren的Hacker's Delight书中的一些“神奇数字”技术:
有一个added chapter on the website包含“两种计算除法余数而不计算商数的方法!”,你可能会发现它有些用处。第一种技术仅适用于有限的除数,因此它不适用于您的特定实例。我还没有真正阅读过在线章节,因此我不确切知道其他技术对您的适用程度。
答案 1 :(得分:10)
x mod 65536仅相当于x& 0xffff如果x是无符号的 - 对于带符号的x,它给出负数的错误结果。对于无符号x,gcc确实将x % 65536
优化为按位并使用65535(在我的测试中甚至在-O0上)。
因为65521不是2的幂,所以不能如此简单地计算x mod 65521。 gcc 4.3.2 on -O3使用x - (x / 65521) * 65521
计算它;使用整数乘以相关常数来完成整数除以。
答案 2 :(得分:6)
如果你不必完全减少你的整数模数65521,那么你可以使用65521接近2 ** 16的事实。即如果x是要减少的unsigned int,则可以执行以下操作:
unsigned int low = x &0xffff;
unsigned int hi = (x >> 16);
x = low + 15 * hi;
这使用2 ** 16%65521 == 15.请注意,这不是完全减少。即从32位输入开始,您只能保证结果最多为20位,并且它当然与输入模65521一致。
此技巧可用于需要以相同常数模数减少许多操作的应用程序,并且中间结果不必是其残差类中的最小元素。
E.g。一个应用程序是Adler-32的实现,它使用模数65521.这个散列函数以65521为模数执行大量操作。为了有效地实现它,人们只能在精心计算的加法数量之后进行模块化约简。如上所示的减少就足够了,只有散列的计算才需要完整的模运算。
答案 3 :(得分:2)
如果除数的格式为2^n
,则按位运算才有效。在一般情况下,没有这样的按位操作。
答案 4 :(得分:1)
如果在编译时知道要使用模数的常量 和你有一个像样的编译器(例如gcc),通常最好让编译器 发挥它的魔力。只需声明模数const。
如果您在编译时不知道常量,但是您要采取 - 比如说 - 十亿个模数具有相同的数字,然后使用此http://libdivide.com/
答案 5 :(得分:1)
当我们处理2的权力时,可以考虑这个(主要是C风格):
.
.
#define THE_DIVISOR 0x8U; /* The modulo value (POWER OF 2). */
.
.
uint8 CheckIfModulo(const sint32 TheDividend)
{
uint8 RetVal = 1; /* TheDividend is not modulus THE_DIVISOR. */
if (0 == (TheDividend & (THE_DIVISOR - 1)))
{
/* code if modulo is satisfied */
RetVal = 0; /* TheDividend IS modulus THE_DIVISOR. */
}
else
{
/* code if modulo is NOT satisfied */
}
return RetVal;
}
答案 6 :(得分:1)
如果x
是一个递增的索引,并且已知增量i
小于n
(例如,当迭代长度 n ),完全避免模数。
一个循环
x += i; if (x >= n) x -= n;
比
快x = (x + i) % n;
你不幸在很多教科书中找到了......
如果你真的需要一个表达式(例如,因为你在for
语句中使用它),你可以使用丑陋但高效的
x = x + (x+i < n ? i : i-n)
答案 7 :(得分:0)
idiv - 整数分部
idiv指令将64位整数EDX:EAX(通过将EDX视为最重要的四个字节构造,EAX构造为最不重要的四个字节)的内容除以指定的操作数值。除法的商结果存储在EAX中,而余数存放在EDX 中。
答案 8 :(得分:-3)
如何实施 MOD :
要查找:y = X mod n
y = X-(X/n)*n
(假设 X 和 n 都是整数)
注意:对于装配级别优化,请使用 iDiv ,如上面Krystian所述。