最优化的计算C模数的方法

时间:2010-04-18 09:13:42

标签: c optimization assembly

我在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计算模数时,我可以执行哪些按位操作。

9 个答案:

答案 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 中。

来源:http://www.cs.virginia.edu/~evans/cs216/guides/x86.html

答案 8 :(得分:-3)

C

中模数的最低成本实施

如何实施 MOD

要查找:y = X mod n

y = X-(X/n)*n

(假设 X n 都是整数)

注意:对于装配级别优化,请使用 iDiv ,如上面Krystian所述。