3

我需要执行许多操作,unsigned long long通过 16 位模数找到除数的余数:

unsigned long long largeNumber;
long residues[100];
unsigned long modules[100];
intiModules(modules); //set different 16-bit values

for(int i = 0; i < 100; i++){
     residues[i] = largeNumber % modules[i];
}

我怎样才能加速这个循环?

迭代次数不大(32-128),但是这个循环执行得非常频繁,所以它的速度很关键。

4

2 回答 2

2

如果速度很关键,根据this answer about branch prediction and this one,循环展开可能会有所帮助,避免for指令引起的测试,减少测试次数并改善“分支预测”。

增益(或没有,一些编译器为您进行优化)因架构/编译器而异。

在我的机器上,更改循环,同时保留操作数

for(int i = 0; i < 500000000; i++){
    residues[i % 100] = largeNumber % modules[i % 100];
}

for(int i = 0; i < 500000000; i+=5){
    residues[(i+0) % 100] = largeNumber % modules[(i+0) % 100];
    residues[(i+1) % 100] = largeNumber % modules[(i+1) % 100];
    residues[(i+2) % 100] = largeNumber % modules[(i+2) % 100];
    residues[(i+3) % 100] = largeNumber % modules[(i+3) % 100];
    residues[(i+4) % 100] = largeNumber % modules[(i+4) % 100];
}

gcc -O2增益约为 15% 。(500000000 而不是 100 观察更显着的时间差异)

于 2014-02-27T10:50:21.450 回答
1

除以常数(其中只有 65536 个)可以通过将倒数相乘来执行,然后进行一些微调。由于这种方法在有限的范围内是准确的,因此可以使用一些技术将 64 位操作数减少到一个小得多的值(仍然与原始值一致):

// pseudo code -- not c
a = 0x1234567890abcdefULL;
a = 0x1234 << 48 + 0x5678 << 32 + 0x90ab << 16 + 0xcdef;

a % N === ((0x1234 * (2^48 % N) +     // === means 'is congruent'
           (0x5678 * (2^32 % N)) +    // ^ means exponentation
           (0x90ab * (2^16 % N)) + 
           (0xcdef * 1)) % N;

中间值只能用(小)乘法计算,最后的余数(%N)有可能用倒数乘法计算。

于 2014-02-27T10:13:11.103 回答