[Из песочницы] Своевременная оптимизация


Всем известно, что преждевременная оптимизация — это плохо и надо себя одёргивать когда, возникает желание пооптимизировать не вовремя. Однако на практике чаще бывает ситуация когда естественное (и, возможно, интуитивно правильное) желание пооптимизировать подавляется по принципу «если вообще не оптимизировать — это не будет преждевременно». Либо так:

4dcb777bd8634ea3a5be3018c4a5423d.jpg

На мой взгляд, подобные ситуация возникают потому, что границы понятия «преждевременности» весьма нечёткие и интуитивные, как будто это что-то эмпирическое и неуловимое вроде сочности хруста французской булки.

Хотя в принципе довольно странно оперировать какими-то эмпирическими понятиями по отношению к архитектуре программ, алгоритмам и их оптимизации — поскольку это вполне измеримые вещи. А значит — можно достаточно просто измерить своевременность оптимизации. Об этом и поговорим.

Что такое оптимизация?


Оптимизация простыми словами — это приведение программы от состояния «не устраивает» до состояния «устраивает» по параметрам производительности (время выполнения, потребление ресурсов, пропускная способность). Т.е. мы знаем, какие показатели нас устраивают, и когда мы видим, что программа до них не дотягивает — пора её оптимизировать.

Что такое преждевременная оптимизация?


В тех случаях, когда мы думаем, что попадаем в состояние «не устраивает», а на самом деле всё «устраивает» — мы оптимизируем преждевременно.

Что такое своевременная оптимизация?


Мы знаем, что программа нас уже/вот-вот/будет «не устраивать» — и принимаем меры по исправлению ситуации.

В идеальном мире мы создаём программу, удовлетворяющую требованиям, выпускаем её на волю — и там нас всё сразу «устраивает». Как случается в реальном мире чаще всего, мы все прекрасно знаем.

К счастью, чтобы понять, своевременно или преждевременно мы оптимизируем, нужно совершить одни и те же действия и для этого обычно достаточно листка бумаги.

Измеримая своевременность


В общем и целом, само по себе измерение своевременности/преждевременности требует довольно простых действий:
  1. Взять требования по производительности
  2. Посчитать, укладывается ли программа в эти требования
  3. Если укладывается — жить счастливо; в противном случае — оптимизировать программу

Поскольку самым распространённым требованием является время отклика, пропускная способность сводится к нему же, а потребление ресурсов отдельная и большая тема сконцентрируемся на первом.

Итак, у нас есть требуемое времени выполнения программы — ТВ и требуемое кол-во обрабатываемых данных за проход — ТД.

Мы ещё только проектируем или разрабатываем программу, и нас не покидает ощущение, что что-то идёт не оптимально и возникает боязнь преждевременной оптимизации. Для измерения нашего ощущения и предотвращения развития фобии нужно выполнить следующие действия:

  1. Определить временную сложность спроектированного/разработанного алгоритма O (n), учитывая константы. 3*n так 3*n, n*logn + n и т.п. Чем точнее — тем лучше.
  2. Получить кол-во выполняемых операций за проход — КО путём подстановки ТД в функцию, описывающую временную сложность.
  3. Посчитать среднее допустимое время обработки одного элемента — СВЭ входного массива (подразумеваем верхнеуровневую сущность, один документ, одного пользователя) на основе TB.Т. е.
    СВЭ = TB / KO
  4. Далее мы убеждаемся что фактическое время обработки одного элемента <= среднего допустимого времени обработки одного элемента, при необходимости повторяя шаги 1 и 2 для программ/алгоритмов находящихся выше по стэку.

Разберём простой пример


Допустим, что нам нужно написать программу, которая должна обработать 100 записей и уложиться в 2 секунды. В ходе разработки мы придумали некий алгоритм, временная сложность которого O (n^2).

В таком случае у нас есть 2/100^2=2×10^-4 секунд (0,2 миллисекунд или 200 микросекунд) в среднем для обработки каждой записи. Этого хватит для выполнения простых действий (арифметика и обращения к памяти занимают десятки или сотни наносекунд, если, например, судить по этой инфографике), но какие бы то ни было сетевые взаимодействия уже становятся недоступны.

Т.е. если мы пишем сортировку массива чисел под такие требования — нас «устраивает», а если нам нужно отправить 100 запросов SOAP — нас «не устраивает» и пора что-то придумывать.

Заключение


В общем-то это всё, данный подход легко масштабируем и позволяет легко оценить верхнюю границу своевременности, избавляет от чувства неопределённости и повышает осмысленность производимых программ. А самое главное — делает ваших пользователей и менеджеров счастливыми, поскольку в продакшене меньше горит.

Естественно, этого подхода недостаточно для вывода в промышленную эксплуатацию — поэтому проконсультируйтесь со специалистом прежде, чем отказываться от нагрузочных испытаний.

Спасибо за внимание!

Комментарии (2)

  • 14 марта 2017 в 12:59 (комментарий был изменён)

    +1

    Где-то я уже писал, что есть определенное не понимание, когда нормальный инженерный подход начинают называть преждевременной оптимизацией. Подумать над кодом и его маштабированием заранее не является примером преждевременной оптимизации. Особенно если код после этого не усложняется ибо нет ничего хуже «неоптимизированного» написанного за пять минут кода, который на самом деле является просто примером применения парадигмы «фигаг фигаг и в продакшен».


    Короче говоря уж очень часто запретом на оптимизацию прикрывают собственную некомпетентность.

    • 14 марта 2017 в 13:17

      0

      нет ничего хуже «неоптимизированного» написанного за пять минут кода

      Если этот код приносит или экономит деньги, то с точки зрения бизнеса лучше сначала в продакшен, а потом думать над оптимизациями. Вот сейчас сижу и думаю, насколько повысится производительность от смены Rest-like на GraphQL, сколько времени это займёт, а деньги бизнесу от Rest уже идут.

© Habrahabr.ru