Почему пустой вызов функции в Python примерно на 15% медленнее для динамически скомпилированного кода Python - PullRequest
7 голосов
/ 19 ноября 2011

Это довольно плохая микрооптимизация, но мне просто любопытно. Обычно это не имеет значения в «реальном» мире.

Итак, я компилирую функцию (которая ничего не делает), используя compile(), затем вызываю exec для этого кода и получаю ссылку на функцию, которую я скомпилировал. Затем я выполняю это пару миллионов раз и рассчитываю время. Затем повторить это с локальной функцией. Почему динамически скомпилированная функция примерно на 15% медленнее (на python 2.7.2) только для вызова?

import datetime
def getCompiledFunc():
  cc = compile("def aa():pass", '<string>', 'exec')
  dd = {}
  exec cc in dd
  return dd.get('aa')

compiledFunc = getCompiledFunc()  
def localFunc():pass


def testCall(f):
  st = datetime.datetime.now()
  for x in xrange(10000000): f()
  et = datetime.datetime.now()
  return (et-st).total_seconds()

for x in xrange(10):
  lt = testCall(localFunc)
  ct = testCall(compiledFunc)
  print "%s %s %s%% slower" % (lt, ct, int(100.0*(ct-lt)/lt))

Вывод, который я получаю, выглядит примерно так:

1.139 1.319 15% slower

1 Ответ

11 голосов
/ 19 ноября 2011

Функция dis.dis () показывает, что объект кода для каждой версии идентичен:

aa
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        
localFunc
 10           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE 

Так что разница в функциональном объекте. Я сравнил каждое из полей (func_doc, func_closure и т. Д.), И одно из них отличается от func_globals. Другими словами, localFunc.func_globals != compiledFunc.func_globals.

Существует плата за предоставление собственного словаря вместо встроенных глобальных переменных (первый нужно искать при создании стекового фрейма при каждом вызове, а на последний можно напрямую ссылаться с помощью кода C, который уже знает о встроенном глобальном словаре по умолчанию).

Это легко проверить, изменив строку exec в вашем коде на:

exec cc in globals(), dd

При изменении разница во времени исчезает.

Тайна раскрыта!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...