我曾经读过某些地方,模数运算符在小型嵌入式设备(如没有整数除法指令的8位微控制器)上效率低下。也许有人可以证实这一点,但我认为差异比整数除法运算慢5-10倍。
除了保持计数器变量并在mod点手动溢出为0之外,还有另一种方法吗?
const int FIZZ = 6;
for(int x = 0; x < MAXCOUNT; x++)
{
if(!(x % FIZZ)) print("Fizz\n"); // slow on some systems
}
VS
我目前正在这样做:
const int FIZZ = 6;
int fizzcount = 1;
for(int x = 1; x < MAXCOUNT; x++)
{
if(fizzcount >= FIZZ)
{
print("Fizz\n");
fizzcount = 0;
}
}
答案 0 :(得分:47)
但是,有一小部分数论专门用于Modular Arithmetic,如果你真的想要了解如何优化模数运算,则需要研究。例如,模块化算术对于生成magic squares非常方便。
所以,在这种情况下,这里有一个very low level look的数学模型,例如x,它应该向你展示它与分裂相比有多简单:
考虑问题的一个更好的方法就是数字 基数和模运算。例如,您的目标是计算DOW mod 7其中DOW是当天的16位表示 周。你可以这样写:
DOW = DOW_HI*256 + DOW_LO
DOW%7 = (DOW_HI*256 + DOW_LO) % 7
= ((DOW_HI*256)%7 + (DOW_LO % 7)) %7
= ((DOW_HI%7 * 256%7) + (DOW_LO%7)) %7
= ((DOW_HI%7 * 4) + (DOW_LO%7)) %7
以这种方式表达,您可以单独计算模7 高字节和低字节的结果。将结果乘以高 4并将其添加到低位,然后最终计算结果模7。
计算8位数的mod 7结果可以在a中执行 类似的时尚。你可以用八进制写一个8位数字,如下所示:
X = a*64 + b*8 + c
其中a,b和c是3位数字。
X%7 = ((a%7)*(64%7) + (b%7)*(8%7) + c%7) % 7
= (a%7 + b%7 + c%7) % 7
= (a + b + c) % 7
自64%7 = 8%7 = 1
当然,a,b和c是
c = X & 7
b = (X>>3) & 7
a = (X>>6) & 7 // (actually, a is only 2-bits).
a+b+c
的最大可能值为7+7+3 = 17
。所以,你需要
又一个八进制步骤。完整的(未经测试的)C版本可以
写得像:
unsigned char Mod7Byte(unsigned char X)
{
X = (X&7) + ((X>>3)&7) + (X>>6);
X = (X&7) + (X>>3);
return X==7 ? 0 : X;
}
我花了一些时间写一个PIC版本。实际执行情况 与上述略有不同
Mod7Byte:
movwf temp1 ;
andlw 7 ;W=c
movwf temp2 ;temp2=c
rlncf temp1,F ;
swapf temp1,W ;W= a*8+b
andlw 0x1F
addwf temp2,W ;W= a*8+b+c
movwf temp2 ;temp2 is now a 6-bit number
andlw 0x38 ;get the high 3 bits == a'
xorwf temp2,F ;temp2 now has the 3 low bits == b'
rlncf WREG,F ;shift the high bits right 4
swapf WREG,F ;
addwf temp2,W ;W = a' + b'
; at this point, W is between 0 and 10
addlw -7
bc Mod7Byte_L2
Mod7Byte_L1:
addlw 7
Mod7Byte_L2:
return
这是测试算法的liitle例程
clrf x
clrf count
TestLoop:
movf x,W
RCALL Mod7Byte
cpfseq count
bra fail
incf count,W
xorlw 7
skpz
xorlw 7
movwf count
incfsz x,F
bra TestLoop
passed:
最后,对于16位结果(我没有测试过),你可以 写:
uint16 Mod7Word(uint16 X)
{
return Mod7Byte(Mod7Byte(X & 0xff) + Mod7Byte(X>>8)*4);
}
斯科特
答案 1 :(得分:35)
如果你计算的数字是2的幂,你可以使用逐位和运算符。只需从第二个数字中减去一个。例如:
x % 8 == x & 7
x % 256 == x & 255
一些警告:
答案 2 :(得分:6)
大多数时候使用modulo的开销不是2的幂。 这与处理器无关(AFAIK),即使是具有模数运算符的处理器,除了掩码操作之外,对于除法的处理速度要慢几个周期。
对于大多数情况,这不是一个值得考虑的优化,当然不值得计算你自己的快捷操作(特别是如果它仍然涉及分割或乘法)。
但是,一条经验法则是选择数组大小等为2的幂。
因此,如果计算星期几,也可以使用%7 如果设置一个大约100个条目的循环缓冲区...为什么不将它设为128.然后你可以编写%128并且大多数(所有)编译器将使这个&amp; 0x7F的
答案 3 :(得分:4)
除非您确实需要在多个嵌入式平台上获得高性能,否则在您进行配置之前不要更改性能原因的代码编码!
为了优化性能而编写的代码很难调试并且难以维护。编写测试用例,并在目标上对其进行概要分析。一旦知道了模数的实际成本,就可以确定替代解决方案是否值得编码。
答案 4 :(得分:3)
@Matthew是对的。试试这个:
int main() {
int i;
for(i = 0; i<=1024; i++) {
if (!(i & 0xFF)) printf("& i = %d\n", i);
if (!(i % 0x100)) printf("mod i = %d\n", i);
}
}
答案 5 :(得分:2)
x%y == (x-(x/y)*y)
希望这有帮助。
答案 6 :(得分:1)
在嵌入式世界中,您需要执行的“模数”操作通常可以很好地分解为可以使用“&amp;”进行的位操作。和'|'有时'&gt;&gt;'。
答案 7 :(得分:1)
您是否可以访问嵌入式设备上的任何可编程硬件?像柜台等?如果是这样,您可能能够编写基于硬件的mod单元,而不是使用模拟的%。 (我在VHDL中做过一次。不知道我是否还有代码。)
请注意,你确实说分裂的速度要快5到10倍。您是否考虑过进行除法,乘法和减法来模拟模型? (编辑:误解了原来的帖子。我觉得分裂比mod更快是奇怪的,它们是相同的操作。)
但是,在您的具体情况下,您正在检查6. 6 = 2 * 3的mod。因此,如果您首先检查最低有效位是否为0,那么您可能会获得一些小的收益。例如:
if((!(x & 1)) && (x % 3))
{
print("Fizz\n");
}
如果你这样做,我建议确认你获得任何收益,对于剖析师而言。并做一些评论。对于下一个不得不查看代码的人来说,我会感到很难过。
答案 8 :(得分:1)
您应该检查所需的嵌入式设备。我见过的所有汇编语言(x86,68000)都使用除法来实现模数。
实际上,除法汇编操作返回除法的结果,剩下的是两个不同的寄存器。
答案 9 :(得分:0)
这并不一定更好,但你可以有一个内循环,它总是上升到FIZZ,一个外循环,它重复它一定次数。如果MAXCOUNT不能被FIZZ整除,那么你最后可能会遇到特殊情况。
也就是说,我建议您在预期的平台上进行一些研究和性能分析,以便清楚地了解您所面临的性能限制。可能会有更高效的地方来进行优化工作。
答案 10 :(得分:0)
@Jeff V:我发现它有问题! (除此之外,您的原始代码正在寻找mod 6,现在您基本上正在寻找mod 8)。你继续做额外的+1!希望你的编译器能够优化它,但为什么不只是测试从2开始并转到MAXCOUNT包含?最后,每当(x + 1)不能被8整除时,你就会返回true。这就是你想要的吗? (我认为是,但只想确认。)
答案 11 :(得分:0)
对于模6,您可以将Python代码更改为C / C ++:
def mod6(number):
while number > 7:
number = (number >> 3 << 1) + (number & 0x7)
if number > 5:
number -= 6
return number
答案 12 :(得分:-4)
print语句比模数运算符的最慢实现要长几个数量级。所以基本上评论“在一些系统上慢”应该“在所有系统上都很慢”。
此外,提供的两个代码片段不会做同样的事情。在第二个,行
if(fizzcount >= FIZZ)
始终为false,因此永远不会打印“FIZZ \ n”。