加速循环执行无符号长模模运算的性能

时间:2014-02-27 09:41:43

标签: c performance loops modulus long-long

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

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),但这个循环经常执行,所以它的速度很关键。

2 个答案:

答案 0 :(得分:2)

如果速度至关重要,根据此answer about branch predictionthis one,循环展开可能有所帮助,避免了指令引起的测试,减少了测试和改进&#34;分支预测&#34;。

增益(或者没有,某些编译器为您做优化)因架构/编译器而异。

在我的机器上,在保留

操作数的同时更改循环
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来观察更显着的时差)

答案 1 :(得分: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)有可能用倒数乘法计算。

相关问题