MPFR划分比原生整数划分快吗?

时间:2014-07-29 20:17:26

标签: python mpfr gmpy

我总是假设整数除法比浮点除法更快,但我做了一些似乎证明不然的测试。

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秒。

我的问题:

  1. 我是否正确设置了此测试?
  2. mpfr分区是否比原生分区更优化?
  3. 涉及浮点和整数的除法是否比涉及两个浮点数的要快得多?

3 个答案:

答案 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

当您比较nn时,您正在衡量nk分区和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在计算除法时可以利用其有限的精度来优势。