找到不大于 A 的最大数的最有效方法,该数可被 B 整除 [关闭]
Posted
技术标签:
【中文标题】找到不大于 A 的最大数的最有效方法,该数可被 B 整除 [关闭]【英文标题】:Most efficient way to find the greatest number not greater than A, which is divisible by B [closed] 【发布时间】:2019-08-14 08:01:07 【问题描述】:我有2个号码A和B。我想找到C = A - (A % B)
,但是有一些问题。首先,如果C
和D = A / B
应该具有相同的奇偶校验((偶数和偶数)或(奇数和奇数)),否则C 应该递增(++C
)。第二个问题是我经常做这个计算,所以我希望它的成本尽可能小。现在我的解决方案是这样的:
uint32_t D = A / B;
C = D * B;
if ((C ^ D) & 0x1) ++C;
有没有更好的方法来做到这一点?由于编译器优化,(C % 2) != (D % 2)
可能更快,但我无法证明这一点。我也想知道是否可以使用某些特定的 intel 函数(寄存器)来完成。
【问题讨论】:
(C ^ D) & 0x1
将是0
或1
,您可以无条件地将其添加到C
。
但是在开始进行此类微优化之前,请先启用优化并进行 measure 和 profile 构建,如果需要,请检查生成的程序集。也许这不是您真正认为的那样的瓶颈?
地板(A / B)* B
【参考方案1】:
我假设输入 A
和 B
也是 uint32_t
?
除法的成本使其他一切都相形见绌,除非 B
在内联后的编译时已知。 (即使它不是 2 的幂)。与其他任何指令相比,实际的 div
指令非常昂贵,并且无法使用 SIMD 进行矢量化。 (x86 上唯一可用的 SIMD 除法是 FP,或者当然是整数移位除以 2)。
到目前为止,您可以做的最有用的事情是安排 B
的值在编译时对编译器可见,或者至少在链接时优化跨文件内联。 (Why does GCC use multiplication by a strange number in implementing integer division?)
如果B
不是编译时常数,x86 除法将免费产生余数以及商。 sub
比 imul
便宜,所以使用并让编译器优化:
uint32_t D = A / B;
uint32_t C = A - A % B;
如果B
是一个编译时常量,编译器会将其优化为除法,然后再乘法,并且(希望)将其优化到与原来的一样好。
不,(C^D) ^ 1
应该是一种更有效的方法来检查低位是否与(C % 2) != (D % 2)
不同。在合并之前对每个输入做一些单独的事情会花费更多的指令,因此最好引导编译器朝着更有效的 asm 实现方向发展。 (显然,看看这两种情况的 asm 输出是个好主意)。
可能有用的是使用+
而不是^
。 XOR = 没有进位的加法,但你只关心低位。 ^
和 +
的低位始终相同。这使编译器可以选择使用lea
指令进行复制和添加。 (在这种情况下可能没有帮助;如果编译器破坏了
持有D
的寄存器中的值,假设在此之后它已经死了。但是如果你也直接用D)
当然,您实际上并不想使用if(...)
进行分支,因此您应该将其写为:
C += (C+D) & 1; // +1 if low bits differ
【讨论】:
以上是关于找到不大于 A 的最大数的最有效方法,该数可被 B 整除 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章