Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在Py3中,在本地命名空间中编写代码比在全局命名空间中编写代码慢_Python_Pypy - Fatal编程技术网

Python 在Py3中,在本地命名空间中编写代码比在全局命名空间中编写代码慢

Python 在Py3中,在本地命名空间中编写代码比在全局命名空间中编写代码慢,python,pypy,Python,Pypy,我用bash的时间函数测试了下面的代码 # test_local.py def main(): n = 10 ** 7 mod = 10 ** 9 + 7 x = 0 for i in range(n): x += i x %= mod if __name__ == "__main__": main() 为什么PyPy3中的test_local.py比test_global.py慢,尽管在CPython中结果相反 更

我用bash的时间函数测试了下面的代码

# test_local.py
def main():
    n = 10 ** 7
    mod = 10 ** 9 + 7
    x = 0
    for i in range(n):
        x += i
        x %= mod


if __name__ == "__main__":
    main()
为什么PyPy3中的test_local.py比test_global.py慢,尽管在CPython中结果相反


更新
根据Armin Rigo的回答,我尝试了下面的另一个代码。 它工作得更快,同时将大部分零件保留在main()中


答案可能在“%”运算符中。PyPy有一个JIT,它一次查看一个循环(包括该循环完成的所有调用,如果有的话)。在代码的第一个版本中,循环是使用“x%=mod”编译的,其中“mod”的值不知道是一个常量——它只是来自函数前面的一个值。它可能看起来是不变的,但并不完全是不变的:如果您使用(比如)一些调试挂钩来运行程序,那么您可以想象,在甚至进入循环之前,即在JIT开始之前,您已经更改了它的值。由于这个原因,JIT不会因为局部变量是常量而进行优化;此外,这在某种程度上是罕见的情况:局部变量通常不是常数

另一方面,在第二种情况下,“x%=mod”使用全局变量“mod”。全局变量通常是常量(例如,大多数全局变量实际上是函数、类或数值常量)。因此PyPy中的JIT包含支持这一点的特殊代码。全局变量在内部比局部变量更复杂:如果它们被更改,它们会记住,并且只要它们没有被更改,它们就会记录生成的汇编程序片段(假设它们是常量)。因此,只要您不更改“mod”,那么JIT将编译“x%=mod”,假设“mod”正好是常量100000007

为什么会有不同?因为常数的除法和模运算被聪明的代码取代,而使用乘法,使用一个众所周知的技巧。GCC或任何好的C编译器也使用类似的技巧。如果您对细节感兴趣,可以使用基于乘法的内容替换常数除法的代码如下(UINT_MUL_HIGH does“(x*y)>>64”,带有无符号64位数字x和y,这是一条汇编指令):

谢谢您的详细回答。>“JIT不会优化局部变量不变”这是一个很好的提示!请注意,JIT确实对具有循环常量值的局部变量进行了优化:例如,如果在循环中使用“k*5”,并且不更改“k”,那么编译后的代码将仅在第一次迭代中执行乘法,并在后续迭代中重用它。但是替换的div->mul不适合这种情况:它只在JIT编译时已知值的情况下工作。
# test_global.py
n = 10 ** 7
mod = 10 ** 9 + 7
x = 0
for i in range(n):
    x += i
    x %= mod
Results:
python3 test_local.py  1.03s user 0.02s system 91% cpu 1.139 total
python3 test_global.py  1.92s user 0.01s system 98% cpu 1.956 total

pypy3 test_local.py  0.26s user 0.12s system 36% cpu 1.034 total
pypy3 test_global.py  0.13s user 0.03s system 97% cpu 0.161 total

Env:
CPython3 (3.8.2), PyPy3 (7.3.0)
#  test_global_constant.py
MOD = 10 ** 9 + 7


def main():
    n = 10 ** 7
    x = 0
    for i in range(n):
        x += i
        x %= MOD


if __name__ == "__main__":
    main()
Results:
python3 test_global_constant.py  1.08s user 0.01s system 99% cpu 1.099 total

pypy3 test_global_constant.py  0.12s user 0.03s system 95% cpu 0.164 total