我总是假设整数除法比浮点除法更快,但我做了一些似乎证明不然的测试。
import gmpy2, time, math
digits = 100000
scale = 10**digits # Decimal precision
gmpy2.get_context().precision = int(math.log2(10) * digits) # Binary precision
def start_timer():
global start_time
start_time = time.time()
def print_timer():
print("%s s" % (time.time() - start_time))
start_timer()
for i in range(1000):
x = scale // 3
print_timer()
start_timer()
for i in range(1000):
x = gmpy2.mpfr(1) / 3
print_timer()
start_timer()
for i in range(1000):
x = gmpy2.mpfr(1) / gmpy2.mpfr(3)
print_timer()
整数除法需要0.17秒,mpfr除法需要0.06秒,两个浮点数之间的除法需要15.56秒。
答案 0 :(得分:3)
我正在使用IPython来计算一些简短的例子然后我会尝试解释结果。
from gmpy2 import mpfr, get_context
get_context().precision=1000
a=mpfr(1);b=mpfr(3)
%timeit a/b
1000000 loops, best of 3: 669 ns per loop
%timeit a/3
1000000 loops, best of 3: 464 ns per loop
get_context().precision=10000
a=mpfr(1);b=mpfr(3)
%timeit a/b
100000 loops, best of 3: 12.9 µs per loop
%timeit a/3
1000000 loops, best of 3: 1.33 µs per loop
get_context().precision=100000
a=mpfr(1);b=mpfr(3)
%timeit a/b
1000 loops, best of 3: 505 µs per loop
%timeit a/3
100000 loops, best of 3: 8.13 µs per loop
请注意,随着精度的提高,a/b
的运行时间比a/3
增加得更快。计算a/b
时,MPFR使用两个值的完整精度,运行时间(大致)为O(n * ln(n))。在计算a/3
时,MPFR使用3的短但精确的表示,并且运行时间(大致)为O(n)。这解释了为什么a/b
慢于a/3
以获得高精度。 (n是以位为单位的a
的长度。)
当Python计算scale//3
时,它利用了3将适合单个digit
这一事实,并且运行时间在scale
的长度上是线性的。这实际上与a/3
的计算方法相同,但由于基础GMP库比Python快,因此a/3
的计算速度比scale//3
快。
以下是Python和GMP之间性能差异的简短示例。
from gmpy2 import mpz
scale = 10**100000
%timeit scale//3
10000 loops, best of 3: 162 µs per loop
scale = mpz(scale)
%timeit scale//3
100000 loops, best of 3: 19 µs per loop
当您比较n
和n
时,您正在衡量n
到k
分区和a/b
分区a/3
分区之间的效果。 (n
是位a
的长度,k
远小于n
。)当您比较scale//3
和`a / 3时',您正在将简单,直接的部门实施与高度优化的实施进行比较。
实施说明:在当前不稳定的开发分支中,a/3
直接调用mpfr_div_ui
。这消除了MPFR创建临时对象。这样可以提高性能,如下所示。
from gmpy2 import mpfr, get_context
get_context().precision=1000
a=mpfr(1);b=mpfr(3)
%timeit a/b
1000000 loops, best of 3: 593 ns per loop
%timeit a/3
1000000 loops, best of 3: 231 ns per loop
get_context().precision=10000
a=mpfr(1); b=mpfr(3)
%timeit a/b
100000 loops, best of 3: 12.7 µs per loop
%timeit a/3
1000000 loops, best of 3: 927 ns per loop
get_context().precision=100000
a=mpfr(1);b=mpfr(3)
%timeit a/b
1000 loops, best of 3: 505 µs per loop
%timeit a/3
100000 loops, best of 3: 6.77 µs per loop
答案 1 :(得分:2)
有关GNU MPFR实现的说明(我是MPFR开发人员,虽然我还没有真正参与该部门):选择最佳的乘法和除法算法非常困难,因为有各种参数(输入和输出的精度,以及输入是否可以用较小的精度表示,特别是因为尾随零),并且某些情况可能比其他情况更难以舍入。此外算法因此时序可能会从一个版本更改为另一个版本,从而改善了某些情况,但同时使其他情况更慢。即使是最近(两个月前),我们还讨论了是否对mpfr_mul_ui和mpfr_div_ui中的整数进行了2的常数幂的特殊识别。
如果你想真正比较整数除法与MPFR FP除法,你应该与GMP的整数除法进行比较。 MPFR基于GMP的分工,但不是天真的。了解MPFR正在做什么的最好方法是使用MPFR日志记录(这可能需要使用--enable-logging
重建)和相应的环境变量。请注意,在MPFR构建中启用日志记录时,即使未使用日志记录,MPFR也可能会慢一些。
答案 2 :(得分:1)
浮点除法通常比CPU上的整数除法快。可以推测这与FPU更适合此操作,或浮点表示使分割更容易。但无论什么理由都不会改变这一事实。最后,获得第二和第三个问题的具体答案的唯一方法是测试它。是的,你的测试看起来还不错。
如果我不得不猜测,我认为将MPFR数除以整数的情况更快,因为GMP在计算除法时可以利用其有限的精度来优势。