Fast Modulo 511和127

时间:2012-04-21 14:44:45

标签: performance modulo

有没有办法,如何比使用“%”运算符快511(和127)模数?

int c = 758 % 511;
int d = 423 % 127;

3 个答案:

答案 0 :(得分:0)

您可以使用预先存储的解决方案的查找表。如果你创建一个包含一百万个整数的数组,那么查找速度大约是我在C#app中实际模数的两倍。

// fill an array
var mod511 = new int[1000000];
for (int x = 0; x < 1000000; x++) mod511[x] = x % 511;

而不是使用

c = 758 % 511;

你使用

c = mod511[758];

这将花费你(可能很多)内存,如果你想将它用于非常大的数字,显然也不会有效。但它更快。

答案 1 :(得分:0)

如果你必须对大量数据重复这两个模数运算,并且你的CPU支持SIMD(例如Intel的SSE / AVX / AVX2),那么你可以对操作进行矢量化,即对许多数据进行并行操作。您可以使用内在函数或内联汇编来完成此操作。是的,解决方案将是特定于平台的,但也许这很好......

答案 2 :(得分:0)

这是一种快速模数为511的方法,假设x最多为32767.它的速度大约是x%511的两倍。它以五个步骤进行模数:两次乘法,两次加法,一次移位。

inline int fast_mod_511(int x) {
    int y = (513*x+64)>>18;
    return x - 511*y;
}

以下是我如何达成这一目标的理论。我在最后发布了我测试过的代码

让我们考虑一下

y = x/511 = x/(512-1) = x/1000 * 1/(1-1/512).

让我们定义z = 512,然后

y = x/z*1/(1-1/z).

使用泰勒扩展

y = x/z(1 + 1/z + 1/z^2 + 1/z^3 + ...).

现在,如果我们知道x的范围有限,我们就可以减少扩展。假设x总是小于2 ^ 15 = 32768。然后我们可以写

512*512*y = (1+512)*x = 513*x.

在查看了重要的数字后,我们到达

y = (513*x+64)>>18 //512^2 = 2^18.

我们可以分三步来划分x / 511(假设x小于32768):

multiply,
add,
shift.

以下是我在Ivy Bridge核心上以MSVC2013 64位版本模式对此进行分析的代码。

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

inline int fast_mod_511(int x) {
    int y = (513*x+64)>>18;
    return x - 511*y;
}

int main() {
    unsigned int i, x;
    volatile unsigned int r;
    double dtime;

    dtime = omp_get_wtime();
    for(i=0; i<100000; i++) {
        for(int j=0; j<32768; j++) {
            r = j%511;
        }     
    }
    dtime =omp_get_wtime() - dtime;
    printf("time %f\n", dtime);

    dtime = omp_get_wtime();
    for(i=0; i<100000; i++) {
        for(int j=0; j<32768; j++) {
            r = fast_mod_511(j);
        }
    }
    dtime =omp_get_wtime() - dtime;
    printf("time %f\n", dtime);



}