Чтобы ответить на первый вопрос об оптимизации, перейдя к любому. Нет, я считаю, что это не очень хорошая идея, потому что это не ее цель. Конечно, это легко реализовать, но обслуживание может стать кошмаром. Таким образом, в вашу кодовую базу вводится новая ошибка. Если функция когда-либо возвращает false, то итератор не будет полностью использован, вызывая странное поведение и ошибки, которые трудно отследить. Кроме того, существуют более быстрые (или, по крайней мере, почти такие же быстрые) альтернативы использованию встроенного любого.
Конечно, вы можете сделать исключение, потому что кажется, что любой действительно может выполнить deque, но использование любого из них, безусловно, является крайним и чаще всего ненужным. Фактически, если что-то и происходит, вы можете вводить оптимизации, которые могут перестать быть «оптимальными» после обновления базы кода Python (см. 2.7 против 3.2).
Еще одна вещь, которую стоит упомянуть, это использование любого не сразу имеет никакого смысла. Вопрос о том, следует ли реализовывать расширение C перед использованием чего-либо подобного, также является спорным. Лично я предпочел бы это по смысловым причинам.
Что касается оптимизации собственного кода, давайте начнем с того, с чем мы столкнулись: обратитесь к run_any_like_presentation. Это довольно быстро:)
Начальная реализация может выглядеть примерно так:
import itertools, hashlib
_md5 = hashlib.md5()
def run():
for _ in xrange(100000000):
_md5.update("foo")
Первый шаг - использовать itertools.repeat, чтобы сделать что-то N раз.
def run_just_repeat():
for foo in itertools.repeat("foo", 100000000):
_md5.update(foo)
Вторая вторая оптимизация заключается в использовании itertools.imap, чтобы получить увеличение скорости от необходимости передавать ссылку foo в коде Python. Сейчас в С.
def run_imap_and_repeat():
for do_work in itertools.imap(_md5.update, itertools.repeat("foo", 10000000)):
pass
Третья оптимизация - полностью переместить цикл for в код C.
import collections
def run_deque_imap_and_repeat():
collections.deque(itertools.imap(_md5.update, itertools.repeat("foo", 10000000)))
Окончательная оптимизация состоит в том, чтобы переместить все потенциальные поиски в пространство имен функции запуска:
Эта идея взята с самого конца http://docs.python.org/library/itertools.html?highlight=itertools
Обратите внимание, многие из вышеперечисленных рецептов можно оптимизировать, заменив глобальные
поиск с локальными переменными, определенными как значения по умолчанию.
Лично я имел смешанный успех с этим показом улучшений. то есть. Небольшие улучшения при определенных условиях, благодаря импорту модуля xxx, также демонстрирующему повышение производительности, не передавая его. Кроме того, иногда, если я передаю некоторые переменные, а не другие, я также вижу небольшие различия. Дело в том, что я чувствую, что вам нужно проверить себя, чтобы увидеть, работает ли это для вас.
def run_deque_imap_and_repeat_all_local(deque = collections.deque,
imap = itertools.imap, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
update = _md5.update
deque(imap(_md5.update, repeat("foo", 100000000)), maxlen = 0)
И, наконец, чтобы быть справедливым, давайте реализуем любую версию, например презентацию, которая также выполняет окончательную оптимизацию.
def run_any_like_presentation_all_local(any = any, deque = collections.deque,
imap = itertools.imap, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
any(imap(_md5.update, repeat("foo", 100000000)))
Хорошо, теперь давайте запустим несколько тестов (Python 2.7.2 OS X Snow Leopard 64-bit):
run_reference - 123,913 секунд
run_deque_imap_and_repeat_all_local - 51.201 секунд
run_deque_local_imap_and_repeat - 53,013 секунды
run_deque_imap_and_repeat - 48,913 секунд
run_any_like_presentation - 49,833 секунды
run_any_like_presentation_all_local - 47,780 секунд
И только для ударов в Python3 (Python 3.2 OS X Snow Leopard 64-bit):
run_reference - 94,273 секунды (вызовы функций 100000004!)
run_deque_imap_and_repeat_all_local - 23,929 секунды
run_deque_local_imap_and_repeat - 23,298 секунд
run_deque_imap_and_repeat - 24.201 секунд
run_any_like_presentation - 24,026 секунд
run_any_like_presentation_all_local - 25,316 секунд
Вот мой источник для тестов:
import itertools, hashlib, collections
_md5 = hashlib.md5()
def run_reference():
for _ in xrange(100000000):
_md5.update("foo")
def run_deque_imap_and_repeat_all_local(deque = collections.deque,
imap = itertools.imap, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
deque(imap(_md5.update, repeat("foo", 100000000)), maxlen = 0)
def run_deque_local_imap_and_repeat(deque = collections.deque,
imap = itertools.imap, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
deque(imap(_md5.update, repeat("foo", 100000000)), maxlen = 0)
def run_deque_imap_and_repeat():
collections.deque(itertools.imap(_md5.update, itertools.repeat("foo", 100000000)),
maxlen = 0)
def run_any_like_presentation():
any(itertools.imap(_md5.update, itertools.repeat("foo", 100000000)))
def run_any_like_presentation_all_local(any = any, deque = collections.deque,
imap = itertools.imap, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
any(imap(_md5.update, repeat("foo", 100000000)))
import cProfile
import pstats
def performance_test(a_func):
cProfile.run(a_func, 'stats')
p = pstats.Stats('stats')
p.sort_stats('time').print_stats(10)
performance_test('run_reference()')
performance_test('run_deque_imap_and_repeat_all_local()')
performance_test('run_deque_local_imap_and_repeat()')
performance_test('run_deque_imap_and_repeat()')
performance_test('run_any_like_presentation()')
performance_test('run_any_like_presentation_all_local()')
и Python3
import itertools, hashlib, collections
_md5 = hashlib.md5()
def run_reference(foo = "foo".encode('utf-8')):
for _ in range(100000000):
_md5.update(foo)
def run_deque_imap_and_repeat_all_local(deque = collections.deque,
imap = map, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
deque(imap(_md5.update, repeat("foo".encode('utf-8'), 100000000)), maxlen = 0)
def run_deque_local_imap_and_repeat(deque = collections.deque,
imap = map, _md5 = _md5, repeat = itertools.repeat,
md5 = hashlib.md5):
deque(imap(_md5.update, repeat("foo".encode('utf-8'), 100000000)), maxlen = 0)
def run_deque_imap_and_repeat():
collections.deque(map(_md5.update, itertools.repeat("foo".encode('utf-8'), 100000000)),
maxlen = 0)
def run_any_like_presentation():
any(map(_md5.update, itertools.repeat("foo".encode('utf-8'), 100000000)))
def run_any_like_presentation_all_local(any = any, deque = collections.deque,
imap = map, _md5 = _md5, repeat = itertools.repeat):
any(imap(_md5.update, repeat("foo".encode('utf-8'), 100000000)))
import cProfile
import pstats
def performance_test(a_func):
cProfile.run(a_func, 'stats')
p = pstats.Stats('stats')
p.sort_stats('time').print_stats(10)
performance_test('run_reference()')
performance_test('run_deque_imap_and_repeat_all_local()')
performance_test('run_deque_local_imap_and_repeat()')
performance_test('run_deque_imap_and_repeat()')
performance_test('run_any_like_presentation()')
performance_test('run_any_like_presentation_all_local()')
Другое дело, не делайте этого в реальном проекте, если нет узкого места в производительности, подлежащего сертификации.
И, наконец, если нам действительно нужна морковь (кроме написания кода, который имеет смысл, и не подвержен ошибкам) в те трудные времена, когда любой из них фактически выполняет deque, ваш более разумный код будет лучшеВы можете воспользоваться преимуществами улучшений в более новых версиях Python без необходимости изменения вашей кодовой базы.
http://www.python.org/doc/essays/list2str/ - хорошее чтение о том, как подходить к оптимизации Python.(т. е. в идеале написание C-расширения - это НЕ первое, чего вы достигнете).
Я также хотел бы указать на ответ Гарета, поскольку он может понять, почему любой может выполнить deque.