performances str en python

87

Lors du profilage d'un morceau de code python ( python 2.6jusqu'à 3.2), j'ai découvert que la strméthode pour convertir un objet (dans mon cas un entier) en une chaîne est presque un ordre de grandeur plus lente que l'utilisation du formatage de chaîne.

Voici la référence

>>> from timeit import Timer
>>> Timer('str(100000)').timeit()
0.3145311339386332
>>> Timer('"%s"%100000').timeit()
0.03803517023435887

Est-ce que quelqu'un sait pourquoi c'est le cas? Est-ce que je manque quelque chose?

Luca Sbardella
la source
2
Et qu'en est-il'{}'.format(100000)
wim
C'est le plus lent mais aussi le plus flexible.
Luca Sbardella

Réponses:

105

'%s' % 100000 est évalué par le compilateur et équivaut à une constante au moment de l'exécution.

>>> import dis
>>> dis.dis(lambda: str(100000))
  8           0 LOAD_GLOBAL              0 (str)
              3 LOAD_CONST               1 (100000)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda: '%s' % 100000)
  9           0 LOAD_CONST               3 ('100000')
              3 RETURN_VALUE        

%avec une expression d'exécution n'est pas (significativement) plus rapide que str:

>>> Timer('str(x)', 'x=100').timeit()
0.25641703605651855
>>> Timer('"%s" % x', 'x=100').timeit()
0.2169809341430664

Notez que strc'est encore légèrement plus lent, comme l'a dit @DietrichEpp, car cela strimplique des opérations de recherche et d'appel de fonction, tandis que la %compilation en un seul bytecode immédiat:

>>> dis.dis(lambda x: str(x))
  9           0 LOAD_GLOBAL              0 (str)
              3 LOAD_FAST                0 (x)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE        
>>> dis.dis(lambda x: '%s' % x)
 10           0 LOAD_CONST               1 ('%s')
              3 LOAD_FAST                0 (x)
              6 BINARY_MODULO       
              7 RETURN_VALUE        

Bien sûr, ce qui précède est vrai pour le système sur lequel j'ai testé (CPython 2.7); d'autres implémentations peuvent différer.

Georg
la source
En effet, cela ressemble à la raison, je viens d'essayer moi-même et le formatage des chaînes est à peu près 5% plus rapide que str. Merci de répondre. Aucune raison de changer de code partout :-)
Luca Sbardella
2
Pour élaborer davantage: strest un nom qui peut être rebondi vers autre chose que le type de chaîne, mais le formatage de chaîne - c'est-à-dire la str.__mod__méthode - ne peut pas être remplacé, ce qui permet au compilateur de faire l'optimisation. Le compilateur ne fait pas grand-chose en termes d'optimisation, mais il fait plus que vous ne le pensez :)
Karl Knechtel
4
... et la leçon à retenir ici est la suivante: n'utilisez jamais de littéraux dans des tests comme ceux-ci!
UncleZeiv
Cette entrée de blog particulière peut vous intéresser: skymind.com/~ocrow/python_string . Il contient un tableau de points de repère pour diverses méthodes de concaténation de chaînes similaires à ce que vous avez fourni ci-dessus.
Aaron Newton
14

Une raison qui me vient à l'esprit est le fait que cela str(100000)implique une recherche globale, mais ce "%s"%100000n'est pas le cas. Le strglobal doit être regardé dans la portée globale. Cela ne tient pas compte de toute la différence:

>>> Timer('str(100000)').timeit()
0.2941889762878418
>>> Timer('x(100000)', 'x=str').timeit()
0.24904918670654297

Comme indiqué par thg435 ,

>>> Timer('"%s"%100000',).timeit()
0.034214019775390625
>>> Timer('"%s"%x','x=100000').timeit()
0.2940788269042969
Dietrich Epp
la source