Я читал этот Почта, а потом наткнулся на код, который был:
jokes=range(1000000)
domain=[(0,(len(jokes)*2)-i-1) for i in range(0,len(jokes)*2)]
Я подумал, не лучше ли вычислить значение len (шутки), когда оно выходит за рамки понимания списка?
Ну, я попробовал и рассчитал три кода
jv@Pioneer:~$ python -m timeit -s 'jokes=range(1000000);domain=[(0,(len(jokes)*2)-i-1) for i in range(0,len(jokes)*2)]'
10000000 loops, best of 3: 0.0352 usec per loop
jv@Pioneer:~$ python -m timeit -s 'jokes=range(1000000);l=len(jokes);domain=[(0,(l*2)-i-1) for i in range(0,l*2)]'
10000000 loops, best of 3: 0.0343 usec per loop
jv@Pioneer:~$ python -m timeit -s 'jokes=range(1000000);l=len(jokes)*2;domain=[(0,l-i-1) for i in range(0,l)]'
10000000 loops, best of 3: 0.0333 usec per loop
Наблюдая за предельной разницей 2,55% между первым и вторым, я подумал - это первое понимание списка
domain=[(0,(len(jokes)*2)-i-1) for i in range(0,len(jokes)*2)]
оптимизирован внутри Python? или 2,55% - это достаточно большая оптимизация (учитывая, что len (шутки) = 1000000)?
Если это так - каковы другие неявные / внутренние оптимизации в Python?
Что такое developer's rules of thumb for optimization in Python?
Редактировать1: Поскольку большинство ответов - «не оптимизируйте, сделайте это позже, если будет медленно», и я получил несколько советов и ссылок от Triptych и Ali A для делать.
Немного изменю вопрос и прошу не.
Можем ли мы получить некоторый опыт от людей, которые столкнулись с проблемой «медлительность», в чем заключалась проблема и как ее исправить?
Edit2: Для тех, у кого его нет, вот интересно читать
Edit3: Неправильное использование timeit, о котором идет речь, пожалуйста, смотрите ответ dF's для правильного использования и, следовательно, времени для трех кодов.






С другой стороны, объединение генераторов в цепочку намного эффективнее, чем объединение в цепочку составных частей списков, и часто более интуитивно понятно.
Что касается практических правил разработчика для оптимизации в Python, они такие же, как и для всех языков.
len для списков - O (1). Нет необходимости сканировать весь список, чтобы найти длину, потому что размер списка сохраняется для поиска. Но, видимо, все же немного быстрее извлечь его в локальную переменную.
Однако, отвечая на ваш вопрос, я бы никогда не стал заботиться о вариациях производительности порядка 5%, если бы я не делал сумасшедшие жесткие оптимизации внутреннего цикла в какой-то симуляции или что-то в этом роде. И в этом случае вы можете значительно ускорить это, вообще не используя диапазон.
Прочтите это: Советы по скорости / производительности Python
Кроме того, в вашем примере общее время настолько мало, что погрешность перевешивает любую фактическую разницу в скорости.
Вы неправильно используете timeit: аргумент -s (setup) - это оператор, который должен быть выполнен один раз изначально, поэтому на самом деле вы просто тестируете пустой оператор. Ты хочешь сделать
$ python -m timeit -s "jokes=range(1000000)" "domain=[(0,(len(jokes)*2)-i-1) for i in range(0, len(jokes)*2)]"
10 loops, best of 3: 1.08 sec per loop
$ python -m timeit -s "jokes=range(1000000)" "l=len(jokes);domain=[(0,(l*2)-i-1) for i in range(0, l*2)]"
10 loops, best of 3: 908 msec per loop
$ python -m timeit -s "jokes=range(1000000)" "l=len(jokes*2);domain=[(0,l-i-1) for i in range(0, l)]"
10 loops, best of 3: 813 msec per loop
Хотя ускорение по-прежнему невелико, оно более значительное (16% и 25% соответственно). Так как это не усложняет код, эта простая оптимизация, вероятно, того стоит.
Чтобы ответить на фактический вопрос ... обычное эмпирическое правило в Python заключается в том, чтобы
При кодировании отдайте предпочтение простому и удобочитаемому коду, а не оптимизации.
Профилируйте свой код (profile / cProfile и pstats - ваши друзья), чтобы выяснить, что вам нужно оптимизировать (обычно такие вещи, как жесткие циклы).
В крайнем случае, повторно реализуйте их как расширения C, что значительно упрощается с помощью таких инструментов, как пирекс и Cython.
Одна вещь, на которую следует обратить внимание: по сравнению со многими другими языками, вызовы функций в Python относительно дороги, поэтому оптимизация в вашем примере имела значение, хотя len - это O (1) для списков.
@dF: Просмотрите edit2 вопроса, в котором говорится о дорогостоящих вызовах функций и прочем.
-1 НЕУДАЧА Ваш 3-й прогон выполняет l=len(jokes*2) (создаёт список вдвое больше, получает его длину, отбрасывает его) вместо того, что было у OP: l=len(jokes)*2. Также не пробовал xrange вместо range. [(0, j) for j in xrange(2*len(jokes)-1,-1,-1)] тоже не пробовал
Это относится ко всему программированию, а не только к Python:
И я бы даже добавил, что не стоит беспокоиться об этом, если только у вас нет проблемы с медлительностью, которая причиняет вам боль.
И, возможно, наиболее важно то, что модульные тесты помогут вам на самом деле.
шаг -1: выяснить правильный алгоритм. шаг 0: напишите чистый код
Самое важное, что нужно сделать, - это написать идиоматичный, понятный и красивый код Python. Многие общие задачи уже находятся в stdlib, поэтому вам не нужно переписывать более медленную версию. (Я имею в виду именно строковые методы и инструменты itertools.) Также широко используйте встроенные контейнеры Python. dict, например, "оптимизировал сопли", и сказано, что код Python, использующий dicts, будет быстрее, чем простой C!
Если это еще не так, есть несколько приемов, которые вы можете использовать, но это также сигнал о том, что вам, вероятно, следует переложить некоторую работу на модули расширения C.
Что касается понимания списков: CPython может сделать несколько оптимизаций по сравнению с обычными циклами аккумуляторов. А именно, код операции LIST_APPEND выполняет добавление к собственной операции списка.
+1 за упоминание stdlib. Я до сих пор помню, как сам написал groupby, а затем нашел itertools.groupby. Но мне интересно, что "код Python, использующий dicts, будет быстрее, чем простой C!"
@JV Я уверен, что это будет быстрее, чем любое домашнее решение на C.
Can we have some experiences from people who faced the 'slowness', what was the problem and how it was corrected?
Проблема заключалась в медленном извлечении данных в приложении с графическим интерфейсом. Я получил 50-кратное ускорение, добавив указатель к таблице, и был повсеместно провозглашен героем и спасителем.
я просил не о медлительности Python, но все же герой остается героем. :) ...просто шучу'.
У меня есть программа, которая анализирует файлы журналов и создает хранилище данных. Типичный запуск включает около 200 миллионов строк файла журнала и выполняется большую часть дня. Стоит оптимизировать!
Поскольку это синтаксический анализатор, и при этом он анализирует довольно изменчивый, своеобразный и ненадежный текст, существует около 100 регулярных выражений, заранее тщательно подготовленных re.compiled () и применяемых к каждой из 200 миллионов строк файла журнала. Я был почти уверен, что они были моим узким местом, и размышлял, как исправить эту ситуацию. У меня были идеи: с одной стороны, делать меньше и красивее RE; с другой - больше и проще; вроде того.
Я профилировал с помощью CProfile и смотрел на результат в "runnake".
Обработка RE занимала всего около 10% времени выполнения кода. Это не то!
Фактически, большая квадратная капля на дисплее Runnake мгновенно сообщила мне, что около 60% моего времени было потрачено на одно из тех печально известных «однострочных изменений», которые я добавил однажды, исключив непечатаемые символы (которые иногда появляются но всегда представляю что-то настолько фальшивое, что меня это действительно не волнует). Это сбивало с толку мой синтаксический анализ и выдачу исключений, о которых я беспокоюсь, потому что это остановило мой день анализа файла журнала.
line = ''.join([c for c in line if curses.ascii.isprint(c) ])
Вот и все: эта строка касается каждого байт каждой из этих 200 миллионов строк (а длина строк составляет в среднем пару сотен байтов). Неудивительно, что это 60% моего времени выполнения!
Теперь я знаю, что есть более эффективные способы справиться с этим, например str.translate (). Но такие строки редки, и я все равно не забочусь о них, и в конечном итоге они вызывают исключение: теперь я просто ловлю исключение в нужном месте и пропускаю строку. Вуаля! программа работает примерно в 3 раза быстрее, мгновенно!
Итак, профилирование
прочтите мой комментарий к ответу, который вы приняли