我需要执行许多操作,通过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),但这个循环经常执行,所以它的速度很关键。
答案 0 :(得分:2)
如果速度至关重要,根据此answer about branch prediction和this 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)有可能用倒数乘法计算。