为什么Python在试图计算一个非常大的数字时“抢先”挂起?
我之前曾经问过这个问题 ,关于杀死一个使用了太多内存的进程,我已经得到了大部分解决scheme。
然而,有一个问题:计算大量的数字似乎没有受到我试图使用的方法。 下面的代码是为了在这个过程中放置一个10秒钟的CPU时间限制。
import resource import os import signal def timeRanOut(n, stack): raise SystemExit('ran out of time!') signal.signal(signal.SIGXCPU, timeRanOut) soft,hard = resource.getrlimit(resource.RLIMIT_CPU) print(soft,hard) resource.setrlimit(resource.RLIMIT_CPU, (10, 100)) y = 10**(10**10)
当我运行这个脚本(在Unix机器上)时,我期望看到的是这样的:
-1 -1 ran out of time!
相反,我没有输出。 我得到输出的唯一方法是用Ctrl + C ,如果我在10秒后按Ctrl + C ,我会得到这个结果:
^C-1 -1 ran out of time! CPU time limit exceeded
如果我在 10秒之前 按Ctrl + C ,那么我必须做两次,控制台输出如下所示:
^C-1 -1 ^CTraceback (most recent call last): File "procLimitTest.py", line 18, in <module> y = 10**(10**10) KeyboardInterrupt
在试验和试图弄清楚这一点的过程中,我还将time.sleep(2)
放在了打印和大数计算之间。 这似乎没有任何效果。 如果我将y = 10**(10**10)
更改为y = 10**10
,则印刷和睡眠声明按预期工作。 在print语句之后,将flush=True
添加到print语句或sys.stdout.flush()
也不起作用。
为什么我不能限制CPU时间来计算一个非常大的数字? 我怎样才能解决或至less减轻这一点?
附加信息:
Python版本: 3.3.5 (default, Jul 22 2014, 18:16:02) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)]
:16: 3.3.5 (default, Jul 22 2014, 18:16:02) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)]
Linux信息: Linux web455.webfaction.com 2.6.32-431.29.2.el6.x86_64 #1 SMP Tue Sep 9 21:36:05 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
TLDR: Python预先计算代码中的常量。 如果至less有一个中间步骤计算出非常大的数字,则该过程将受CPU时间限制。
它花费了很多search的时间,但是我发现了Python 3 在计算任何内容之前预先计算了它在代码中find的常量字面值的证据。 其中之一是这个网页: Python的窥视优化器 。 我在下面引用了一些内容。
ConstantExpressionEvaluator
这个类预先计算了一些常量expression式,并将它们存储在函数的常量列表中,包括明显的二元和一元运算以及只包含常量的元组。 特别值得注意的是,复杂文字不是由编译器表示为常量,而是expression式,所以2 + 3jperformance为
LOAD_CONST n (2)
LOAD_CONST m (3j)
BINARY_ADD
这个类将这些转换成
LOAD_CONST q (2+3j)
这对于使用复杂常量的代码可能会导致相当大的性能提升。
2+3j
作为例子的事实非常强烈地表明,不仅是预先计算和caching的小常量,还有代码中的任何常量文字。 我也发现了另一个堆栈溢出问题的注释 ( 是恒定的计算caching在Python? ):
请注意,对于Python 3,窥视优化器会预先计算
1/3
常量。 (当然,CPython是特定的) – Mark Dickinson 10月7日19:40
这些由replace的事实支持
y = 10**(10**10)
这也挂起,即使我从来没有打电话的function!
def f(): y = 10**(10**10)
好消息
幸运的是,在我的代码中,我没有任何这样巨大的字面常量。 这些常量的任何计算都会在稍后发生,这可能会受到CPU时间限制的限制。 我变了
y = 10**(10**10)
对此,
x = 10 print(x) y = 10**x print(y) z = 10**y print(z)
并根据需要得到这个输出!
-1 -1 10 10000000000 ran out of time!
故事的寓意:如果Python尝试预先计算的代码中没有大的字面常量,那么通过CPU时间或内存消耗(或其他方法)限制进程将会工作 。
使用一个function。
看起来,Python试图预先计算整数文字(我只有经validation据;如果有人有来源,请让我知道)。 这通常是一个有用的优化,因为绝大多数脚本文字可能足够小,不会在预计算时产生明显的延迟。 为了解决这个问题,你需要让你的文字是一个非常量计算的结果,就像带参数的函数调用一样。
例:
import resource import os import signal def timeRanOut(n, stack): raise SystemExit('ran out of time!') signal.signal(signal.SIGXCPU, timeRanOut) soft,hard = resource.getrlimit(resource.RLIMIT_CPU) print(soft,hard) resource.setrlimit(resource.RLIMIT_CPU, (10, 100)) f = lambda x=10:x**(x**x) y = f()
这给出了预期的结果:
xubuntu@xubuntu-VirtualBox:~/Desktop$ time python3 hang.py -1 -1 ran out of time! real 0m10.027s user 0m10.005s sys 0m0.016s