Пост был написан 2015 году. Некоторые примеры, например работа с жёстком диском, стали менее критичными из-за доступной стоимости и надёжности SSD. Так же OOP и DDD сейчас не так популярны. Но основная идея стала еще более актуальна. Изучение механики работы процессора, памяти, ядра, ОС и протоколов помогает писать быстрые и эффективные программы.
Сейчас повсеместно присутствует еще несколько уровней абстракции - виртуализация, контейнеры и оркестраторы(kubernetes и пр.). Понимания как устроены и работают несколько уровней абстракций под вашим приложением и над ним делает вас более лучшим разработчиком.
Знаменитый шотландский гонщик Джек Стюарт, победитель всех гонок Formula-1 1965-1973 годов, назвал одну из причин своего успеха:
You don’t have to be an engineer to be a racing driver, but you do have to have mechanical sympathy.
Мартин Томпсон был один из первых кто озвучил в IT мире термин Mechanical Sympathy.
Hardware and software working together in harmony
Перевод Mechanical Sympathy: Understanding the Hardware Makes You a Better Developer
Последние несколько лет своей карьеры я посвятил работе в области высокопроизводительных систем с низкой задержкой. Два наблюдения поразили меня, когда я вернулся к работе над железом:
- Многие из предположений, которые делают программисты за пределами этой эзотерической области, просто неверны.
- Уроки, извлеченные из высокопроизводительных систем с низкой задержкой, применимы ко многим другим задачам.
Некоторые из этих предположений довольно просты. Например, что быстрее: запись на диск или запись в кластерную пару в сети? Если вы, как и я, выжили в программировании конца XX века, вы знаете, что сеть медленнее диска, но на самом деле это неверно! Оказывается, гораздо эффективнее использовать кластеризацию в качестве механизма резервного копирования, чем сохранять все на диск.
Другие предположения носят более общий характер и наносят больший вред. В нашей индустрии распространен мем “избегайте предварительной оптимизации”. Я сам когда-то проповедовал это, и теперь очень жалею об этом. Это лишь несколько якобы очевидных принципов, которые ставят под сомнение действительно высокопроизводительные системы. Для многих разработчиков эти правила кажутся достаточно незыблемыми в повседневной практике. Но по мере того как требования к производительности становятся все более жесткими, разработчикам становится важно понимать, как именно работают системы - как на абстрактном, процедурном уровне, так и на уровне самого железа.
МОТИВАЦИЯ: ПОСЛЕДСТВИЯ НЕЭФФЕКТИВНОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
Для начала давайте подумаем, почему важно отказаться от грубых упрощений вроде “дисковый ввод-вывод быстрее сетевого”.
Одна из мотиваций - немного негативная. Я не могу представить ни одну другую сферу человеческой деятельности, которая бы терпела такой уровень неэффективности, как в нашей индустрии. Мой опыт показывает, что для большинства систем любой специалист по производительности может довольно легко повысить их производительность в десять раз. Это происходит потому, что большинство приложений в сотни, тысячи и даже десятки тысяч раз менее эффективны, чем могли бы быть. Современное оборудование обладает феноменальными возможностями, но мы, программисты, регулярно недооцениваем успехи, достигнутые производителями аппаратного обеспечения. В результате мы часто не используем новые возможности аппаратуры и пропускаем значительные узкие места в производительности.
Вы можете сказать, что это неважно - в конце концов, для чего нужна вся эта аппаратная производительность, если не для того, чтобы было легче писать программы? И тогда я отвечу: она нужна для многих вещей. Вот одна очень конкретная причина. Потребление энергии дата-центрами составляет значительную долю CO2, выбрасываемого в нашу атмосферу. Если большинство программного обеспечения более чем в 10 раз менее эффективно, чем могло бы быть, то это может означать 10-кратное увеличение выбросов CO2. Это также означает 10-кратное увеличение капитальных затрат на оборудование, необходимое для работы всего этого неэффективного программного обеспечения.
Возможно, еще важнее то, что производительность - это не просто эффективность ради нее самой. Производительность также является важным фактором, способствующим инновациям. Возможность уместить данные о 1000 песен на крошечном жестком диске iPod первого поколения позволила Apple совершить революцию в музыкальной индустрии. Еще более простой пример: графические пользовательские интерфейсы стали возможны только тогда, когда аппаратное обеспечение стало достаточно быстрым, чтобы “тратить” все это время на рисование красивых картинок. Высокопроизводительное оборудование позволило внедрить эти инновации; программное обеспечение просто должно было последовать за ним.
А еще есть цена возможностей. Что мы могли бы делать со всеми свободными мощностями нашего поразительно быстрого оборудования и огромных хранилищ, если бы не тратили их на неэффективное программное обеспечение?
В тех областях, где я работал, идея Mechanical Sympathy очень помогла. Давайте рассмотрим эту идею поближе.
КЛЮЧЕВАЯ КОНЦЕПЦИЯ: MECHANICAL SYMPATHY
Термин “Mechanical Sympathy” был придуман гонщиком Джеки Стюартом и применен к программному обеспечению Мартином Томпсоном. Джеки Стюарт сказал: “Вам не надо быть инженером, чтобы быть хорошим гонщиком, достаточно просто чувствовать машину”. Он имел в виду, что понимание того, как работает машина, делает вас лучшим гонщиком. Это так же верно и для написания кода. Вам не нужно быть инженером по аппаратному обеспечению, но вы должны понимать, как работает оборудование, и принимать это во внимание при разработке программного обеспечения.
Возьмем такую простую вещь, как запись файла на диск. Диски имеют случайный доступ, верно? На самом деле, нет. Диски работают, кодируя данные в секторах и адресуя их в сегменты диска. Когда вы считываете или записываете данные на диск, вам нужно дождаться, пока головки переместятся в нужное физическое место. Если делать это случайным образом, то производительность будет снижена, поскольку головки физически перемещаются по поверхности диска, а вы ждете, пока вращение диска поместит нужный сектор под головки чтения/записи. В среднем это составляет около 3 мс на перемещение головок и около 3 мс задержки при вращении. Таким образом, общее среднее время поиска составляет около 6 мс!
Есть и еще более конкретные причины, по которым “случайный доступ” - это плохое эмпирическое правило. На самом деле, современные диски оптимизированы для потоковой передачи данных, чтобы они могли воспроизводить фильмы и аудио. Если вы поймете это и будете относиться к файловому хранилищу как к последовательному, блочному устройству, а не как к устройству со случайным доступом, вы получите значительно более высокую производительность. В данном примере разница может составлять два порядка (200 МБ/с - хорошая рабочая цифра для верхней границы).
ДОСТИЖЕНИЕ ВЫСОКОЙ ПРОИЗВОДИТЕЛЬНОСТИ: СТАВЬТЕ ЭКСПЕРИМЕНТЫ И СЧИТАЙТЕ.
Абстракция - это важно. Важно, чтобы мы были в какой-то степени изолированы от сложности устройств и систем, которые образуют платформу, на которой выполняется наше программное обеспечение. Однако многие абстракции, которые мы считаем само собой разумеющимися, очень плохи и дырявы. Общая сложность системы увеличивается, когда мы строим абстракцию поверх абстракции, чтобы скрыть проблемы, вызванные утечками, что приводит к резкой разнице в производительности между высокопроизводительными и “обычными” системами.
Вам не нужно моделировать или измерять всю систему, чтобы справиться с ее сложностью. Для начала нужно провести несколько экспериментов, чтобы понять теоретические максимумы. У вас должна быть примерная модель следующего:
- Сколько данных вы можете записать на диск за одну секунду;
- Сколько сообщений ваш код может обработать за одну секунду;
- Сколько данных вы можете отправить или получить по сети за одну секунду.
Идея заключается в том, чтобы измерить фактическую производительность по сравнению с теоретически возможной производительностью системы в рамках ограничений, накладываемых базовым оборудованием.
Давайте рассмотрим конкретный пример. Недавно я разговаривал с разработчиком, который считал, что работает над высокопроизводительной системой. Он сказал мне, что его система работает со скоростью 10 транзакций в секунду. По состоянию на середину 2015 года современные процессоры на коммодити аппаратном обеспечении могут легко выполнять 2-3 миллиарда инструкций в секунду. Таким образом, при 2-3 миллиардах инструкций в секунду для достижения своих целей разработчик был ограничен 200-300 миллионами инструкций на транзакцию ([10 транзакций/сек] / [2-3G инструкций/сек] = 200-300m инструкций/транзакция). Конечно, его приложение отвечает не за все эти операции - операционная система тоже отнимает часть циклов, - но 200 миллионов инструкций - это ужасно много работы. Как показал Дэвид Хорн, эффективного игрока в шахматы можно написать на 672 байтах.
Ладно, возможно, узким местом был ввод-вывод. Возможно, приложение этого разработчика было привязано к диску. Это маловероятно - современные жесткие диски, устанавливаемые в обычное оборудование, могут передавать данные с феноменальной скоростью. Умеренная скорость передачи данных с диска в буфер в памяти составляет около 10 МБ/с. Если это предел, то на каждую транзакцию между диском и памятью должно уходить более 10 МБ. В 10 миллионах байт можно уместить очень много информации!
Возможно, проблема заключалась в сети. Скорее всего нет, cеть 10 Гбит/с может передавать примерно 1 ГБ данных в секунду. Это означает, что каждая из 10 транзакций нашего непутевого разработчика в секунду занимает 1/10 часть ГБ, или примерно 100 МБ - более чем в 10 раз больше, чем пропускная способность наших дисков!
Проблема была не в аппаратном обеспечении.
ВЫСОКОПРОИЗВОДИТЕЛЬНАЯ ПРОСТОТА I: МОДЕЛИРОВАНИЕ ПРЕДМЕТНОЙ ОБЛАСТИ
Один из самых распространенных мифов о высокопроизводительном программировании заключается в том, что высокопроизводительные решения сложнее, чем “обычные”. По определению, высокопроизводительное решение должно выполнять наибольший объем работы за наименьшее количество инструкций. Сложные решения точно не продержатся долго в высокопроизводительных системах, потому что сложность - это место, где скрываются узкие места производительности.
Лучшие программисты, которых я знаю, добиваются простоты, необходимой для высокопроизводительных систем, путем моделирования области с которой они работают. Программное симулирование предметной области - лучший способ выработать элегантное решение.
ВЫСОКОПРОИЗВОДИТЕЛЬНАЯ ПРОСТОТА II: УПРОЩЕНИЕ КОДА
Если вы следуете за предметной областью, то в итоге получаете небольшие, простые классы с более четкими связями. Если вы придерживаетесь принципа разделения обязанностей как главного принципа, то ваш дизайн будет толкать вас в направлении более чистого и простого кода.
Современные компиляторы чрезвычайно эффективно оптимизируют код, но лучше всего они справляются с оптимизацией простого кода. Если вы напишете 300-строчные методы с множеством for-циклов, каждый из которых содержит несколько вложенных if-условий, бросающих исключения повсюду и возвращающихся из нескольких точек, то оптимизатор просто сдастся. Если вы пишете небольшие, простые, легко читаемые методы, их легче тестировать и легче понять оптимизатору, что приводит к значительному повышению производительности.
Один из самых распространенных мифов о высокопроизводительном программировании заключается в том, что высокопроизводительные решения сложнее, чем “обычные”. Это просто неправда.
КЛЮЧ К ВЫСОКОЙ ПРОИЗВОДИТЕЛЬНОСТИ: МИНИМИЗАЦИЯ ИНСТРУКЦИЙ, МИНИМИЗАЦИЯ ДАННЫХ
В основе разработки высокопроизводительных систем лежит простая задача: минимизировать количество обрабатываемых инструкций и объем перемещаемых данных.
Этого можно достичь, моделируя предметную область и устраняя ненужную сложность. “Mechanical Sympathy” поможет понять, где вы делаете что-то не так. Она также может указать вам направление для более точных измерений и профилирования, которые помогут вам разобраться, почему ваш код не работает на теоретическом максимуме. Так почему бы не попробовать сэкономить деньги своей компании, поработать с упрощенной кодовой базой и, возможно, помочь уменьшить углеродный след наших дата-центров - и все это в один момент?
Комментарии в Telegram-группе!