desempenho str em python

87

Ao python 2.6criar o perfil de uma parte do código Python ( até 3.2), descobri que o strmétodo para converter um objeto (no meu caso, um inteiro) em uma string é quase uma ordem de magnitude mais lento do que usar a formatação de string.

Aqui está o benchmark

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

Alguém sabe por que isso acontece? Estou esquecendo de algo?

Luca Sbardella
fonte
2
E quanto a'{}'.format(100000)
wim
É o mais lento, mas também o mais flexível.
Luca Sbardella

Respostas:

105

'%s' % 100000 é avaliada pelo compilador e é equivalente a uma constante em tempo de execução.

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

%com uma expressão de tempo de execução não é (significativamente) mais rápido do que str:

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

Observe que strainda é um pouco mais lento, como disse @DietrichEpp, porque strenvolve operações de pesquisa e chamada de função, enquanto %compila para um único bytecode imediato:

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

Claro que o acima é verdadeiro para o sistema que testei (CPython 2.7); outras implementações podem ser diferentes.

georg
fonte
Na verdade, esse parece ser o motivo, eu mesmo tentei e a formatação de strings é cerca de 5% mais rápida do que str. Obrigado pela resposta. Não há razão para mudar o código em todos os lugares :-)
Luca Sbardella
2
Para elaborar mais: stré um nome que pode ser devolvido a algo diferente do tipo de string, mas a formatação de string - isto é, o str.__mod__método - não pode ser substituída, o que permite que o compilador faça a otimização. O compilador não faz muito em termos de otimização, mas faz mais do que você imagina :)
Karl Knechtel
4
... e a lição a aprender aqui é: nunca use literais em testes como esses!
UncleZeiv
Esta entrada de blog em particular pode interessar a você: skymind.com/~ocrow/python_string . Ele contém um gráfico de benchmarks para vários métodos de concatenação de string semelhantes ao que você forneceu acima.
Aaron Newton
14

Um motivo que vem à mente é o fato de str(100000)envolver uma pesquisa global, mas "%s"%100000não envolve . O strglobal tem que ser visto no âmbito global. Isso não explica toda a diferença:

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

Conforme observado por thg435 ,

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