为什么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