【问题标题】:Why do these constant functions performance differ?为什么这些常量函数的性能不同?
【发布时间】:2020-09-18 09:58:02
【问题描述】:

在下面的 sn-p 中,为什么 py_sqrt2 几乎是 np_sqrt2 的两倍?

from time import time
from numpy import sqrt as npsqrt
from math import sqrt as pysqrt

NP_SQRT2 = npsqrt(2.0)
PY_SQRT2 = pysqrt(2.0)

def np_sqrt2():
    return NP_SQRT2

def py_sqrt2():
    return PY_SQRT2

def main():
    samples = 10000000

    it = time()
    E = sum(np_sqrt2() for _ in range(samples)) / samples
    print("executed {} np_sqrt2's in {:.6f} seconds E={}".format(samples, time() - it, E))

    it = time()
    E = sum(py_sqrt2() for _ in range(samples)) / samples
    print("executed {} py_sqrt2's in {:.6f} seconds E={}".format(samples, time() - it, E))


if __name__ == "__main__":
    main()

$ python2.7 snippet.py 
executed 10000000 np_sqrt2's in 1.380090 seconds E=1.41421356238
executed 10000000 py_sqrt2's in 0.855742 seconds E=1.41421356238
$ python3.6 snippet.py 
executed 10000000 np_sqrt2's in 1.628093 seconds E=1.4142135623841212
executed 10000000 py_sqrt2's in 0.932918 seconds E=1.4142135623841212

请注意,它们是常量函数,仅从具有相同值的预计算全局变量中加载,并且常量仅在程序启动时的计算方式不同。

此外,这些函数的反汇编表明它们按预期工作并且只访问全局常量。

In [73]: dis(py_sqrt2)                                                                                                                                                                            
  2           0 LOAD_GLOBAL              0 (PY_SQRT2)
              2 RETURN_VALUE

In [74]: dis(np_sqrt2)                                                                                                                                                                            
  2           0 LOAD_GLOBAL              0 (NP_SQRT2)
              2 RETURN_VALUE

【问题讨论】:

    标签: python performance bytecode python-internals repr


    【解决方案1】:

    因为您每次都将它发送到 c 以获取一个值

    改用以下方法

    t0=time.time()
    numpy.sqrt([2]*10000)
    t1 = time.time()
    print("Took %0.3fs to do 10k sqrt(2)"%(t1-t0))
    
    t0 = time.time()
    for i in range(10000):
        numpy.sqrt(2)
    t1 = time.time()
    print("Took %0.3fs to do 10k math.sqrt(2)"%(t1-t0))
    

    【讨论】:

    • 感谢您的回答,但我不确定这是否回答了原始问题。我确实理解减少函数调用是件好事,但在问题的示例中,两个循环都具有相同数量的函数调用,这些函数调用显然是相同的。我的问题与 python 在幕后所做的更相关,这使得访问其中一个全局变量 (NP_SQRT2) 比访问另一个全局变量 (PY_SQRT2) 慢得多。
    • numpy 每次都需要将其发送到 c/c++ .... 所以会有一点点额外的开销 ... 与将其保存在 python 中(正常的 sqrt 函数所做的)相比。 ... numpy 旨在处理大量数据,而不是一遍又一遍地处理单个点
    • 现在我注意到常量是一个 numpy 类型,您的回答更有意义。谢谢。
    【解决方案2】:

    在脚本的两个版本上运行 perf record 后,一个仅使用 PY_SQRT2,另一个仅使用 NP_SQRT2,似乎使用 numpy 常量的版本正在进行额外的调用。

    这让我意识到这两个常量有不同的类型:

    In [4]: type(PY_SQRT2)                                                          
    Out[4]: float
    
    In [5]: type(NP_SQRT2)                                                          
    Out[5]: numpy.float64
    

    所以在sum 上运行(也许正在加载?numpy.float64s 比原生浮点数慢。

    This answer 也有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2022-01-08
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多