8
votes

L'expression multiplicative Python évalue-t-elle plus rapidement si on trouve un zéro?

Supposons que l'IA ait une expression multiplicative avec beaucoup de multiplicands (petites expressions) xxx

où, par exemple, c est (x-1), D est (Y ** 2-16), k est (x y-60) ..... x, y sont des nombres
et je sais que c, d, k, j peut-être zéro
Est-ce que l'ordre d'écrire l'expression est-il une évaluation plus rapide?
Est-il préférable d'écrire c d j .... * w ou python évaluera toute expression, quelle que soit la commande que j'écris?


1 commentaires

Comment expliquer le dernier timing de Baldur?


5 Réponses :


-1
votes

Probablement pas. La multiplication est l'une des opérations les moins chères de tous. Si un 0 devrait être plus rapide, il serait nécessaire de vérifier les zéros avant et c'est probablement plus lentement que de faire la multiplication.

La solution la plus rapide doit être multiplier.Reduce ()


1 commentaires

Vous oubliez que l'INT (Python 3.x) et Longs (Python 2.x) sont une longueur variable et la durée de multiplication augmente avec la longueur. Donc 0 * énorme * énorme fonctionnera beaucoup plus vite que énorme * énorme * 0



2
votes
>>> import timeit
>>> timeit.timeit('1*2*3*4*5*6*7*8*9*9'*6)
0.13404703140258789
>>> timeit.timeit('1*2*3*4*5*6*7*8*9*0'*6)
0.13294696807861328
>>> 

0 commentaires

5
votes

N'essayez pas d'optimiser avant votre référence.

Avec cela à l'esprit, il est vrai que toutes les expressions seront évaluées même si un terme intermédiaire est zéro. P>

L'ordre peut encore importer. Les expressions sont évalué de gauche à droite . Si a, b, c, ... code> sont des nombres très volumineux, ils pourraient forcer Python à allouer beaucoup de mémoire, ralentissant le calcul avant de ne pas atteindre j = 0 code >. (Si j = 0 code> est venu plus tôt dans l'expression, le produit ne serait jamais aussi important et aucune allocation de mémoire supplémentaire ne serait nécessaire). P>

Si, après avoir choisi votre code avec TIMEIT ou CProfile , vous estimez que cela peut être votre situation, vous pourriez essayer de pré-évaluer C, D, K, j code> et tester p> xxx pré>

puis l'heure avec Timeit code> ou CProfile code>. La seule façon de vraiment dire si cela est utile dans votre situation est de repasser. P> xxx pré>

Bien que Pypy soit beaucoup plus rapide, il ne semble pas optimiser cela non plus: P >

% pypy-c
Python 2.7.3 (d994777be5ab, Oct 12 2013, 14:13:59)
[PyPy 2.2.0-alpha0 with GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``http://twitpic.com/52ae8f''
>>>> import timeit
>>>> timeit.timeit('10**100*10**100*0')
0.020643949508666992
>>>> timeit.timeit('0*10**100*10**100')
0.003732919692993164


6 commentaires

Cela rend Python semble vraiment stupide. Cela ne devrait-il pas attraper des choses comme la multiplication par zéro pendant la compilation à la bytecode et les optimiser? Pypy est-il meilleur à de telles choses?


@endolith: Non, Pypy n'il optimise pas non plus. Voir au dessus.


Mais dans vos deux exemples, le temps est réduit par un ordre de grandeur lorsque vous mettez le 0 au début, non? Je suppose que j'ai manqué quelque chose .. Pouvez-vous s'il vous plaît élaborer?


@George: L'OP est demandé si Python reconnaît que 0 fois tout est 0 et est ainsi capable de court-circuiter tout le calcul et de retourner à 0. Si Python a fait cette optimisation, alors nous devrions nous attendre à 10 ** 100 * 10 ** 100 * 0 Pour être évalué aussi vite que 0 * 10 ** 100 * 10 ** 100 . Étant donné que le calendrier est clairement différent, Python ne doit pas effectuer cette optimisation.


@George: Notez que la réponse de Nick Dandoulakis montre que Python Parfois effectue cette optimisation, mais parfois pas. Comparez dis.dis (lambda: (256 ** 256 * 0)) contre dis.dis (lambda: (100 * 256 ** 256 * 0)) Par exemple .


Ok, maintenant ça a du sens :) Merci pour la réponse super rapide / claire!



7
votes

Python V2.6.5 ne vérifie pas les valeurs zéro.

def test1():
    return 0 * 1

def test2():
    a = 1
    return 0 * a * 1

def test3():
    return 243*(5539**35)*0

def test4():
    return 0*243*(5539**35)

def test5():
    return (256**256)*0

def test6():
    return 0*(256**256)

>>> dis.dis(test1) # 0 * 1
  2           0 LOAD_CONST               3 (0)
              3 RETURN_VALUE       

>>> dis.dis(test2) # 0 * a * 1
  5           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (a)

  6           6 LOAD_CONST               2 (0)
              9 LOAD_FAST                0 (a)
             12 BINARY_MULTIPLY     
             13 LOAD_CONST               1 (1)
             16 BINARY_MULTIPLY     
             17 RETURN_VALUE        

>>> dis.dis(test3) # 243*(5539**35)*0
  9           0 LOAD_CONST               1 (243)
              3 LOAD_CONST               5 (104736434394484...681759461305771899L)
              6 BINARY_MULTIPLY     
              7 LOAD_CONST               4 (0)
             10 BINARY_MULTIPLY     
             11 RETURN_VALUE        

>>> dis.dis(test4) # 0*243*(5539**35)
 12           0 LOAD_CONST               5 (0)
              3 LOAD_CONST               6 (104736433252667...001759461305771899L)
              6 BINARY_MULTIPLY     
              7 RETURN_VALUE        

>>> dis.dis(test5) # (256**256)*0
 15           0 LOAD_CONST               4 (0L)
              3 RETURN_VALUE        

>>> dis.dis(test6) # 0*(256**256)
 18           0 LOAD_CONST               1 (0)
              3 LOAD_CONST               3 (323170060713110...853611059596230656L)
              6 BINARY_MULTIPLY     
              7 RETURN_VALUE        


5 commentaires

Ceci est pour les constantes, tandis que la question implique de multiplier des expressions.


Cela implique toujours des expressions constantes, BTW. :-) Je pense que les bonnes choses à comparer seraient quelque chose comme (disons) x = 5; y = 12; (x y-58) * (x y-59) * (x * y-60) pour différentes commandes de l'expression finale (mais avec des constantes plus grandes).


@Shreeevatsar, allez, ne a * b * c démontre déjà cela? Quoi qu'il en soit, j'ai fait plus de combinaisons. Vraiment, sauf si je manque quelque chose, je ne pense pas que le compilateur incorporera un code qui vérifie les variables pour des valeurs zéro. Le code généré est toujours une série de charges, multiplie, ajoutez, soustrayez des instructions.


Désolé, aurait dû être plus clair. Je pense que vous avez déjà démontré que Python "évaluera toute expression, peu importe l'ordre que j'écris". Mais pour répondre "fait l'ordre que j'écris l'expression des questions pour une évaluation plus rapide?", Je soupçonne toujours que la réponse est oui (éventuellement pas dans la manière dont le questionneur signifiait, c'est-à-dire même si le code généré est le même), simplement parce que ( En supposant que la multiplication est effectuée de gauche à droite) Multipliez plusieurs grosses constantes de 0 devrait être plus rapide que de multiplier des constantes sans cesse croissantes, et seulement finalement par 0. Je suis trop paresseux pour ne pas tester moi-même. : p


@Shreevatsar, OK, maintenant Vous êtes clair :) Vous voulez dire 0 * 1 * 2 * 3 * N est plus rapide que 1 * 2 * 3 * n * 0 . Oui, probablement multiplier ne fera pas la multiplication si un opérande est nul, le currying a zéro du début est meilleur. J'ai répondu spécifiquement à l'abandon de l'évaluation une fois que Python rencontre un zéro, mais ouais bon point ;-)