【问题标题】:Efficient divisibility check for printing progress打印进度的高效除法检查
【发布时间】:2020-02-25 18:24:36
【问题描述】:

在我的程序中,当我觉得需要很长时间时,我会执行一个进度检查:我打印迭代器的当前值和每 n 次迭代的时间差,使用模 0 (if i % n == 0...) 。

Python 解释器(或任何其他编程语言编译器)是否有效地检查 2 的幂的整除性?就像取最后一个(幂)位并检查是否都等于 0?

【问题讨论】:

  • 我可以向您保证,单模运算不是您程序中的瓶颈。尝试仅在出现问题时担心性能。
  • 另外,这不是 Python 中的模运算符
  • 请记住,检查模数非常快。打印通常很慢
  • 尤其是在Python中,“我觉得”并不是寻找性能不佳原因的好依据。您是否尝试过使用分析器?
  • 我认为 OP 只是想要一些进展的迹象,而不是试图找到一种方法来加快循环。

标签: python modulo


【解决方案1】:

Python 中任何整数运算的效率充其量都是值得怀疑的。除了解释器开销之外,您还面临整数是精度可变的事实,需要对每个操作进行更多处理。

话虽如此,您可以使用位旋转来检查是否可以被 2 的幂除。对于给定的n,您可以这样做

p = (1 <<  n) - 1 # n of your choice
...
if not i & p:
    ...

这使p 成为n 的预计算掩码一位。按位&amp; 运算符检查是否设置了最低的n birs。

【讨论】:

  • 这似乎是。我做了一个 timeit,大约花了 73 ns,而 not i % m 花了大约 55 ns。
  • @HeapOverflow。我并不感到意外。 int 操作的瓶颈是遍历无限精度整数数据的循环,通常不是操作本身。
  • 您是否将 ns 中的时间作为算术平均值?以 ns 为单位的测量似乎更多的是测量误差。
  • @Ferazhu 我没有做任何不寻常的事情。我跑了例如python -m timeit -s "i = 1000; p = 4095" "not i &amp; p",它报告了5000000 loops, best of 5: 71.4 nsec per loop
  • @MadPhysicist 不确定您的意思,但python -m timeit "for i in range(10**6): pass" 表示 31 毫秒,而 python -m timeit -s "p = 4095" "for i in range(10**6): not i &amp; p" 表示 92 毫秒。因此,在这种情况下,循环/步进似乎不是成为瓶颈。
【解决方案2】:

查看以下 lambdas 的格式化字节码,在 CPython 中的字节码级别似乎没有任何区别:

In [1]: import dis

In [2]: dis.dis(lambda x: x % 3 == 0)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (3)
              4 BINARY_MODULO
              6 LOAD_CONST               2 (0)
              8 COMPARE_OP               2 (==)
             10 RETURN_VALUE

In [3]: dis.dis(lambda x: x % 2 == 0)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (2)
              4 BINARY_MODULO
              6 LOAD_CONST               2 (0)
              8 COMPARE_OP               2 (==)
             10 RETURN_VALUE

In [4]:

BINARY_MODULO(一个取 2 个参数的模运算,因此是“二进制”)最终在 CPython 中由 Objects/longobject.c 中的 long_mod 函数处理(对于 2 个整数,又名“longs”)。

通过浏览代码,对于操作数只有一个数字的情况,似乎有一些初始优化。

如果他们不这样做,x_divrem 最终会被调用,其中包含以下注释:

/* We follow Knuth [The Art of Computer Programming, Vol. 2 (3rd
       edn.), section 4.3.1, Algorithm D], except that we don't explicitly
       handle the special case when the initial estimate q for a quotient
       digit is >= PyLong_BASE: the max value for q is PyLong_BASE+1, and
       that won't overflow a digit. */

我真的不想花时间从代码中理解算法(而且我没有提到这本书),而是从略读它——看起来它正在执行一些聪明的位移操作。我敢打赌,在除数是 2 的幂的情况下——这个算法会运行得更快。不过不完全确定。

您可以在这里查看: https://github.com/python/cpython/blob/eb8ac57af26c4eb96a8230eba7492ce5ceef7886/Objects/longobject.c#L2712

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-30
    • 1970-01-01
    相关资源
    最近更新 更多