Что происходит? Может кто-нибудь объяснить мне, что здесь происходит, я изменился в узкой петле:
## j=i
## while j < ls - 1 and len(wordlist[j]) > lc: j+=1
j = next(j for j in range(i,ls) if len(wordlist[j]) <= lc)
Прокомментированная while версия запускала всю программу: 625 мс, следующая версия генератора выполняла всю программу во время 2.125 с.
Что может быть причиной того, что эта более питоническая версия вызывает такую катастрофу в производительности?
EDIT: Может быть, это вызвано использованием модуля psyco? Разумеется, по крайней мере, время работы с Python 2.7, которое не было psyco, было 2.141 для следующей версии, означает почти то же самое, что и Python 2.6 с psyco.
После удаления файлов *.pyc я не получил код, чтобы замедлить работу. Затем, когда я удалил импорт psyco из модуля библиотеки, я также получил 2,6 такта и для использования без psyco, результаты для версии без psyco и версии psyco (так как теперь также замедляется и процедура библиотеки), и это также актуально:)
не psyco:
- пока: подготовка в библиотеке: 532 мс, общее время работы 2.625 с
- next: подготовка в библиотеке: 532 мс, общее время работы (time.clock()): 2.844 с (версия с xrange тем же временем стены)
Психо:
- пока: подготовка в библиотеке: 297 мс, общее время работы: 609..675 мс
- next: подготовка в библиотеке: 297 мс, общее время работы: 1.922 с (версия с диапазоном вместо xrange везде в программе: 1.985 с)
Работа в WindowsXP AMD Sempron 3100+ с 2 ГБ оперативной памяти. Подсчет циклов и вызовов с двумя глобалями:
j=i
callcount += 1
while j < ls - 1 and len(wordlist[j]) > lc:
j+=1
loopcount += 1
Результат для тестового ввода с помощью psyco:
Finished in 625 ms
Loopcount: 78317
Callcount: 47970
Ration: 1.633
Итак, цикл находится внутри жесткого цикла, но в среднем выполняется всего пару раз (обратите внимание, что два приращения глобальных счетчиков не замедляли код в psyco)
Выводы: Несмотря на очень чувствительный характер алгоритма относительно длины словаря, который заставлял меня передавать некоторые замечательные слова из рассмотрения этим циклом, позже основные случаи рекурсии проверяются поиском по словарю, который является O (n), поэтому очень выгодная более ранняя оптимизация становится не очень полезной, даже при более длительном вводе и перемещении счетчика вызовов в начале функции, показало, что количество вызовов не зависит от длины словаря, но количество внешних циклов слабо снижено ( код, первоначально опубликованный, находится в элементе if if).
Более длительное время выполнения (29 372 решения) с циклом while и удалением всего цикла (с использованием я вместо j) (подготовка библиотеки 312 мс):
- Без цикла: количество ветвей elif: 485488, outerloopcount: 10129147, отношение: 0,048, время работы 6000 с (без счетчиков: 4,594 с)
- С петлей: loopcount: 19355114, outercount: 8194033, соотношение: 0,236, время выполнения 5,704 с (без счетчиков: 4,688 с)
(время работы без цикла, счетчиков и psyco: 32 792 с, библиотека 608 мс)
Таким образом, без дополнительных счетчиков преимущество этого цикла, использующего psyco, в более тяжелом случае: (4688-4594) * 100/4688.0% = 2%
Это вдохновило меня на отменить еще одну более раннюю оптимизацию, о чем я задавался в DaniWeb. Раньше версия кода выполнялась быстрее, когда наименьший размер слова был глобальным, а не параметрирующим. Согласно документации, вызовы с локальной переменной быстрее, но при этом затраты на рекурсию более тяжелые. Теперь в более трудном случае эта другая реверсия оптимизации привела к более ожидаемому результату работы в случае без оптимизации длины слова: время работы с psyco составило 312 мс, 4 469..4 484 с общее время работы. Таким образом, это сделало очиститель кода и принесло больше пользы в этом случае, поскольку удаленный цикл имел. И добавление параметра в версию с циклом while не сильно изменило время выполнения (изменение стало больше для кода подготовки библиотеки)
**What I learned from this: If you do n optimizations for speed
you must check the first n-1 optimizations after doing nth one**