2012-05-10 57 views
87

Podczas profilowania kawałek kodu Pythona (python 2.6 do 3.2), odkryłem, że metoda str konwertować obiekt (w moim przypadku liczba całkowita) do łańcucha jest prawie o rząd wielkości mniejsza niż za pomocą formatowania ciągów.wydajność str w Pythonie

Oto benchmarku

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

Czy ktoś wie dlaczego tak jest? Czy czegoś brakuje?

+2

A co z '' {} .formatem (100000) ' – wim

+0

Jest to najwolniejsza, ale także najbardziej elastyczna. –

Odpowiedz

104

'%s' % 100000 oceniana jest przez kompilator i jest odpowiednikiem stała w czasie wykonywania.

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

% z wyrażeniem run-time nie jest (znacznie) szybciej niż str:

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

Należy pamiętać, że str jest wciąż nieco wolniej, jak @DietrichEpp powiedział, to dlatego str obejmuje wyszukiwanie i funkcje wywołania funkcji, podczas gdy % kompiluje się do pojedynczego kodu bajtowego:

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

Oczywiście powyższe jest prawdziwe dla systemu, na którym testowałem (CPython 2.7); inne implementacje mogą się różnić.

+0

Rzeczywiście wygląda to na powód, po prostu próbowałem siebie i formatowanie stringów jest o około 5% szybsze niż "str". Dziękuję za odpowiedź. Nie ma powodu, aby zmieniać kod wszędzie :-) –

+2

Aby rozwinąć dalej: 'str' jest nazwą, która może być odbiciem do czegoś innego niż typ łańcucha, ale formatowanie - czyli metoda" str .__ mod__ "- nie może być zastąpione, co umożliwia kompilatorowi przeprowadzenie optymalizacji. Kompilator nie robi zbyt wiele na drodze optymalizacji, ale robi więcej, niż mogłoby się wydawać :) –

+4

... a lekcją do nauczenia się tutaj jest: nigdy nie używaj literałów w takich testach! – UncleZeiv

14

Jedną z przyczyn, która przychodzi na myśl, jest to, że str(100000) dotyczy wyszukiwania globalnego, ale nie ma takiej potrzeby: "%s"%100000. Globalny kod musi być sprawdzony w globalnym zasięgu. To nie uwzględnia całej różnicy:

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

Jak zauważył thg435,

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