Ответ 1
Вы можете избежать этого, перейдя на Python 3.3 или заменив стандартную систему импорта альтернативой. В разговоре strace
, который я дал две недели назад в PyOhio, я обсуждаю неудачную производительность O (nm) (для n каталогов и возможных суффиксов) старого механизма импорта; начинайте с этот слайд.
Я демонстрирую, как easy_install
плюс веб-среда с поддержкой Zope генерирует 73,477 системных вызовов просто для того, чтобы сделать достаточно импорта для запуска и запуска.
После быстрой установки bottle
в virtualenv на моем ноутбуке, например, я нахожу, что для Python требуется, чтобы для этого модуля было загружено ровно 1000 вызовов:
$ strace -c -e stat64,open python -c 'import bottle'
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000179 0 1519 1355 open
0.00 0.000000 0 475 363 stat64
------ ----------- ----------- --------- --------- ----------------
100.00 0.000179 1994 1718 total
Если я прыгаю в os.py
, я могу добавить импортера кеширования и даже с наивной реализацией может сократить количество промахов почти на тысячу:
$ strace -c -e stat64,open python -c 'import bottle'
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000041 0 699 581 open
0.00 0.000000 0 301 189 stat64
------ ----------- ----------- --------- --------- ----------------
100.00 0.000041 1000 770 total
Я выбрал os.py
для эксперимента, потому что strace
показывает, что он является самым первым модулем, который импортирует Python, и чем раньше мы сможем установить наш импортер, тем меньше стандартных модулей библиотеки Python придется импортировать под своим старым ужасный медленный режим!
# Put this right below "del _names" in os.py
class CachingImporter(object):
def __init__(self):
self.directory_listings = {}
def find_module(self, fullname, other_path=None):
filename = fullname + '.py'
for syspath in sys.path:
listing = self.directory_listings.get(syspath, None)
if listing is None:
try:
listing = listdir(syspath)
except OSError:
listing = []
self.directory_listings[syspath] = listing
if filename in listing:
modpath = path.join(syspath, filename)
return CachingLoader(modpath)
class CachingLoader(object):
def __init__(self, modpath):
self.modpath = modpath
def load_module(self, fullname):
if fullname in sys.modules:
return sys.modules[fullname]
import imp
mod = imp.new_module(fullname)
mod.__loader__ = self
sys.modules[fullname] = mod
mod.__file__ = self.modpath
with file(self.modpath) as f:
code = f.read()
exec code in mod.__dict__
return mod
sys.meta_path.append(CachingImporter())
Конечно, это грубые края - он не пытается обнаружить файлы .pyc
или файлы .so
или любые другие расширения, которые Python может искать. Он также не знает о файлах __init__.py
или о модулях внутри пакетов (для чего потребуется выполнить lsdir()
в подкаталогах записей sys.path
). Но это, по крайней мере, иллюстрирует, что тысячи дополнительных вызовов можно устранить с помощью чего-то подобного и продемонстрировать направление, которое вы можете попробовать. Когда он не может найти модуль, вместо этого вместо него запускается обычный механизм импорта.
Интересно, есть ли хороший импортер кэширования, уже доступный на PyPI или где-то еще? Это похоже на то, что было бы написано сотни раз в разных магазинах. Я думал, что Noah Gift написал один и поместил его в сообщение в блоге или что-то в этом роде, но я не могу найти ссылку, подтверждающую мою память.
Изменить:, поскольку @ncoglan упоминает в комментариях, есть альфа-релиз backport новой системы импорта Python 3.3+ для Python 2.7, доступный в PyPI: http://pypi.python.org/pypi/importlib2 - к сожалению, похоже, что вопросник все еще застрял на 2.6.