Термин «Mechanical Sympathy» ввел в обиход программистов разработчик Мартин Томпсон. Взял он его из мемуаров знаменитого шотландского гонщика Джеки Стюарта (Jackie Stewart) 60-ых годов, неизменного победителя всех гонок 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.Вам не надо быть инженером, чтобы быть хорошим гонщиком, достаточно просто чувствовать машину.
Казалось бы в гонках главное — это скорость. Жми на педаль, не жалей машину и гони ее к финишу на полном форсаже. Однако после такой гонки машину можно сдавать в утиль. Эффективным гонщиком считается водитель, способный выиграть гонку или хотя бы довести машину до финиша и при этом не убить дорогой аппарат. Понимание того, как работает машина в целом и как работают ее узлы в частностях, помогают хорошему гонщику вести автомобиль на гоночной скорости, чувствовать, когда она работает на последнем пределе и не изнашивать ее механизмы без особой надобности.
Какое отношение это все имеет к программированию?
Нет нужды пересказывать всё, что было уже сказано в многочисленных презентациях Мартина Томпсона, который даже свой блог назвал Mechanical Sympathy. Последние записи в блоге, увы, датированы 2013 годом, но статьи, опубликованные в нем, по-прежнему актуальны и интересны.
Mechanical sympathy упоминается и в книге Optimizing Java, что естественно. Невозможно оптимизировать Java-приложение, не понимая механику JVM, сборщика мусора, операционной системы и так далее.
Данная статья является лишь моим скромным пересказом идеи mechanical sympathy.
Мы все дальше от железа, и ближе к абстракции
Для обычного, рядового программирования достаточно знать язык и методы разработки ПО. Современные языки программирования высокого уровня абстрагируют для программиста железо и операционную систему. В этом их главное назначение. Они потому и называются языками высокого уровня. Высокого уровня абстракции от конкретного аппаратного обеспечения. С наступлением облачных сервисов виртуализация и облака еще больше отдаляют нас от железа.
Раньше, когда писали в машинных кодах, программист скрупулезно отсчитывал, куда какой байт в какой регистр или ячейку памяти записывается и сколько времени на это уйдет. Объемы памяти исчислялись килобайтами. Тактовые частоты — килогерцами. А труд программиста — литрами пота и месяцами отладки кода.
Сейчас область ПО стала шире и для большинства случаев нет никакой разницы: миллисекундой меньше, миллисекундой больше будет исполняться участок кода. Если скорость работы программы в вашем проекте не приоритетна и падение производительности на несколько миллисекунд — не критично, вам может быть и не нужно читать эту статью. Но я считаю, что все же полезно.
Судите сами, современные процессоры работают на частотах близких к 3 ГГц. То есть три машинных такта за наносекунду! В теории это исполнение трёх машинных инструкций в наносекунду. За одну микросекунду — 3 тысячи инструкций. За одну миллисекунду — 3 миллиона инструкций. Если у вашей программы уходит 1 миллисекунда на выполнение, скажем, поиска в базе данных ключевого слова, значит ли это, что за эту миллисекунду исполнилось приблизительно 3 миллиона инструкций? Нет. Большую часть времени в эту миллисекунду процессор работал в холостую, ожидая поступления данных из памяти, записи данных в память, на диск или в сеть. То есть вся мощь данная вам в руки развитием технологии за последние 60 лет, тратится на простои, ожидания, в пустую, в пар, в свисток. Как верна цитата о том, что «программисты успешно нивелируют все достижения в аппаратном обеспечении написанием все более неэффективных и неповоротливых программ».
The most amazing achievement of the computer software industry is its continuing cancellation of the steady and staggering gains made by the computer hardware industry. Henry Petroski
Казалось бы, ну что уж тут поделаешь. Аппарат ныне дешев. Добавим еще больше процессоров, отмасштабируем, компилятор нам сам автоматически все соптимизирует. Но это ведь безумное расточительство!
Почему бы не попробовать писать такой код, который использовал бы все элементы аппаратной платформы эффективно, не давая ни одному элементу работать в холостую, простаивать в пустую.
Производительности современных процессоров вполне достаточно для большинства нужд человечества. Если вам кажется, что процессор тормозит и делает что-то медленно, это значит, что программа, исполняемая на нем, написана неэффективно, без понимания механики процессора.
Дальше от абстракции, ближе к железу
Программирование финансовых торговых систем — это особая область программирования, где высоко ценятся эффективные, быстрые, производительные программы именно потому что поговорка «время — деньги» приобретает буквальный смысл.
Чтобы писать такие программы надо понимать, что происходит «под капотом» на нижних этажах-уровнях компьютерной системы, где исполняется ваша программа. От вас, конечно, не требуется быть уникальным инженером микропроцессорной техники или системным гуру-программистом операционных систем. Вы должны просто знать, из-за чего один код будет исполняться компьютером медленно, а другой — быстро. То есть надо писать код, чувствуя машину, с любовью к машине, испытывая mechanical sympathy к ней.
Кишочки JVM
Книги по оптимизации Java-приложений
Устройство операционных систем
Как устроена операционная система, переключение контекстов, как работает виртуальная память, планировщик процессов, какие утилиты используются для замера производительности и мониторинга процесса.
Устройство аппаратной системы
В качестве отправной точки могу посоветовать свои статьи «О железе» и о сетевом оборудовании.
Устройство микропроцессоров
Программист должен интересоваться, что под капотом у его компьютера, знать, как работает процессор, иерархия кэшей, система памяти, многопроцессорные системы, аппаратное взаимодействие процессора с сетью. Как эволюционируют процессоры. Чем принципиально отличаются семейства процессоров друг от друга. Как к решению одной и той же проблемы подходят Intel, AMD, Oracle, IBM, ARM.
Непременно посоветую вам статью Бесплатного супа больше не будет. Вкратце, чтобы программа считала быстрее, бессмысленно ждать более быстрого процессора. Время гонки гигагерцов прошло, гонка уперлась в стенку. Теперь производительность процессоров растет за счет роста количества вычислительных ядер, роста объема кэша процессоров и более тесной интеграции всех его элементов благодаря всё более уменьшающемуся техпроцессу. Следовательно, чтобы программа была быстрее она должна использовать все механизмы современного процессора: распараллеливаться на множество ядер процессора, быть cache-friendly, быть pipeline-friendly. Для того чтобы написать такую программу надо знать как устроен процессор, значит — опять статья про книги о «железе».
Вывод
Секрет состоит в том, чтобы изучить, как устроено, то с чем вы имеете дело, и понять, как быстро оно в теории может работать. Если на практике оказывается, что ваше приложение работает медленнее теоретических пределов, значит вы не до конца используете резервы системы, либо используете их неверно, т.е. не в том режиме, в каком ваша система может выдать максимальный результат.
Писать простые рядовые программы можно и без знания механики процессоров, операционных систем и протоколов. Но чтобы писать быстрые и эффективные программы, надо понимать, что происходит у машины внутри. Нужно почувствовать ее.
P.S. 2022.10.22: Очень рекомендую к просмотру фильм «Форд против Феррари«. Там прекрасно показано, как именно гонщик должен «чувствовать машину», чтобы выжать из нее все, не покалечив ее, и выиграть гонку.