2009-01-24 5 views
10

Il ya beaucoup d'articles sur le web concernant la performance de python, la première chose que vous lisez: concaténation des chaînes ne doit pas être fait en utilisant '+': éviter s1 + s2 + s3, à la place str.joinperformance de jointure de chaîne python

J'ai essayé les éléments suivants: concaténer deux chaînes dans le cadre d'un chemin d'accès: trois approches:

  1. '+' que je ne devrais pas faire
  2. str.join
  3. os.path.join

Voici mon code:

import os,time 

s1='/part/one/of/dir' 
s2='part/two/of/dir' 
N=10000 

t=time.clock() 
for i in xrange(N): 
    s=s1+os.sep+s2 
print time.clock()-t 

t=time.clock() 
for i in xrange(N): 
    s=os.sep.join((s1,s2)) 
print time.clock()-t 

t=time.clock() 
for i in xrange(N): 
    s=os.path.join(s1,s2) 
print time.clock()-t 

Voici les résultats (Python 2.5 WinXP)

0.0182201927899 
0.0262544541275 
0.120238186697 

devrait-il pas être exactement l'inverse?

+1

Si je je vous recommande de renommer le titre de votre question « chaîne de python se joindre à la performance », tellement il est plus évident pour les personnes qui pourraient déposer une double question. –

+1

Aussi, légèrement hors sujet, mais vous pourriez vouloir regarder le module 'timeit' pour faire des timings. –

+0

a changé le titre – Danny

Répondre

4

Il est vrai que vous ne devriez pas utiliser '+'. Votre exemple est assez spécial, essayez le même code avec:

s1='*'*100000 
s2='+'*100000 

Ensuite, la deuxième version (str.join) est beaucoup plus rapide.

12

La plupart des problèmes de performance liés à la concaténation de chaînes sont des problèmes de performances asymptotiques, de sorte que les différences deviennent plus importantes lorsque vous concaténéz de nombreuses chaînes longues. Dans votre exemple, vous effectuez la même concaténation plusieurs fois. Vous ne créez pas de longue chaîne, et il se peut que l'interpréteur python optimise vos boucles. Cela expliquerait pourquoi le temps augmente lorsque vous passez à str.join et path.join - ce sont des fonctions plus complexes qui ne sont pas aussi facilement réduites. (os.path.join fait beaucoup de vérifications sur les chaînes pour voir si elles ont besoin d'être réécrites avant qu'elles ne soient concaténées, ce qui sacrifie certaines performances pour des raisons de portabilité.)

D'ailleurs, Les chemins de fichiers ne sont généralement pas très longs, vous voulez certainement utiliser os.path.join pour la portabilité. Si la performance de la concaténation est un problème, vous faites quelque chose de très bizarre avec votre système de fichiers.

5

Cela ne devrait-il pas être exactement le contraire?

Pas nécessairement. Je ne connais pas assez les internes de Python pour commenter spécifiquement mais quelques observations communes sont que votre première boucle utilise un opérateur simple + qui est probly implémenté comme primitif par le runtime. En revanche, les autres boucles doivent d'abord résoudre un nom de module, résoudre la variable/classe qui s'y trouve et ensuite appeler une fonction membre.

Une autre remarque est que votre boucle peut être simplement trop petite pour générer des nombres significatifs. Compte tenu de votre faible temps de fonctionnement global, cela rend probablement vos tests inutiles.

De plus, votre scénario de test est hautement spécialisé sur deux chaînes courtes. De tels cas ne donnent jamais une image claire de la performance des cas limites.

4

Le conseil concerne la concaténation de nombreuses chaînes.

Pour calculer s = s1 + s2 + ...+ sn,

1) en utilisant +. Une nouvelle chaîne s1 + s2 est créée, puis une nouvelle chaîne s1 + s2 + s3 est créée, ..., etc., donc beaucoup d'allocation de mémoire et d'opérations de copie sont impliquées. En fait, s1 est copié n-1 fois, s2 est copié n-2 fois, ..., etc.

2) en utilisant "" .join ([s1, s2, ..., sn]). La concaténation est effectuée en une passe et chaque caractère des chaînes est copié une seule fois.

Dans votre code, la jointure est appelée à chaque itération, c'est donc comme si vous utilisiez +. La méthode correcte consiste à collecter les éléments dans un tableau, puis à appeler la jointure.

edit: fixer la typo

+0

vous avez une erreur ... ce n'est pas "joing" mais "join" –

1

concaténation de chaîne (+) présente une mise en oeuvre optimisée sur CPython. Mais cela peut ne pas être le cas sur d'autres architectures comme Jython ou IronPython. Donc, quand vous voulez que votre code fonctionne bien sur ces interpréteurs, vous devez utiliser la méthode .join() sur les chaînes. os.path.join() est spécifiquement destiné à rejoindre les chemins du système de fichiers. Il prend également en charge différents séparateurs de chemins. Ce serait la bonne façon de construire un nom de fichier.

0

Je voudrais ajouter un lien vers le python de wiki, où il y a des notes concernant concaténation de chaînes et que « cette section est un peu de mal à python2.5. Concaténation de chaîne Python 2.5 est assez rapide ». Je crois que la concaténation de chaînes a eu une grande amélioration depuis 2.5, et que même si str.join est encore plus rapide (spécialement pour les grandes chaînes), vous ne verrez pas autant d'améliorations que dans les anciennes versions de Python.

http://wiki.python.org/moin/PythonSpeed/PerformanceTips#StringConcatenation