производительность str в Python

88

При профилировании фрагмента кода Python ( python 2.6до 3.2) я обнаружил, что strметод преобразования объекта (в моем случае целого числа) в строку почти на порядок медленнее, чем при использовании форматирования строки.

Вот эталон

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

Кто-нибудь знает, почему это так? Я что-то упускаю?

Лука Сбарделла
источник
2
А что насчет'{}'.format(100000)
wim
Это самый медленный, но и самый гибкий.
Лука Сбарделла

Ответы:

106

'%s' % 100000 оценивается компилятором и эквивалентна константе во время выполнения.

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

%с выражением времени выполнения не (значительно) быстрее, чем str:

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

Обратите внимание, что strэто все еще немного медленнее, как сказал @DietrichEpp, потому что strвключает операции поиска и вызова функций, а %компилируется в один непосредственный байт-код:

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

Конечно, вышесказанное верно для системы, на которой я тестировал (CPython 2.7); другие реализации могут отличаться.

Георг
источник
На самом деле это похоже на причину, я только что попробовал, и форматирование строк примерно на 5% быстрее, чем str. Спасибо за ответ. Нет причин менять код везде :-)
Лука Сбарделла
2
Для дальнейшего уточнения: strэто имя, которое может быть повторно связано с чем-то другим, кроме строкового типа, но форматирование строки - то есть str.__mod__метод - не может быть заменено, что позволяет компилятору выполнить оптимизацию. Компилятор не слишком много оптимизирует, но он делает больше, чем вы думаете :)
Карл Кнехтель
4
... и урок, который следует извлечь из этого: никогда не используйте литералы в подобных тестах!
UncleZeiv
Эта конкретная запись в блоге может вас заинтересовать: skymind.com/~ocrow/python_string . Он содержит таблицу тестов для различных методов конкатенации строк, подобных тем, которые вы предоставили выше.
Аарон Ньютон
14

Одна из причин, которые приходят на ум, - это тот факт, что str(100000)глобальный поиск требует, но "%s"%100000не делает этого. strГлобальное должно быть ищутся в глобальном масштабе. Это не объясняет всей разницы:

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

Как отмечает thg435 ,

>>> Timer('"%s"%100000',).timeit()
0.034214019775390625
>>> Timer('"%s"%x','x=100000').timeit()
0.2940788269042969
Дитрих Эпп
источник