Оптимизация кода: Оптимизации в компиляторах. Часть 1 / Хабр
Оптимизация программного кода | Techrocks
Зачем же нужна оптимизация и откуда она взялась? С первых дней развития эры вычислительной техники возник вопрос экономии места и увеличения производительности программ. Программистам приходилось создавать сложные дееспособные программы, которые смогли бы работать при очень низком быстродействии процессоров, использовать считанные килобайты оперативной памяти и места на диске. Поэтому все разработчики ПО были заинтересованы в максимальном быстродействии при минимальном размере кода.
Сегодня эти мощности вызывают улыбку. Но традиции оптимизации кода сохранились. Как известно, сколько ни наращивай размер диска и объем ОЗУ, все равно будет мало. Потому написанные «неряшливо» приложения, медленные и ресурсоемкие, проигрывают конкурентную борьбу аналогам, даже если они красивы и удобны.
Особо жесткие требования касаются драйверов и системных утилит. Они должны работать быстро, корректно и максимально экономить ресурсы компьютера. То есть взаимодействие процессора с периферией должно происходить без лишних затрат времени, с высокой скоростью передачи данных между устройствами. И сейчас мы решили немного разобраться, какие бывают способы оптимизировать программный код, в чем их плюсы и минусы.
Основные принципы оптимизации
Оптимизация стоит на трех «китах» — естественность, производительность, затраченное время. Давайте разберемся подробнее, что они означают.
- Естественность. Код должен быть аккуратным, модульным и легко читабельным. Каждый модуль должен естественно встраиваться в программу. Код должен легко поддаваться редактированию, интегрированию или удалению отдельных функций или возможности без необходимости вносить серьезные изменения в другие части программы.
- Производительность. В результате оптимизации вы должны получить прирост производительности программного продукта. Как правило, удачно оптимизированная программа увеличивает быстродействие минимум на 20-30% в сравнение с исходным вариантом.
- Время. Оптимизация и последующая отладка должны занимать небольшой период времени. Оптимальными считаются сроки, не превышающие 10 – 15 % времени, затраченного на написание самого программного продукта. Иначе это будет нерентабельно.
Полезный совет. Перед началом оптимизации программного кода не забудьте сохранить копию исходного кода. Тогда в случае ошибки при внесении изменений всегда можно будет откатится до рабочей версии.
Стоит ли применять Ассемблер
Многие разработчики искренне считают, что критические секции (некоторые называют их «узкими» местами программы) кода удобнее писать на ассемблере, так как он обеспечивает самый быстрый доступ к данным и устройствам.
Многочисленные сравнения результатов оптимизации кода на языке высокого уровня и применения ассемблерных вставок показывают, что в первом случае после компиляции программа будет работать чуть медленнее, чем при использовании ассемблера. Обычно эти цифры измеряются от 2% до 7%. Максимум – разница составляет 20%. Стоит ли ради получения столь малого эффекта тратить время и силы на написание ассемблерной версии? На наш взгляд лучше уделить больше внимания работе с кодом, который написан на ЯП высокого уровня, и оптимизировать работу алгоритма.
Как правильно оптимизировать
Теперь давайте разберемся, как проводится оптимизация, и разберемся, с чего начинать, чего лучше избегать, и когда без ассемблера не обойтись.
Начало оптимизации
Первое, что необходимо сделать, это выявить «узкие места» программы. Нет смысла трогать тот кусок программы, где и без вас все работает прекрасно. Здесь вы вряд ли что-то выиграете при оптимизации. В первую очередь, стоит обратить внимание на блоки кода, которые регулярно или часто повторяются в процессе работы – циклы и подпрограммы.
Пример: Если оптимизировать работу цикла хотя бы на 2% за одну итерацию, а число его повторов будет 1000 раз, в итоге мы получаем: 2% × 1000 = 2000%, вполне ощутимый результат при работе кода.
Участки кода, которые не оптимизируются
Не стоит трогать единичные операнды, поскольку работают они крайне редко и толку в их модификации нет никакого. Они отработают один раз, и больше к этому коду обращений не будет. Но при условии, что при внесении изменений вы добьетесь увеличения производительности более чем на 10%, это не лишено смысла. Здесь все зависит от вашего здравого смысла и опыта.
Также вы не сумеете добиться достойных результатов в случае обращения к внешним устройствам и другим программным системам. До и после таких фрагментов можно что-то ускорить. Но там, где задержка может возникать по причине взаимодействия с внешними данными, лучше предусмотрите заглушку типа «Подождите, операция может занять несколько минут».
Еще раз об ассемблере
Помните, что использовать ЯП низкого уровня нужно только там, где это действительно необходимо. Нет никакой причины вставлять его где ни попадя, это никак не повлияет на производительность. Впрочем, если вы – асс ассемблера и можете писать на нем также быстро, как и на удобном языке высокого уровня, можете пользоваться им активно. Правда, тогда возникает другой нюанс – вы усложняете читабельность кода для программистов, которые будут заниматься проектом после вас.
Оптимизировать или нет?
Если вы не уверены, что сможете ускорить работу программы, при этом тестирование не выявляет никаких критичных проблем, оставьте все как есть. Помните старую мудрость программистов: работает – не мешай.
Иначе вы можете потратить лишнее время на работу с кодом, а в результате сделаете даже хуже, программа начнет работать медленно, да и от багов никто не застрахован.
Заниматься оптимизацией следует только тогда, когда на программу поступают жалобы пользователей либо на этапе тестирования находятся проблемные участки, на которых программа «подвисает» и тормозит работу устройства. В таких случаях производится отладка, а для уже выпущенных в серию продуктов выпускают новые версии или, так называемые, «заплатки» (patch).
Методы оптимизации программ
Оптимизация кода не слишком отличается от обычного исправления багов. Более того, с их устранения и начинается работа по оптимизации программы.
Первым делом нужно проверить код на наличие устаревших или вообще ненужных фрагментов. Таких исполняемых модулей или веток в большой программе находится обычно много. Что-то написали, но оказалось, что функционал не нужен, и его просто забыли удалить. Другие части оказались не нужны в результате очередного обновления. Все они занимают место. А некоторые продолжают исполняться, хоть в этом нет никакого смысла. И, таким образом, замедляют работу системы.
Пишите аккуратный код. Не забывайте о комментариях. Так вы поможете и себе, и другим разработчикам, понять, что в программе нужно, а что – уже не актуально. Эти общие советы помогают и при отладке, и при поиске багов. В общем, не будьте неряшливым «говнокодером», и ваши программы будут работать быстро и без проблем.
Второй этап поиска проблемных мест также простой. Разберитесь, когда приложение работает медленнее всего, в какие моменты оно заметно подвисает. Изучите код на предмет ошибок или излишне сложных запутанных решений. Попробуйте написать проще.
Если все равно что-то не работает или «тормозит», придется использовать профилировщики отладочного вывода, в том числе, с учетом записи логов всех запросов SQL, если программа работает с базами данных. В случае поиска вслепую вы потратите много времени и не факт, что сможете добиться положительных результатов.
Рассмотрим самые популярные методы оптимизации программ. Некоторые из них возможно вызовут у вас недоумение, но поверьте, они работают.
Настройка окружения
Используемая вами SQL или другая система управления базами данных могут быть неверно настроены. Настройки по умолчанию далеко не идеальны. Возможно, какие-то дополнительные проверки как раз и замедляют процесс.
Иногда удается заметно ускорить работу программы, изменив ключевые настройки виртуальной машины Java. Кстати, это поможет сделать быстрее работу не только тестируемого приложения, но и всей системы.
Также обратите внимание на саму операционную систему и мощность «железа». Может быть они вообще не предназначены для работы программного продукта, который вы пытаетесь запустить и ускорить? А, может, устарели и потому «тормозят»?
Все это не относится напрямую к оптимизации программы, но проверить нужно до начала работы с кодом. Просто потому, что такие «досадные мелочи» нередко оказываются ключевой проблемой, а код – вообще не причем. И не стоит снисходительно улыбаться. Проверять окружение забывают даже опытные разработчики.
Избавляемся от ненужного функционала
Для увеличения скорости работы приложения можно использовать подход сокращения ненужного кода. Часто бывает так, что программа автоматически решает маловажные или уже не актуальные задачи. Например, заказчик, описывая задачу программисту, попросил о каких-то возможностях, а потом передумал. Или вышел новый релиз программы, где часть функций выделили в отдельный модуль, а старый код просто забыли удалить.
В итоге мы имеем лишний функционал, который будет «тормозить» быстродействие. Со временем такой код обрастет совершенно ненужными «костылями» и «подпорками», что не лучшим образом влияет на производительность. В таком случае мы рекомендуем просто переписать модуль «с нуля», выбросив все старое, как ненужных хлам.
Мемоизация
Меморизация (от англ. Memoization) означает запоминание. Фактически это простое сохранение результата выполнения определенной функции, которое поможет избежать ее повторного выполнения. Применяя меморизацию, вы сможете повысить производительность программы.
Работает это очень просто. Перед тем, как функция будет выполняться, проверяется условие – исполнялась ли она ранее. По итогам можно получить два варианта:
- функция вызвана в первый раз, тогда она выполняется, а результат сохраняется;
- модуль уже работал, можно использовать сохраненный результат.
Иногда говорят о табулировании, это синоним мемоизации, который используется во многих языках программирования.
Кеширование
Это метод временного хранения данных в памяти устройства пользователя. Получить доступ к такой информации можно гораздо быстрее, чем каждый раз обращаться к серверу или базам данных. С помощью кэширования значительно ускоряют работу с сайтами, онлайн-системами и т.д.
Вся необходимая информация в данном случае храниться на носителях с быстрым доступом. Это может быть выделенная часть диска или оперативная память. Программа в процессе работы использует кэш по мере необходимости, и обращается к основному хранилищу данных только если не находит их в кэше.
Распараллеливание программ
Это способ адаптации алгоритмов, которые были реализованы, как программы для компьютерных систем с параллельной архитектурой. Как правило, это относится к многопроцессорным системам.
Подробно описывать метод мы здесь не будем, так как это займет слишком много места. Кратко можно сказать так: разные вычисления одной программы выполняются одновременно в параллельных потоках. Такой подход позволяют далеко не все языки, а потому тут нередко используют внешние команды системы или ассемблер.
«Ленивые» вычисления
Ленивые (Lazy evaluation) или отложенные вычисления – стратегия, которую применяют в некоторых системах счисления. Суть метода заключается в том, что все расчеты откладываются до тех пор, пока не будет затребован их результат.
Такая стратегия позволит существенно снизить общий объем производимых вычислений, так как ненужные операции попросту не будут выполняться. Чтобы метод начал работать, нужно описать зависимости функций (операндов) друг от друга, что поможет отслеживать работу. В итоге вы получите код, который будет выполняться только в том случае, когда он действительно нужен.
Метод приближения
Приближение или аппроксимаация (от лат. proxima — ближайшая или приближе́ние) – метод замены строгого алгоритма на наиболее подходящие приближенные значения, что влечет за собой определенную потерю точности вычислений. Снижение точности экономит память и повышает скорость. Для того чтобы не использовать длинную арифметику, можно воспользоваться обычными float’ами. Но пользоваться таким методом нужно крайне осторожно, не всегда снижение точности допустимо.
Использование сторонних языков
Иногда написанная программа может медленно работать из-за того, что много времени занимает проверка описанных типов, что занимает дополнительное время. Чтобы избежать этого эффекта, можно применять фрагменты кода или модули, написанные на других языках. Но делать это нужно крайне осторожно. Все эти «лишние» проверки защищают вас от багов и «дыр» в безопасности, связанных, в том числе, с буферизацией. Потому хорошо подумайте, действительно ли экономия времени столь существенна? И если придете к выводу, что здесь это – лучшее решение, обязательно проведите особо внимательное тестирование.
Кроме того, если начать использовать в коде фрагменты других языков, это может вызвать эффект «зоопарка», что сильно снижает читабельность программы. Также имейте в виду, что метод может попросту не сработать или даже критически навредить всей программе.
Существует еще очень много методов оптимизации, как общеизвестных, так и личных находок программистов. Мы постарались рассказать, что такое оптимизация, и познакомить вас с самыми популярными методами. А как вы ускоряете работу своих приложений? Делитесь в комментариях.
10 слов об оптимизации кода — «Хакер»
Немного о грустном: вся наша жизнь – это борьба с тормозами. И вечно так продолжаться не может. Необходимо оптимизировать всё – начиная от своего рабочего места до времени. В этой статье я привёл примеры оптимизации кода на языке программирования Delphi, но поверь, эти советы могут пригодиться тебе и в реальной жизни, если подумать.
1. Оптимизировать можно практически всё. И даже там, где тебе кажется, что всё работает быстро, можно сделать ещё быстрее. Необходимо помнить, что любую задачу можно решить несколькими путями, и твоя задача выбрать из них наиболее рациональный.
2. Оптимизацию всегда надо начинать со слабых мест в коде программы. Обычно оптимизировать то, что и так быстро работает, необходимости не возникает. Да и эффект такой оптимизации будет минимален.
3. При оптимизации нужно разбирать все операции, каждый оператор, ничего не пропуская. Обычно оптимизацию начинают с тех мест в коде, где находятся регулярно повторяющиеся операции, циклы. То, что находится внутри цикла, будет повторена n количество раз, поэтому, чем меньше кода находится в цикле, тем быстрее процессор просчитает его. Если цикл получается слишком большой, его можно разложить на несколько более маленьких. В данном случае размер нашей программы повыситься, зато скорость увеличиться.
4. Старайтесь поменьше использовать вычисления с плавающей запятой. Любые операции с целыми числами выполняются на порядок быстрее. Операции умножения или деления также выполняются достаточно долго. Вместо умножения лучше использовать сложение, а деление можно заменить сдвигом. Сдвиг работает намного быстрее и умножения, и деления. Это связано с тем, что все числа хранятся в двоичной системе. Если перевести число из десятичной системы счисления в двоичную и сдвинуть число вправо на одну позицию, то можно заметить, что данная операция аналогична делению на 2. При сдвиге влево происходит деление числа на 2. Хоть эти операции и аналогичны, но сдвиг работает в несколько раз быстрее.
5. При создании процедур не надо обременять их большим количеством входных параметров. А всё потому, что при каждом вызове процедуры её параметры подымаются в специальную область памяти, стек, а после выхода изымаются оттуда. Также необходимо действовать аккуратно и с самими параметрами. Не надо пересылать процедурам переменные, содержащие данные большого объёма в чистом виде. Лучше передать адрес ячейки памяти, где хранятся данные, а внутри процедуры уже работать с этим адресом.
6. В самых критических моментах работы программы, например вывод на экран, можно воспользоваться языком Assembler. Даже встроенный в Delphi ассемблер намного быстрее родных функций языка. Код ассемблера можно вынести в отдельный модуль, откомпилировать и подключить к своей программе.
7. Лишних проверок не бывает. Не надо думать, что если у вас не возникла какая-то нестандартная ситуация, то она не возникнет и у пользователя. Всегда делайте проверку того, что вводит пользователь, не дожидаясь, когда эти данные понадобятся.
8. Если ты пишешь достаточно большую и громоздкую программу, добавляй в неё комментарии. Компилятор их всё равно игнорирует. И если вдруг тебе захочется продать исходные коды своих программ, комментарии повысят им цену, да и самому будет легче в них ориентироваться.
9. Для достижения хорошего эффекта ты должен знать IDE, интегрированную среду разработчика, языка, на котором ты программируешь, в нашем случае Delphi. Обычно в опциях IDE разрешается выбирать различные типы компиляторов, а по умолчанию стоит самый простой, быстро компилирующий, но создающий менее оптимизированный код. Поэтому всегда ставь самый оптимизирующий вид компилятора.
10. Старайся делать в программах стандартный интерфейс. Ну не надо делать треугольные кнопочки, нестандартные меню и прочие графические навороты. Всё это очень сильно тормозит программу, расходует большое количество ресурсов компьютера и требует дополнительного времени на разработку. К примеру, настоящий UNIX – это вообще обычный shell – строка для ввода команд.
Вот вроде и всё. Желаю, удачи в написании своих программ, просто следуй этим советам, и всё у тебя получиться.
Продвижение сайта, оптимизация кода / Хабр
Продвижение сайта в сети интернет, неотъемлемое мероприятие после создания веб сайта. Эти мероприятия производятся с целью привлечения посетителей на Ваш ресурс. Существует много методов продвижения сайта. В этой статье будет описано как правильно оптимизировать внутренние страницы сайта для правильного и успешного продвижения Вашего ресурса в сети интернет.
В предыдущей статье о влиянии скорости загрузки сайта, я описывал как скорость загрузки сайта влияет на ранжирование страницы поисковыми системами. В целях успешного продвижения, важно максимально уменьшить вес страниц сайта. то есть уменьшение объема кода страницы, как главной страницы сайта, так и внутренних.
Оптимизация кода
Все поисковые системы первым делом обращают внимание на количество контента на странице и количеству кода. То есть соотношение размера контента (статьи) к размеру кода всей страницы.
Конкретного соотношения не существует, может и существует но его никто не знает, но известно только одно чем меньше кода на странице, а больше уникального текста на странице тем выше вероятность что ваша страница будет Выше в выдаче поисковой системы, чем страницы вашего конкурента.
Одним из факторов ранжирования поисковой системы Google, является скорость загрузки страниц сайта, чем скорость загрузки сайта или страницы больше, тем лучше. Вероятность попадания в топ увеличивается.
Уменьшив вес страницы, убрав лишние скрипты, уберете лишние html сроки на странице, вы сделаете первый шаг к успешному продвижению.
Мета – теги страницы сайта
После оптимизации кода страницы, добавления на неё уникального контента, нужно правильно указать мета-теги для каждой страницы сайта. Эти теги должны быть разными. Эти теги должны обязательно присутствовать на странице. С помощью этих тегов поисковые системы находят нужные страницы и определяют какую страницы выдать после поискового запроса.
Основные мета – теги
- Title – заголовок страницы;
- Description – описание страницы;
- Keywords – ключевые слова для страницы.
Правильно указав мета-теги вы сообщите поисковику информацию о вашем сайта, или о конкретно странице вашего ресурса. Это очень важный момент в SEO оптимизации и продвижении сайта в поисковых системах.
Заголовки h2-h6
Теги h2-h6 определяют важность текста, который располагается после заголовка. С помощью них можно выделить отдельные участки текста. Самым важным заголовком является заголовок h2, он считается заголовком первого уровня, соответственно и приоритеты у нег самые большие.
Им выделяют заголовок статьи на странице. Остальные теги распределяются по порядку и имеют более низкие приоритеты. Основными тегами как правило являются теги h2, h3, h4.
В заключении можно сделать выводы, что для продвижения сайта нужно выполнить несколько простых мероприятий.
Продвижения сайта. Оптимизация кода:
- Уменьшение объема кода
- Добавление мета-тегов к странице
- Написание заголовков h2-H6
- Написание атрибутов title и alt к изображениям
- Перелинковка страниц сайта (перелинковка)
- Проверка кода на валидацию
Ускорение кода на Python средствами самого языка / Хабр
Каким бы хорошим не был Python, есть у него проблема известная все разработчикам — скорость. На эту тему было написано множество статей, в том числе и на Хабре.
Чаще всего, предлагают следующие решения:
- Использовать Psyco
- Переписать часть программы на С используя Python C Extensions
- Сменить
мозгиалгоритм
Безусловно, решения верные. Но у каждого из них есть свои недостатки.
Psyco — прекрасный модуль, достигающий ускорения кода в сотни процентов, но: поддерживаются лишь 32-битный Python версий не выше 2.6, большое потребление памяти и тот факт, что в последнее время разработка psyco сбавила темпы, последнее обновление на сайте датировано 16.07.2010. Возможно, с выходом Psyco v2 ситуация изменится, но пока что, этот модуль применим не всегда.
Python C Extensions (рекомендую отличную статью rushman Пишем модуль расширения для Питона на C) — создатели Python’a сделали всем разработчикам, использующим этот язык, неоценимый подарок — Python/С API, дающий возможность относитенльно прозрачной интеграции Си-шного кода в программы на Python’e. Недостатков у этого решения только два:
- «Порог вхождения» у C и Python/C API все же выше, чем у «голого» Python’a, что отсекает эту возможность для разработчиков, не знакомых с C
- Одной из ключевых особенностей Python является скорость разработки. Написание части программы на Си снижает ее, пропорционально части переписанного в Си кода к всей программе
Так что, данный метод тоже подойдет не всем.
Смена алгоритма же возможна не всегда, часты ситуации, что и самый быстрый алгоритм выдает разочаровывающие результаты по скорости.
Так что же делать?
Тогда, если для вашего проекта выше перечисленные методы не подошли, что делать? Менять Python на другой язык? Нет, сдаваться нельзя. Будем оптимизировать сам код. Примеры будут взяты из программы, строящей множество Мандельброта заданного размера с заданным числом итераций.
Время работы исходной версии при параметрах 600*600 пикселей, 100 итераций составляло 3.07 сек, эту величину мы возьмем за 100%
Скажу заранее, часть оптимизаций приведет к тому, что код станет менее pythonic, да простят меня адепты python-way.
Шаг 0. Вынос основного кода программы в отдельную
Данный шаг помогает интерпретатору python лучше проводить внутренние оптимизации про запуске, да и при использовании psyco данный шаг может сильно помочь, т.к. psyco оптимизирует лишь функции, не затрагивая основное тело программы.
Если раньше рассчетная часть исходной программы выглядела так:
for Y in xrange(height):
for X in xrange(width):
#проверка вхождения точки (X,Y) в множество Мандельброта, itt итераций
То, изменив её на:
def mandelbrot(height, itt, width):
for Y in xrange(height):
for X in xrange(width):
#проверка вхождения точки (X,Y) в множество Мандельброта, itt итераций
mandelbrot(height, itt, width)
мы получили время 2.4 сек, т.е. 78% от исходного.
Шаг 1. Профилирование
Стандартная библиотека Python’a, это просто клондайк полезнейших модулей. Сейчас нас интересует модуль cProfile, благодаря которому, профилирование кода становится простым и, даже, интересным занятием.
Полную документацию по этому модулю можно найти здесь, нам же хватит пары простых команд.
python -m cProfile sample.py
Ключ интерпетатора -m позволяет запускать модули как отдельные программы, если сам модуль предоставляет такую возможность.
Результатом этой команды будет получение «профиля» программы — таблицы, вида 4613944 function calls (4613943 primitive calls) in 2.818 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 2.309 2.309 2.766 2.766 mand_slow.py:22(mandelbrot)
…
С её помощью, легко определить места, требующие оптимизации (строки с наибольшими значениями ncalls (кол-во вызовов функции), tottime и percall (время работы всех вызовов данной функции и каждого отдельного соответственно)).
Для удобства можно добавить ключ -s time
, отсортировав вывод профилировщика по времени выполнения.
В моем случае интересной частью вывода было (время выполнение отличается от указанного выше, т.к. профилировщик добавляет свой «оверхед»): 4613944 function calls (4613943 primitive calls) in 2.818 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 2.309 2.309 2.766 2.766 mand_slow.py:22(mandelbrot)
3533224 0.296 0.000 0.296 0.000 {abs}
360000 0.081 0.000 0.081 0.000 {math.atan2}
360000 0.044 0.000 0.044 0.000 {math.cos}
360000 0.036 0.000 0.036 0.000 {math.sqrt}
…
Итак, профиль получен, теперь займемся оптимизацией вплотную.
Шаг 2. Анализ профиля
Видим, что на первом месте по времени стоит наша основная функция mandelbrot, за ней идет системная функция abs, за ней несколько функций из модуля math, далее — одиночные вызовы функций, с минимальными временными затратами, они нам не интересны.
Итак, системные функции, «вылизаные» сообществом, нам врядли удастся улучшить, так что перейдем к нашему собственному коду:
Шаг 3. Математика
Сейчас, код выглядит так:
pix = img.load() #загрузим массив пикселей
def mandelbrot(height, itt, width):
step_x = (2 - width / 1.29) / (width / 2.6) - (1 - width / 1.29) / (width / 2.6) #шаг по оси х
for Y in xrange(height):
y = (Y - height / 2) / (width / 2.6) #для Y рассчет шага не так критичен как для Х, его отсутствие положительно повлияет на точность
x = - (width / 1.29) / (width / 2.6)
for X in xrange(width):
x += step_x
z = complex(x, y)
phi = math.atan2(y, x - 0.25)
p = math.sqrt((x - 0.25) ** 2 + y ** 2)
pc = 0.5 - 0.5 * math.cos(phi)
if p <= pc: #если лежит в области кардиоиды - отмечаем
pix[X, Y] = (255, 255, 255)
continue
Z_i = 0j
for i in xrange(itt): #проверка на выход за "границы бесконечности"
Z_i = Z_i ** 2 + z
if abs(Z_i) > 2:
color = (i * 255) // itt
pix[X, Y] = (color, color, color)
break
else:
pix[X, Y] = (255, 255, 255)
print("\r%d/%d" % (Y, height)),
Заметим, что оператор возведения в степень ** — довольно «общий», нам же необходимо лишь возведение во вторую степень, т.е. все конструкции вида x**2 можно заменить на х*х, выиграв таким образом еще немного времени. Посмотрим на время:
1.9 сек, или 62% изначального времени, достигнуто простой заменой двух строк:
p = math.sqrt((x - 0.25) ** 2 + y ** 2)
...
Z_i = Z_i **2 + z
на:
p = math.sqrt((x - 0.25) * (x - 0.25) + y * y)
...
Z_i = Z_i * Z_i + z
Шажки 5, 6 и 7. Маленькие, но важные
Прописная истина, о которой знают все программисты на Python — работа с глобальными переменными медленней работы с локальными. Но часто забывается факт, что это верно не только для переменных но и вообще для всех объектов. В коде функции идут вызовы нескольких функций из модуля math. Так почему бы не импортировать их в самой функции? Сделано:
def mandelbrot(height, itt, width):
from math import atan2, cos, sqrt
pix = img.load() #загрузим массив пикселей
Еще 0.1сек отвоевано.
Вспомним, что abs(x) вернет число типа float. Так что и сравнивать его стоит с float а не int:
if abs(Z_i) > 2: ------> if abs(Z_i) > 2.0:
Еще 0.15сек. 53% от начального времени.
И, наконец, грязный хак.
В конкретно этой задаче, можно понять, что нижняя половина изображения, равна верхней, т.о. число вычислений можно сократить вдвое, получив в итоге 0.84сек или 27% от исходного времени.
Заключение
Профилируйте. Используйте timeit. Оптимизируйте. Python — мощный язык, и программы на нем будут работать со скоростью, пропорциональной вашему желанию разобраться и все отполировать:)
Цель данной статьи, показать, что за счет мелких и незначительных изменения, таких как замен ** на *, можно заставить зеленого змея ползать до двух раз быстрее, без применения тяжелой артиллерии в виде Си, или шаманств psyco.
Также, можно совместить разные средства, такие как вышеуказанные оптимизации и модуль psyco, хуже не станет:)
Спасибо всем кто дочитал до конца, буду рад выслушать ваши мнения и замечания в комментариях!
UPD Полезную ссылку в коментариях привел funca.
уменьшение объема кода, CSS и HTML оптимизаторы, валидация
Содержание статьи
Оптимизировать код HTML и каскадные таблицы стилей (CSS) нужно для того, чтобы ускорить процесс загрузки сайта и контента, размещенного на нем. Экономия времени и трафика, в целом, спустя долгий срок после оптимизации, получится существенной, даже если внешне изменение скорости будет не очень заметно.
Оптимизация HTML-кода
Для того чтобы HTML-код способствовал быстрой загрузке сайта, он должен соответствовать нескольким условиям:
- Быть простым и информативным. Проверив его на валидность, нужно исправить ошибки, чтобы ботам было легче анализировать его. Код должен четко представлять структуру страницы.
- Основные составляющие страниц, такие как заголовки, указатели, блоки информации должны быть легко и быстро определяемы.
- Освобождение кода от лишних сведений, вынесение их в отдельные файлы (например, можно вынести CSS и JS), повысит скорость загрузки, что также упрощает работу ботов.
Добившись выполнения этих условий, сайт можно сделать более быстрым, удобным и повысить эффективность индексации его ботами поисковых систем.
Уменьшение объема кода и оптимизация CSS
Сделать код сайта проще можно, уменьшив его объем. Для этого нужно выполнить несколько операций.
Прежде всего, следует избегать флеш-технологий, JavaScript, фреймов, текста, представленного картинкой. Все элементы, какие возможно, следует оформить в отдельных, внешних файлах (например, CSS и JS, как уже отмечалось выше). Помогут для оптимизации кода специальные плагины. К примеру, можно установить плагин Autoptimize, который автоматически оптимизирует код HTML, CSS, JS (если поставить соответствующие галочки). Необходимо настроить грамотную и удобную навигацию по сайту, которая будет понятна пользователям и корректно распознаваема поисковиками. Это увеличит скорость, качество загрузки и индексации сайта.
Что касается оптимизации CSS, то ее можно сделать самостоятельно или доверить это специальному ресурсу или программе. Ручная оптимизация – трудоемкий и длительный процесс, к тому же можно пропустить какие-то ошибки. Программа или сервис для оптимизации качественнее устранят недостатки, но могут нарушить некоторые функции, которые работали на сайте, и после проверки может некорректно отображаться контент, поэтому их работу нужно корректировать.
Некоторые способы улучшить структуру CSS в ручном режиме:
- Удалить лишние пробелы и разрывы строк, которые перегружают файл CSS и затрудняют работу роботов;
- Прописывать обобщающие свойства вместо несколько раз повторяющихся похожих команд;
- Использовать лаконичные, понятные описания в комментариях;
- Необычные шрифты прописывать при помощи стилей, а не изображений;
- Для картинок создавать alt и title (разные для каждого изображения), чтобы их содержание распознавалось ботами поисковых систем;
- Применять к заголовкам инструменты h2 – и далее, чтобы они корректно распознавались при индексации;
- Прописывать в keywords только те ключевые слова, которые используются на странице, минимизировать их количество;
- Использовать разнообразные и краткие мета-теги.
CSS и HTML оптимизаторы
Удобно и быстро осуществить оптимизацию непосредственно в браузере можно при помощи специальных сервисов, например:
- CleanCSS.com;
- CSS Optimizer;
- CSS Compressor;
- CY-PR.com;
- плагин Autoptimize.
Специалисты в SЕО и IT советуют пользоваться CleanCSS.com, так как он позволяет выбрать степень сжатия от низкой до высочайшей или подобрать индивидуальные настройки. После высокого и высочайшего уровней оптимизации код сайта принимает практически нечитаемый вид, и вносить в него изменения будет практически невозможно. Поэтому для начала следует воспользоваться стандартной оптимизацией. Можно выбрать режимы сжатия конкретного контента или оптимизацию отдельных параметров: сжатие шрифтов, изображений, удаление пробелов.
Также программа позволит сохранить результат в файле, в том числе в виде текста с отчетом о произведенных изменениях, подсветкой синтаксиса.
На ресурсе CY-PR.com также есть похожий инструмент для оптимизации, который облегчает структуру CSS на 25-30%, но здесь нет возможности сформировать файл с кодом после выполнения операции.
Остальные сервисы достаточно радикально могут поменять код так, что некоторые функции на сайте перестанут работать. Поэтому с их помощью лучше производить только локальные изменения в конкретных параметрах сайта.
После оптимизации следует проверить работоспособность сайта, корректное отображение дизайна, скорость загрузки. Нормальной считается ситуация, когда страницы и пользовательские опции сайта загружаются не более 3-5 секунд.
Для того чтобы проверить степень оптимизации и понять, насколько эффективно теперь прописан HTML-код, можно воспользоваться такими сервисами, как:
- optimization.com;
- Портал seo-чеклист;
- плагин Firebug.
Эти инструменты помогут проверить, все ли сделано для уменьшения объема HTML-кода и улучшения его структуры. На ресурсе SEO-чеклист можно проверять по списку, что уже было сделано для оптимизации, а что – еще нет, и устанавливать пометку на совершенных делах.
Валидация
Проверить код сайта на наличие ошибок можно при помощи сервисов проверки валидации – валидаторов. Эффективная проверка кода осуществляется с помощью validator.w3c.org
Если система при первой проверке выдаст более полутысячи ошибок, не стоит расстраиваться. После исправления определенного недочета, можно запускать проверку снова и ошибок станет намного меньше, так как один недостаток влечет за собой другие и при его исправлении они исчезнут.
Сайт с исправленным и оптимизированным кодом имеет больше шансов на высокие позиции в выдаче, чем ресурс с перегруженным HTML-кодом, в котором присутствуют ошибки.
Руководство по оптимизации кода — Часть 1
Введение
Эта статья предназначена для ознакомления с программным обеспечением
разработчиков в тему методов оптимизации. Для этого будут изучены различные методы оптимизации.
Как
первый шаг, я выбрал простой
понять алгоритм, к которому я применил различные методы оптимизации:
Задача, которую мы решим, — это задача 3n + 1 (подробности): для каждого числа n
от 1 до 1000000 примените следующую функцию:
, пока число не станет 1, считая количество раз, когда мы применяли функцию.
Это
алгоритм будет выполняться для всех чисел от 1 до 1000000. Нет ввода
номер с клавиатуры будет прочитан и программа распечатает результат,
за которым следует время выполнения (в миллисекундах), необходимое для вычисления результата.
Тестовая машина будет представлять собой ноутбук со следующими характеристиками: AMD Athlon 2 P340 Dual Core 2,20 ГГц, 4 ГБ ОЗУ, Windows 7 Ultimate x64.
Языки, используемые для реализации: C # и C ++ (Visual Studio 2010).
Необходимые условия
Н / Д
Другой
реализации для той же проблемы
начальная версия реализации: для каждого числа от 1 до 1000000 указанное выше
упомянутый алгоритм будет применяться, генерируя последовательность чисел до n
становится 1.Будут засчитаны шаги, необходимые для достижения 1.
и будет определено максимальное количество шагов.
Код C ++:
для (int i = nFirstNumber; i nMaxCycleCount)
{
nMaxCycleCount = nCurrentCycleCount;
}
}
Код C #:
для (int i = FirstNumber; i MaxCycleCount)
{
MaxCycleCount = iCurrentCycleCount;
}
}
Я скомпилировал код для сборок отладки и выпуска, как 32-разрядной, так и 64-разрядной версии.Затем я запускал каждый исполняемый файл 100 раз и вычислял среднее время (мс), необходимое для выполнения вычислений.
Вот результаты:
C ++ Debug | C ++ Release | C # Debug | C # Release | ||||||
x86 версия | 6882.91 | 6374.50 | 6358.4109 | x64 версия | 1020,78 | 812,71 | 1890.36 | 742,28 |
Первое, на что следует обратить внимание в таблице, это то, что
32-битные версии программы в 5-7 раз медленнее, чем 64-битные версии.
Это связано с тем, что на архитектурах x64 один регистр может содержать длинную переменную типа long, а на x86 нам нужно 2 регистра. Это означает, что на x86 операции с длинными длинными значениями выполняются медленно. Из-за этого мы больше не будем рассматривать 32 бита в этом
статья.
Второе, на что следует обратить внимание, это разница
между сборками Release и Debug, а также что для C # различия заключаются в
больше, чем для C ++.
Другой
наблюдение — это разница между версией C # Release и C ++ Release
версия. Это вместе с предыдущим наблюдением заставляет меня думать, что
Компилятор C # выполняет оптимизацию лучше, чем компилятор C ++ (возможно, даже
используя некоторые методы оптимизации, о которых мы поговорим
позже).
Первые оптимизации, которые я буду применять, связаны с выполнением математических
быстрее, заменив традиционный способ их выполнения на
нестандартный способ.
Если
мы смотрим на приведенный выше код и видим, что у нас всего 3 сложных математических
операции: операция по модулю 2 (%),
умножение на 3 (*) и деление на 2 (/).
Первая операция, которую я оптимизирую, — это модуль 2.
Мы знаем, что все числа представлены в памяти как последовательность битов. мы
также знайте, что представление нечетного числа всегда будет иметь последний бит 1 (5
= 101, 13 = 1101 и т. Д.), И представление четного числа всегда будет
имеет последний бит 0 (6 = 110, 22 = 10110).Итак, если мы сможем получить последнюю часть
number и проверьте его на 0, мы знаем, является ли число нечетным или четным. Чтобы получить
последний бит числа я использую побитовый оператор И (&).
В C ++ замените:
if ((nNumberToTest% 2) == 1)
с:
if ((nNumberToTest & 0x1) == 1)
В C # заменить:
, если ((iNumberToTest% 2) == 1)
с:
, если ((iNumberToTest & 0x1) == 1)
Вот результаты:
Отладка C ++ | Выпуск C ++ | Отладка C # | Выпуск C # |
922.46 | 560,86 | 1641,41 | 714,10 |
C ++
Релизная версия больше всего выигрывает от этой оптимизации. Разница в
улучшение между версиями C ++ Release и Debug заставляет меня думать, что
компилятор может удалить больше инструкций в сборке Release с помощью
новый алгоритм оптимизации.
C #
похоже, что эта оптимизация не слишком выгодна.
Следующая операция, которую я постараюсь оптимизировать, — это деление на 2.Если мы снова посмотрим на
двоичное представление чисел, мы можем заметить, что когда мы делим на
2 отбрасываем последний бит числа и добавляем 0 бит перед оставшимся
биты. Итак, 5 (= 101) / 2 = 2 (= 010), 13 (= 1101) / 2 = 6 (= 0110), 6 (= 110) / 2 = 3
(= 011) и т. Д. Заменяю эту операцию побитовым сдвигом вправо
операция, дающая тот же результат.
В C ++ замените:
nNumberToTest = nNumberToTest / 2;
с:
nNumberToTest = nNumberToTest >> 1;
В C # заменить:
iNumberToTest = iNumberToTest / 2;
с:
iNumberToTest = iNumberToTest >> 1;
Вот результаты:
Отладка C ++ | Выпуск C ++ | Отладка C # | Выпуск C # |
821.58 | 555,96 | 1432,01 | 652,11 |
C ++
Debug, C # Debug, C # Release версия выигрывает от 65 до 200 миллисекунд от
это оптимизация.
C ++
Релиз почти ничего не получает от этой замены, вероятно, потому что
компилятор уже выполнял эту оптимизацию.
Последняя
математическая операция, которая требует времени, — это умножение на 3. Единственное
что мы можем сделать с этой операцией, так это заменить ее дополнениями.
В C ++ заменить:
nNumberToTest = nNumberToTest * 3 + 1;
с:
nNumberToTest = nNumberToTest + nNumberToTest + nNumberToTest + 1;
В C # заменить:
iNumberToTest = iNumberToTest * 3 + 1;
с:
iNumberToTest = iNumberToTest + iNumberToTest + iNumberToTest + 1;
Вот результаты:
C ++ Debug | C ++ Release | C # Debug | C # Release |
820.84 | 548,93 | 1535,28 | 629,89 |
наибольший прирост производительности наблюдается в версии C # Release, за которой следует
версия C ++ Release.
Версия отладки C # показывает снижение производительности из-за
к тому, что текущая версия программного обеспечения выполняет больше инструкций, чем
предыдущая и компилятор не может оптимизировать инструкции (не может
замените их чем-нибудь другим, потому что нам может потребоваться установить точку останова на любом из них).
Там
это последняя математическая оптимизация, которую мы можем выполнить на основе некоторых специальных инструкций, которые
процессор орудия. Эти инструкции представляют собой так называемый условный ход.
инструкции. Чтобы определить компилятор для создания условного перехода
инструкцию, я заменю оператор IF (который проверяет, нечетное ли число
или даже) с тернарным оператором (?:).
Кому
иметь возможность реализовать упомянутую выше оптимизацию, нам нужно изменить
постановка задачи. Если число четное, оно будет разделено на 2 (как предписано
за проблему).Если число нечетное, его можно выразить как 2 * n + 1. Применяя
при изменении исходного вида функции получим:
Из приведенного выше уравнения видно, что мы можем выполнить
2 шага алгоритма в 1. Мы перепишем алгоритм так, чтобы мы
вычислить следующее значение числа для проверки, предполагая, что текущее значение четное.
Затем мы сохраним значение последнего бита текущего числа для проверки. Если
это значение истинно, мы увеличим счетчик текущего цикла и добавим текущий
number + 1 к следующему значению числа для проверки.(Примечание: эта оптимизация
станет действительно важным в одной из следующих статей, когда я расскажу о
SSE).
В C ++ заменить:
if ((nNumberToTest% 2) == 1)
{
nNumberToTest = nNumberToTest * 3 + 1;
}
еще
{
nNumberToTest = nNumberToTest / 2;
}
nCurrentCycleCount ++;
с:
int nOddBit = nNumberToTest & 0x1;
long long nTempNumber = nNumberToTest >> 1;
nTempNumber + = nOddBit? nNumberToTest + 1: 0;
nCurrentCycleCount + = nOddBit? 2: 1;
nNumberToTest = nTempNumber;
В C # заменить:
если ((iNumberToTest% 2) == 1)
{
iNumberToTest = iNumberToTest * 3 + 1;
}
еще
{
iNumberToTest = iNumberToTest / 2;
}
iCurrentCycleCount ++;
с:
bool bOddBit = (iNumberToTest & 0x1) == 0x1;
long iTempNumber = iNumberToTest >> 1;
iTempNumber + = bOddBit? iNumberToTest + 1: 0;
iCurrentCycleCount + = bOddBit? 2: 1;
iNumberToTest = iTempNumber;
Вот результаты:
Отладка C ++ | Выпуск C ++ | Отладка C # | Выпуск C # |
1195.38 | 462,21 | 1565,01 | 752,92 |
Обе отладочные сборки показывают замедление, потому что мы
теперь выполняют больше инструкций по сравнению с предыдущими версиями
код и компиляторы не могут их оптимизировать.
Версия выпуска C # показывает замедление из-за отсутствия условного перемещения
инструкции на C #.
мощь этой категории инструкций подтверждается повышенной скоростью работы
Версия выпуска C ++.
Это
можно заметить, что я решил проблему с помощью рекурсии.Для этой проблемы
рекурсивный алгоритм будет чрезвычайно медленным: максимальная длина цикла 525,
поэтому предполагая, что большинство чисел имеют длину цикла около 150 (просто предположение,
фактически не проверено), если у нас есть 150 рекурсивных вызовов для каждого числа между
1 и 1000000, нам нужно будет выполнить 150000000 вызовов. Это явно не
небольшое число и, поскольку вызов функции занимает много времени, рекурсия
определенно, не лучшее решение этой проблемы.
Достопримечательности
Это
время делать выводы:
- Операция по модулю и делению занимает много времени, и их следует заменить на
что-то другое. - Попытайтесь проанализировать проблему и получить альтернативное представление
проблема. - Попробуйте исключить операторы IF из вашего кода в случае, если они
цель — установить некоторые значения на основе условия.
The
в следующий раз тема будет о том, как сделать нашу программу быстрее, используя многопоточность
в C # и C ++.
История
- 27 мая 2012 г. — Первый выпуск.
- 28 мая 2012 г. — Я хотел бы поблагодарить anlarke за указание на вещи, которые можно улучшить в статье, и за отправку его кода (Время отладки C ++: 546.76 мс, C ++ Время выпуска: 386,35 мс). Также я хотел бы поблагодарить Реонекота за разъяснения по теме WoW. Он прав и проблемы с производительностью вызваны тем, что регистры 32-битные (для x86) и 64-битные (для x64).
.
Советы и рекомендации по оптимизации кода Python, которые вы должны знать
Python — мощный язык программирования. И мы можем сделать так много, чтобы сделать код легче и быстрее. Он не только поддерживает такие функции, как многопроцессорность, но и делает это с легкостью. Ниже мы перечислили некоторые из лучших советов и приемов оптимизации кода Python . Читайте дальше!
Критики
Python иногда утверждают, что он медленный. Но это не из-за Python, а из-за методов, используемых при написании кода.Здесь, в этом посте, вы найдете советы по ускорению работы приложений Python.
Поскольку Python — это язык с богатым набором функций, всегда есть возможности для улучшения. Если вы хотите, чтобы ваш код Python работал еще быстрее и эффективнее, продолжайте читать.
Красота этих советов и Python — это все методы оптимизации, которые на самом деле лежат в области Python . Вам просто нужно знать их и применять дисциплинированно при кодировании. Недавно мы написали статью о тридцати основных советах и приемах программирования на Python .Вы также можете проверить это, чтобы найти то, что вы еще не знаете.
Здесь представлена таблица индекса , чтобы вам было легко ориентироваться в этом сообщении.
1. Струны для определения эффективности.
2. Оптимизация глазка.
3. Профиль вашего кода.
3.1. Используйте профилирование секундомера с
3.2. Используйте расширенное профилирование с
4. Используйте генераторы и ключи для сортировки.
5. Оптимизация циклов.
5.1. Иллюстрации для оптимизации цикла for в Python.
5.2. Давайте расшифруем то, что мы оптимизировали?
6. Используйте операции с наборами.
7. Избегайте использования глобальных переменных.
8. Используйте внешние библиотеки / пакеты.
9. Используйте встроенные операторы.
10. Ограничение поиска метода в цикле.
11. Оптимизация с помощью строк.
12. Оптимизация с помощью оператора If.
Давайте сначала начнем с некоторых основных внутренних компонентов Python, которые вы можете использовать в своих интересах.
Советы и рекомендации по оптимизации кода Python для компьютерных фанатов
1. Струны для повышения эффективности
Интернирование строки — это метод хранения только одной копии каждой отдельной строки. И мы можем заставить интерпретатор Python повторно использовать строки, манипулируя нашим кодом, чтобы вызвать интернирование строк.
Обычно, когда мы создаем строковый объект, интерпретатор Python определяет, кэшировать ли строку или нет.Это внутренняя природа интерпретатора, которую он раскрывает в определенных условиях, например при обработке идентификаторов.
Объявление строки с именем, которое начинается с буквы или подчеркивания и включает только или комбинации букв / подчеркиваний / цифр, заставит Python интернировать строку и создать для нее хэш.
Поскольку Python имеет много внутреннего кода, использующего словари, он не выполняет. поисков идентификаторов. Таким образом, интернирование строк идентификаторов ускоряет весь процесс.Проще говоря, Python хранит все идентификаторы в таблице и генерирует уникальные ключи (хэш) для каждого объекта для будущих поисков. Эта оптимизация происходит во время компиляции. И он также сочетает в себе интернирование строковых литералов, напоминающих идентификаторы.
Так что это довольно полезная функция Python, которую вы можете использовать с пользой. Такая функция может помочь вам ускорить обработку большого приложения для интеллектуального анализа текста или аналитики. Потому что они требуют частого поиска и перебрасывания сообщений для бухгалтерского учета.
Строки, которые вы читаете из файла или получаете по сети, не являются частью автоматического интернирования в Python. Вместо этого вы можете передать эту задачу функции intern () для обработки таких строк.
TOC
2. Методика оптимизации глазка
Оптимизация глазка — это метод, который оптимизирует небольшой сегмент инструкций из программы или части программы. Этот сегмент тогда известен как
Давайте посмотрим, как Python справляется с оптимизацией глазка. В нем есть встроенный способ сделать это, посмотрите примеры ниже.
Пример-1
В примере есть функция, инициализирующая два своих члена. Один из них — строка, а другой — целое число. Далее к функции добавляется еще один атрибут кода, который по умолчанию будет <Нет>. Интересно то, что четыре литерала останутся в памяти как константы.Пожалуйста, обратитесь к изображению, приведенному ниже.
Советы и приемы по оптимизации кода Python — пример (1)
На прилагаемом снимке вы можете видеть, что мы использовали константу <.__ code __. Co_consts>. Это один из трех кортежей, которые есть у каждого объекта функции в Python. Да, функция также является объектом в Python. Он состоит из следующих трех кортежей.
1. <__ code __. Co_varnames>: содержит локальные переменные, включая параметры.
2. Код <__ code__.co_names>: хранит глобальные литералы.
3. <__ code __. Co_consts>: ссылки на все константы.
Теперь есть еще кое-что, что может сделать оптимизация с помощью глазка, например, превращение изменяемых конструкций в неизменяемые. См. Примеры ниже.
Пример-2
В этом примере мы ищем определенный элемент в наборе, используя оператор «in». Здесь Python обнаружит, что набор будет использоваться для проверки принадлежности элемента.Таким образом, он будет рассматривать инструкции как операцию с постоянной стоимостью, независимо от размера набора. И обработает их быстрее, чем это было бы в случае кортежа или списка. Этот метод известен как тестирование членства в Python. Пожалуйста, проверьте приложенный снимок экрана.
Советы и приемы оптимизации кода Python — Пример (2)
Пример-3
Тем не менее, если вы используете объект списка аналогично тому, как мы это делали с набором в последнем примере, Python также переведет его в константу кортежа.Теперь, в этом примере, мы объединили использование набора и списка вместе. И показано, что оба объекта переводятся в константы. См. Приложенный снимок экрана.
Советы и приемы оптимизации кода Python — пример (3)
Тот же факт мы можем проверить, посмотрев на байт-код переведенного кода. Для этого нам нужно импортировать модуль «dis» Python. А передача объекта функции в качестве аргумента конструктору dis распечатает весь макет памяти в байт-коде.
Советы и приемы оптимизации кода Python — пример (4)
Из вывода «dis» в прилагаемом изображении довольно легко убедиться, что и набор, и список превратились в константы.
Ключевым моментом здесь является то, что Python выполняет это преобразование только для литералов. Если бы какой-либо из наборов / списков, используемых в приведенных выше примерах, не был бы литералами, то оптимизации не произошло бы.
TOC
3. Укажите свой код
Прежде чем углубляться в оптимизацию кода, было бы наивно, если бы вы не знали, в чем заключаются узкие места.Итак, прежде всего профилируйте свой код, используя любой из двух подходов, упомянутых ниже.
3.1. Используйте профилирование секундомера с
Это традиционный способ профилирования с использованием модуля Python
время импорта subStrings = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] def simpleString (подстроки): finalString = '' для части в подстроках: finalString + = часть вернуть finalString def formatString (subStrings): finalString = "% s% s% s% s% s% s% s"% (subStrings [0], subStrings [1], subStrings [2], subStrings [3], subStrings [4], subStrings [5], subStrings [6]) вернуть finalString def joinString (подстроки): возвращение ''.присоединиться (подстроки) print ('joinString () Time:' + str (timeit.timeit ('joinString (subStrings)', setup = 'from __main__ import joinString, subStrings'))) print ('formatString () Time:' + str (timeit.timeit ('formatString (subStrings)', setup = 'from __main__ import formatString, subStrings'))) print ('simpleString () Time:' + str (timeit.timeit ('simpleString (subStrings)', setup = 'from __main__ import simpleString, subStrings')))
joinString () Время: 0.236362000001 formatString () Время: 0.711244 simpleString () Время: 0.6721448
Приведенный выше пример демонстрирует, что метод соединения немного более эффективен, чем другие.
3.2. Используйте расширенное профилирование с
Начиная с Python 2.5, cProfile является частью пакета Python. Он предлагает хороший набор функций профилирования для выявления узких мест в коде. Вы можете связать это с вашим кодом разными способами. Например, оберните функцию внутри ее метода запуска, чтобы измерить производительность. Или запустите весь сценарий из командной строки, активировав cProfile в качестве аргумента с помощью опции Python «-m».
Здесь мы показываем несколько основных примеров, чтобы вы могли узнать, как его использовать.
3.2.1. Пример (1) — Использование cProfile в Python
импорт cProfile cProfile.run ('10 * 10 ')
3 вызова функций за 0,000 секунд Упорядочено по: стандартному названию ncalls tottime percall cumtime percall имя файла: белье (функция) 1 0,000 0,000 0,000 0,000 <строка>: 1 (<модуль>) 1 0,000 0.000 0.000 0.000 {встроенный метод builtins.exec} 1 0,000 0,000 0,000 0,000 {метод 'disable' объектов '_lsprof.Profiler'}
Глядя на результаты, вы можете исследовать области для улучшения. Еще более полезно то, что мы можем прикрепить cProfile во время выполнения скрипта.
3.2.2. Пример (2) — Использование cProfile в Python
$ python -m cProfile -s cumtime test1.py 3 вызова функций за 0,000 секунд Упорядочено по: стандартному названию ncalls tottime percall cumtime percall имя файла: белье (функция) 1 0.000 0,000 0,000 0,000 <строка>: 1 (<модуль>) 1 0,000 0,000 0,000 0,000 {встроенный метод builtins.exec} 1 0,000 0,000 0,000 0,000 {метод 'disable' из '_lsprof.Prof iler 'объекты} 303 вызова функций (302 примитивных вызова) за 0,005 секунды Упорядочено по: совокупному времени ncalls tottime percall cumtime percall имя файла: белье (функция) 2/1 0,000 0,000 0,005 0.005 {встроенный метод builtins.exec} 1 0,000 0,000 0,005 0,005 test1.py:1 (<модуль>) 1 0,000 0,000 0,004 0,004 cProfile.py:15(run) 1 0,000 0,000 0,004 0,004 profile.py:52(run) 1 0,000 0,000 0,004 0,004 cProfile.py:92(run) 1 0,000 0,000 0,004 0,004 cProfile.py:97(runctx) 1 0,004 0,004 0,004 0,004 {метод «включить» _lsprof.Profi ler 'объекты} 1 0.000 0,000 0,001 0,001 <замороженная importlib._bootstrap>: 966 (_find_and_load) 1 0,000 0,000 0,001 0,001 <замороженная importlib._bootstrap>: 939 (_find_and_load_unlocked) 1 0,000 0,000 0,000 0,000 <замороженный importlib._bootstrap>: 879 (_find_spec) 1 0,000 0,000 0,000 0,000 <замороженная importlib._bootstrap_external>: 1133 (find_spec) 1 0,000 0,000 0,000 0,000 <замороженная importlib._bootstrap_external>: 1101 (_get_spec) 4 0.000 0,000 0,000 0,000 <замороженная importlib._bootstrap_external>: 1215 (find_spec) 1 0,000 0,000 0,000 0,000 <замороженная importlib._bootstrap>: 659 (_load_unlocked) 1 0,000 0,000 0,000 0,000 <замороженная importlib._bootstrap_external>: 659 (exec_module) 1 0,000 0,000 0,000 0,000 <замороженная importlib._bootstrap_external>: 729 (get_code) 6 0,000 0,000 0,000 0,000 <замороженная importlib._bootstrap_external>: 68 (_path_stat) 6 0.000 0.000 0.000 0.000 {встроенный метод nt.stat} [...]
3.2.3. Как интерпретировать результаты cProfile?
Еще важнее найти виновника по результатам профилирования. Вы можете принять решение, только если знаете ключевые элементы, составляющие отчет cProfile.
1.
2.
3.
4.
5.
6.
Теперь у вас есть все элементы отчета профилирования. Таким образом, вы можете продолжать поиск возможных разделов вашей программы, создавая узкие места в коде.
Прежде всего, начните проверять наиболее важные
TOC
4. Использование генераторов и ключей для сортировки
Генераторы
— отличный инструмент для оптимизации памяти. Они облегчают создание функций, которые могут возвращать один элемент (итератор) за раз, а не возвращать все сразу. Хороший пример — когда вы составляете огромный список цифр и складываете их вместе.
Кроме того, при сортировке элементов в списке вы должны по возможности использовать ключи и метод
оператор импорта test = [(11, 52, 83), (61, 20, 40), (93, 72, 51)] print ("Перед сортировкой:", тест) test.sort (ключ = operator.itemgetter (0)) print ("После сортировки [1]:", тест) контрольная работа.sort (key = operator.itemgetter (1)) print ("После сортировки [2]:", тест) test.sort (ключ = operator.itemgetter (2)) print ("После сортировки [3]:", тест)
Перед сортировкой: [(11, 52, 83), (61, 20, 40), (93, 72, 51)] После сортировки [1]: [(11, 52, 83), (61, 20, 40), (93, 72, 51)] После сортировки [2]: [(61, 20, 40), (11, 52, 83), (93, 72, 51)] После сортировки [3]: [(61, 20, 40), (93, 72, 51), (11, 52, 83)]
TOC
5. Оптимизация циклов
Большинство языков программирования подчеркивают необходимость оптимизации циклов.В Python у нас есть способ заставить циклы работать быстрее. Рассмотрим метод, который программисты часто упускают из виду, — это предотвращение использования точек в цикле.
В Python вы увидите несколько строительных блоков, поддерживающих цикл. Из этих немногих преобладает использование цикла for. Хотя вы можете любить использовать петли, но они имеют свою цену. Движок Python тратит значительные усилия на интерпретацию конструкции цикла for. Следовательно, всегда предпочтительно заменять их встроенными конструкциями, такими как Карты.
Далее, уровень оптимизации кода также зависит от ваших знаний встроенных функций Python. В приведенных ниже примерах мы попытаемся объяснить, как разные конструкции могут помочь в оптимизации циклов.
5.1. Иллюстрации для оптимизации цикла for в Python
Пример-1
Давайте рассмотрим функцию, которая обновляет список почтовых индексов, удаляет конечные пробелы и использует цикл for.
newZipcodes = [] для почтового индекса в старых почтовых индексах: новые почтовые индексы.добавить (zipcode.strip ())
Пример-2
Теперь посмотрим, как вы можете преобразовать вышесказанное в одну линию с помощью объекта карты. Кроме того, теперь это будет более рентабельно.
newZipcodes = map (str.strip, oldZipcodes)
Пример-3
Мы даже можем использовать списки, чтобы сделать синтаксис немного более линейным.
Почтовые индексы + = [iter.strip () для iter в новых почтовых индексах]
Пример-4
И, наконец, самым быстрым подходом было бы преобразование цикла for в выражение генератора.
itertools.chain (Почтовые индексы, (iter.strip () для iter в новых почтовых индексах))
5.2. Давайте расшифруем, что мы оптимизировали?
Как объяснялось выше, использование выражения генератора — самый быстрый способ оптимизировать цикл for в приведенном выше варианте использования (и в целом). Мы собрали код из четырех примеров, чтобы вы также могли увидеть прирост производительности, достигнутый при каждом подходе.
время импорта импортировать itertools Почтовые индексы = ['121212', '232323', '434334'] newZipcodes = ['131313', '242424', '212121', '323232', '342312', '565656'] def updateZips (новые почтовые индексы, почтовые индексы): для почтового индекса в новых почтовых индексах: Почтовые индексы.добавить (zipcode.strip ()) def updateZipsWithMap (новые почтовые индексы, почтовые индексы): Почтовые индексы + = карта (str.strip, newZipcodes) def updateZipsWithListCom (новые почтовые индексы, почтовые индексы): Почтовые индексы + = [iter.strip () для iter в новых почтовых индексах] def updateZipsWithGenExp (новые почтовые индексы, почтовые индексы): вернуть itertools.chain (Zipcodes, (iter.strip () для iter в newZipcodes)) print ('updateZips () Time:' + str (timeit.timeit ('updateZips (newZipcodes, Zipcodes)', setup = 'from __main__ import updateZips, newZipcodes, Zipcodes'))) Почтовые индексы = ['121212', '232323', '434334'] print ('updateZipsWithMap () Time:' + str (timeit.timeit ('updateZipsWithMap (newZipcodes, Zipcodes)', setup = 'from __main__ import updateZipsWithMap, newZipcodes, Zipcodes'))) Почтовые индексы = ['121212', '232323', '434334'] print ('updateZipsWithListCom () Time:' + str (timeit.timeit ('updateZipsWithListCom (newZipcodes, Zipcodes)', setup = 'from __main__ import updateZipsWithListCom, newZipcodes, Zipcodes'))) Почтовые индексы = ['121212', '232323', '434334'] print ('updateZipsWithGenExp () Time:' + str (timeit.timeit ('updateZipsWithGenExp (newZipcodes, Zipcodes)', setup = 'from __main__ import updateZipsWithGenExp, newZipcodes, Zipcodes')))
updateZips () Время: 1.525283 updateZipsWithMap () Время: 1.4145331 updateZipsWithListCom () Время: 1.4271637 updateZipsWithGenExp () Время: 0.6092696999999996
TOC
6. Использование операций над наборами
Python использует хеш-таблицы для управления наборами. Всякий раз, когда мы добавляем элемент в набор, интерпретатор Python определяет его позицию в памяти, выделенной для набора, используя хэш целевого элемента.
Поскольку Python автоматически изменяет размер хеш-таблицы, скорость может быть постоянной (O (1)) независимо от размера набора.Именно поэтому заданные операции выполняются быстрее.
В Python операции над множествами включают объединение, пересечение и разность. Так что вы можете попробовать использовать их в своем коде там, где они могут поместиться. Обычно это быстрее, чем перебирать списки.
Описание синтаксической операции ------ --------- ----------- set (l1) | set (l2) Union Set со всеми элементами l1 и l2. set (l1) & set (l2) Набор пересечений с общими элементами l1 и l2. set (l1) -set (l2) Разница Установить с l1 элементами, не входящими в l2.
7. Избегайте использования глобальных переменных
Это не ограничивается Python, почти все языки не одобряют чрезмерное или незапланированное использование глобальных переменных. Причина в том, что они могли иметь скрытые / неочевидные побочные эффекты, приводящие к спагетти-коду. Более того, Python очень медленно обращается к внешним переменным.
Однако он допускает ограниченное использование глобальных переменных. Вы можете объявить внешнюю переменную, используя ключевое слово global. Кроме того, сделайте локальную копию, прежде чем использовать их внутри циклов.
8. Использование внешних библиотек / пакетов
Некоторые библиотеки Python имеют эквивалент на языке C с теми же функциями, что и исходная библиотека. Написание на «C» заставляет их работать быстрее. Например, попробуйте использовать cPickle вместо использования pickle.
Затем вы можете использовать
Вы также можете рассмотреть возможность использования пакета PyPy. Он включает JIT-компилятор (Just-in-time), благодаря которому код Python запускается невероятно быстро. Вы даже можете настроить его, чтобы обеспечить дополнительное ускорение обработки.
TOC
9. Используйте встроенные операторы
Python — это интерпретируемый язык, основанный на абстракциях высокого уровня. Поэтому вы должны использовать встроенные модули везде, где это возможно. Это сделает ваш код более эффективным, поскольку встроенные модули предварительно скомпилированы и работают быстро. В то время как длительные итерации, включающие интерпретированные шаги, выполняются очень медленно.
Точно так же предпочитайте использовать встроенные функции, такие как карта, которые значительно улучшают скорость.
10. Ограничить поиск метода в цикле
При работе в цикле вы должны кэшировать вызов метода вместо его вызова для объекта. В противном случае поиск методов обходится дорого.
Рассмотрим следующий пример.
>>> для него в xrange (10000): >>> myLib.findMe (это)
>>> findMe = myLib.Найди меня >>> для него в xrange (10000): >>> findMe (это)
TOC
11. Оптимизация с помощью строк
Конкатенация строк выполняется медленно, никогда не делайте этого внутри цикла. Вместо этого используйте метод соединения Python. Или используйте функцию форматирования, чтобы сформировать единую строку.
Операции
RegEx в Python выполняются быстро, так как они возвращаются в код C. Однако в некоторых случаях базовые строковые методы, такие как
Также вы можете протестировать различные методы с помощью модуля
12. Оптимизация с помощью оператора if
Подобно большинству языков программирования, Python допускает выполнение отложенного выполнения. Это означает, что если есть объединяющие условия «И», то не все условия будут проверены, если одно из них окажется ложным.
1. Вы можете настроить свой код для использования этого поведения Python.Например, если вы ищете фиксированный шаблон в списке, вы можете уменьшить объем, добавив следующее условие.
Добавьте условие «И», которое становится ложным, если размер целевой строки меньше длины шаблона.
Кроме того, вы можете сначала проверить быстрое условие (если оно есть), например «строка должна начинаться с @» или «строка должна заканчиваться точкой».
2. Вы можете проверить условие <, например, если выполнено
, а не Нет
>, что быстрее, чем использование <, если выполнено! = Нет
>.
TOC
Резюме
— Советы и приемы оптимизации кода Python
Мы хотим, чтобы методы, приведенные в этой статье, помогли вам создавать более быстрые приложения Python. Но при применении этих советов помните, что оптимизируйте только тогда, когда вы установили серые области в своем коде.
Некоторые советы, которые вы можете сразу применить в своей практике кодирования. Например, использование методов профилирования, именно они указывают вам правильное направление и ведут на путь оптимизации.
Помимо приведенных выше советов, вы должны проверить свой код на квадратичное поведение. Вы должны знать о временной сложности различных конструкций Python, таких как список, набор и коллекции. Наконец, соберите как можно больше данных, это поможет вам определить, правильно вы делаете или нет.
Если каким-то образом вышеуказанный пост « Советы и рекомендации по оптимизации кода Python » побудил вас писать более быстрый и качественный код, то не прочь поделиться им с остальным миром.
Если вы работаете над тем, что действительно волнует, вас не нужно подталкивать. Видение тянет вас. — Стив Джобс
Всего наилучшего,
TechBeamers
.