47 votes

Pourquoi Python "préventivement" se bloquer lorsque vous essayez de calculer un très grand nombre?

J'ai posé cette question avant de tuer un processus qui utilise trop de mémoire, et j'ai plus de solution a fonctionné.

Cependant, il y a un problème: le calcul massivement semble être épargnée par la méthode que je suis en train d'utiliser. Ce code ci-dessous est destiné à mettre un 10 secondes de temps CPU limite sur le processus.

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)

Ce que j' attends de voir quand je lance ce script sur une machine Unix) est-ce:

-1 -1
ran out of time!

Au lieu de cela, je n'ai pas de sortie. La seule manière, je suis sortie avec Ctrl + C, et je reçois ce si je Ctrl + C au bout de 10 secondes:

^C-1 -1
ran out of time!
CPU time limit exceeded

Si je Ctrl + C avant 10 secondes, puis-je le faire deux fois, et la sortie de la console ressemble à ceci:

^C-1 -1
^CTraceback (most recent call last):
  File "procLimitTest.py", line 18, in <module>
    y = 10**(10**10)
KeyboardInterrupt

Dans le cadre de l'expérimentation et d'essayer de comprendre cela, j'ai aussi mis time.sleep(2) entre le print et le grand nombre de calcul. Il ne semble pas avoir d'effet. Si je change y = 10**(10**10) de y = 10**10, puis l'imprimer et le sommeil états fonctionner comme prévu. L'ajout d' flush=True pour l'instruction print ou sys.stdout.flush() après l'instruction d'impression ne fonctionne pas non plus.

Pourquoi ne puis-je pas de limite de temps PROCESSEUR pour le calcul d'un très grand nombre? Comment puis-je réparer ou au moins atténuer ce risque?


Informations supplémentaires:

Une version de Python: 3.3.5 (default, Jul 22 2014, 18:16:02) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)]

Linux renseignements: 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

50voto

El'endia Starman Points 1598

TLDR: Python précalcule constantes dans le code. Si un très grand nombre est calculé avec au moins une étape intermédiaire, le processus va être temps CPU limited.


Il a fallu un peu de recherche, mais j'ai découvert des preuves que Python 3 n' précalculer les constantes littérales qu'il trouve dans le code avant d'évaluer quoi que ce soit. L'un d'eux est à cette page web: Un Judas Optimizer pour Python. J'ai cité certains de il ci-dessous.

ConstantExpressionEvaluator

Cette classe précalcule un certain nombre d'expressions constantes et les stocke dans la fonction de la liste des constantes, y compris évident binaire et les opérations unaires et les tuples composée uniquement des constantes. De particulier à noter est le fait que le complexe littéraux ne sont pas représentés par le compilateur comme des constantes, mais comme des expressions, donc 2+3j apparaît comme

LOAD_CONST n (2) LOAD_CONST m (3j) BINARY_ADD

Cette classe convertit ceux de

LOAD_CONST q (2+3j)

ce qui peut entraîner dans un assez grand gain de performance pour le code qui utilise des constantes complexes.

Le fait qu' 2+3j est utilisé comme un exemple très fortement penser que non seulement les petites constantes sont précalculées et mis en cache, mais aussi de toutes les constantes littérales dans le code. J'ai aussi trouvé ce commentaire sur un autre Débordement de Pile question (Sont constantes, les calculs mis en cache en Python?):

Notez que pour Python 3, le judas de l'optimiseur ne précalculer l' 1/3 constante. (Disponible spécifiques, bien sûr.) – Mark Dickinson 7 Oct à 19:40

Elles sont soutenues par le fait que le remplacement de

y = 10**(10**10)

avec cela aussi se bloque, même si je n'ai jamais appeler la fonction!

def f():
    y = 10**(10**10)

La bonne nouvelle

Heureusement pour moi, je n'ai pas de tels géants constantes littérales dans mon code. Tout calcul de ces constantes se fera plus tard, qui peut être, et est limité par le CPU limite de temps. J'ai changé

y = 10**(10**10)

pour cela,

x = 10
print(x)
y = 10**x
print(y)
z = 10**y
print(z)

et a obtenu cette sortie, comme vous le souhaitez!

-1 -1
10
10000000000
ran out of time!

La morale de l'histoire: la Limitation d'un processus en temps CPU ou de la mémoire de la consommation (ou une autre méthode) fonctionnera si il n'y a pas une grande constante littérale dans le code Python essaie de précalculer.

12voto

Mego Points 368

L'utilisation d'une fonction.

Il semble que Python essaie de précalculer les littéraux entiers (je n'ai que des preuves empiriques; si quelqu'un a une source s'il vous plaît laissez-moi savoir). Cela devrait normalement être utile pour l'optimisation, puisque la grande majorité des littéraux dans les scripts sont probablement suffisamment petite pour ne pas subir des délais d'attente lors de la precomputing. Pour contourner ce problème, vous avez besoin pour faire de votre littérale être le résultat d'un non-constante de calcul, comme un appel de fonction avec des paramètres.

Exemple:

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()

Cela ne se bloque plus, mais sur mon système, il renvoie une erreur:

$ python3 hang.py
-1 -1
Traceback (most recent call last):
  File "./hang.py", line 12, in <module>
    resource.setrlimit(resource.RLIMIT_CPU, (10, 100))
ValueError: current limit exceeds maximum limit

Je vais faire des essais plus et de voir ce qui se passe.

Mise à JOUR:

L'exécution de cette dans une Xubuntu machine virtuelle m'a donné la sortie que j'attendais (j'aurais du lire les étiquettes avant d'essayer de l'exécuter dans Windows!):

xubuntu@xubuntu-VirtualBox:~/Desktop$ time python3 hang.py
-1 -1
ran out of time!

real    0m10.027s
user    0m10.005s
sys     0m0.016s

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X