高效的模 3 运算? [复制]
Posted
技术标签:
【中文标题】高效的模 3 运算? [复制]【英文标题】:Efficient Modulo 3 operation? [duplicate] 【发布时间】:2011-11-15 19:13:10 【问题描述】:可能重复:Fast modulo 3 or division algorithm?
每个人都知道模运算可能是性能上的一个巨大缺陷。有谁知道 x%3 操作的好选择?我知道 x%2 存在一个,但我确实需要一个用于模 3,因为我想在 for 循环中在三个缓冲区之间交替。
谢谢!
【问题讨论】:
除非您的代码在模数处出现瓶颈,否则这将是过早优化的情况。 我建议你先检查一下瓶颈在哪里。我非常怀疑这是模... 每个人都知道,试图超越编译器的模优化可能会大大降低开发人员的效率。 “每个人都知道......”是吗? 【参考方案1】:好吧,而不是通常的“测量它”东西一个实际的答案 - 因为这些东西实际上是真正有趣的数学。尽管编译器可以并且可能也这样做(至少现代优化 c++ 编译器,javac 当然不会,我不知道 JVM 是否这样做) - 所以最好检查它是否已经在为你。
但了解优化背后的理论仍然很有趣:我将使用汇编,因为我们需要乘法的更高 32 位字。以下内容来自 Warren 的关于 bit twiddling 的书:
n 是我们想要取模的输入整数:
li M, 0x55555556 ; load magical number (2^32 + 2) / 3
mulhs q, M, n ; q = higher word of M * n; i.e. q = floor(M*n / 2^32)
shri t, n, 31 ; add 1 to q if it is negative
add q, q, t
这里 q 包含 n / 3 的除数,所以我们只像往常一样计算余数:r = n - q*3
数学是有趣的部分 - 乳胶在这里会很酷:
q = 楼层( (2^32+2)/ 3 * (n / 2^32) ) = 楼层( n/3 + 2*n/(3*2^32) )
现在对于 n = 2^31-1(有符号 32 位整数可能的最大 n),误差项小于 1/3(并且非负数),这使得很容易证明结果确实是正确的。对于 n = -2^31,我们在上面进行了 1 的更正,如果您简化它,您会发现误差项始终大于 -1/3,这意味着它也适用于负数。
我为感兴趣的人留下了带有错误术语界限的证明 - 这并不难。
【讨论】:
gcc 生成几乎和你写的一样: int f(int n) return n / 3; 0: 89 f8 mov %edi,%eax 2: ba 56 55 55 55 mov $0x55555556,%edx 7: c1 ff 1f sar $0x1f,%edi a: f7 ea imul %edx c: 29 fa sub %edi,% edx e: 89 d0 mov %edx,%eax 10: c3 retq【参考方案2】:如果它在一个直线循环中,则无需计算模数。保留第二个 int var,每 3 步重置一次。
int i, bn = 0;
for(i=0; i<whatever; i++)
...
if(++bn == 3) bn = 0;
这不是过早的优化,而是避免了不必要的计算。
编辑:在 OP 中说他使用循环在缓冲区之间切换,所以我的解决方案看起来很合适。 至于否决票,如果是错误的,没问题。
【讨论】:
@Stefan 如果它真的是一个循环,这几乎肯定比现代 x86 cpu 上的任何其他可能的解决方案都要快。为什么?因为现代 CPU 中的分支预测器(即用于高性能 CPU..x86、Power 等 - 肯定不是 ARM)将得到如此简单的重复模式。因此,我们用一个分支和一个mov reg, 0
指令替换我上面的计算,它仍然包括两个imuls、shifts、一个add(或其他有很多adds、shifts 的可能性)。为什么你认为这会慢一些?
对不起我的错误。等待部署时不应该投票。我真的以为他是通过循环计算模数。您可以编辑答案空间或其他内容,以便我可以撤消否决票吗?它没有回答有效模数的确切问题,但可能是实际需要的最佳答案。
你的意思是如果它在一个循环中并且你需要循环索引的模3。无论哪种方式,这绝对是过早的优化,因为它降低了代码的清晰度,更重要的是,它甚至不能保证是一种优化,因为编译器可能能够比 if 更好地理解模数,从而制作更好的代码。或者它可能不会,谁知道呢。如果模数和 if 均未优化,并且您在具有错误/无分支预测的平台上工作,则由于不会导致管道停顿,模数甚至可能更快。
@Stefan 我只担心他以一种重要的方式编辑了答案(不知道 SO 究竟定义了什么是重要的)。但我敢肯定,他所需要的只是一个衷心的抱歉 :) -2 分并没有那么成问题
@Grizzly 即使按照我的回答对 mod 3 进行了优化,这样做也会显着(对于少数指令来说非常显着 - 我们在这里谈论
【参考方案3】:
如果3
在编译时已知,那么编译器将生成“技巧”以尽可能高效地执行此操作。当除数在运行时未知时,取模需要更长的时间。
【讨论】:
以上是关于高效的模 3 运算? [复制]的主要内容,如果未能解决你的问题,请参考以下文章