【问题标题】:Is MPFR division faster than native integer division?MPFR 除法比本机整数除法快吗?
【发布时间】:2014-07-29 20:17:25
【问题描述】:

我一直假设整数除法比浮点除法快,但我做了一些似乎证明并非如此的测试。

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. 一个浮点数和一个整数的除法是否比两个浮点数的除法快得多?

【问题讨论】:

  • 请注意,您在测试中包括了创建 mpfr 对象的时间以及进行除法的时间。另外,我建议将timeit 模块用于此类事情。
  • 真的感到惊讶吗?将 330000 位浮点数除以 2 位整数比将 330000 位浮点数除以另一个 330000 位浮点数要快得多? :-)
  • @MarkDickinson 文档说在除法期间两个数字都被转换为正确的类型,所以我认为这意味着两个数字都必须是整数或浮点数
  • 我猜想在第二部分中,gmpy2 正在将 RHS 3 转换为具有小精度的 mpfr 实例(因为可以精确地完成而不损失精度) ,而在第三部门中,您将以当前精度创建两个成熟的 mpfr 实例。
  • 而Python自己的长整数除法算法简单便携但没有特别优化。它并不是真正针对 100000 位计算。这就是 gmpy2 的用途。因此,'native' 除法比 MPFR 的除法慢一点也就不足为奇了。

标签: python mpfr gmpy


【解决方案1】:

我正在使用 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/ba/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

当您比较 a/ba/3 时,您正在衡量 n by n 部门和 n by k 部门之间的性能。 (na 的长度,以位为单位,kn 小得多。)当您比较 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

【讨论】:

    【解决方案2】:

    关于 GNU MPFR 实现的说明(我是一名 MPFR 开发人员,虽然我并没有真正研究过除法):为乘法和除法选择最佳算法非常困难,因为有各种参数 (输入和输出的精度,以及输入是否可以因为尾随零而以较小的精度表示,特别是),并且某些情况可能比其他情况更难四舍五入。此外,算法因此时间可能会从一个版本更改为另一个版本,从而改善某些情况,但同时使其他情况变慢。甚至在最近(两个月前),我们也讨论过是否对 mpfr_mul_ui 和 mpfr_div_ui 中的整数进行 2 的常数幂的特殊识别。

    如果你想真正比较整数除法和 MPFR FP 除法,你应该用 GMP 的整数除法进行比较。 MPFR是基于GMP的划分,但并不天真。了解 MPFR 正在做什么的最好方法是使用 MPFR 日志记录(这可能需要使用 --enable-logging 进行重建)和相应的环境变量。请注意,在 MPFR 构建中启用日志记录时,即使不使用日志记录,MPFR 也可能会慢一些。

    【讨论】:

      【解决方案3】:

      在 CPU 上,浮点除法通常比整数除法更快。可以推测,这与 FPU 对该操作进行了更优化有关,或者浮点表示使除法更容易。但无论什么原因都改变不了事实。最后,获得第二个和第三个问题具体答案的唯一方法就是测试它。是的,我觉得你的测试没问题。

      如果我不得不冒险猜测,我认为将 MPFR 数除以整数的情况更快,因为 GMP 在计算除法时可以利用其有限的精度。

      【讨论】:

      • FPU 可能甚至没有加入其中。 MPFR 浮点数在内部使用整数肢体表示,并且很可能所有计算都使用整数算术。
      猜你喜欢
      • 1970-01-01
      • 2013-07-26
      • 2022-01-05
      • 1970-01-01
      • 2017-08-08
      • 2011-06-30
      • 2021-05-21
      • 2019-05-22
      • 2022-01-18
      相关资源
      最近更新 更多