Не так давно у нас на работе (Виктор, Игорь, Олег привет вам) состоялась дискуссия на тему: должны ли программисты знать как работает железо на котором выполняются их программы? И я еще раз убедился в том, что большинство программистов придерживаются мнения: "железо само по себе, а я сам по себе". Точка зрения вполне ожидаемая и ничего принципиально неправильного в ней нет, но я хотел бы "копнуть глубже".
Вообще, мне кажется вопрос "стоит ли программистам изучать железо" в чем-то похож на вопрос "стоит ли программистам изучать математику", — ответ неоднозначен. Существует множество вопросов, ответы на которые могут повлиять на окончательное решение: в какой предметной области вы работаете, насколько эти конкретные знания ценны для вас и т.д. Это вопрос инвестиций и прибыли. В данном случае под инвестициями подразумевается в первую очередь ваше свободное время, а под прибылью — профессиональные навыки и знания, которые позволят вам более эффективно делать вашу работу. Тогда вопрос сводится к следующему: чем изучение железа может помочь нам программистам делать свою работу лучше?
Мы с вами живем в эпоху высокоуровневых языков. Программисты большинства отраслей с успехом забыли про unmanaged языки, не говоря уже об ассемблерных вставках. В этом есть свои преимущества. Во-первых, это позволило нам программистам быть более эффективными. При равных доступных ресурсах теперь мы можем решать более сложные задачи. Во-вторых, не требуя от программистов "погружения в железо", мы позволяем им специализироваться в своей области, а также увеличить их количество за счет более короткой learning curve. Последнее впрочем имеет и свои негативные последствия, но я не буду сейчас акцентировать на них внимание.
И все же знание железа может быть полезно. Но позвольте мне сразу оговорится, я не считаю что результатом погружения в hardware должен стать код более быстрый с точки зрения машины. Да, иногда это является самоцелью и приходится экономить буквально говоря миллисекунды, но в текущем контексте я хочу акцентировать внимание на другом аспекте.
Почему стоит изучать?
Как бы программистам не хотелось думать, что они никоим образом не зависят от аппаратной платформы, — это не так. Да, компиляторы, виртуальные машины и операционные системы перевели среднестатистического программиста на "дальнюю орбиту" в аппаратных вопросах. Но железо все же влияет на то, какими абстракциями мы пользуемся для написания программ. Просто мы не всегда это замечаем.
Возьмите хотя бы возросший интерес, к actor'ам. В общем и целом actor'ы представляют собой модель кооперативной многозадачности. Если вы хорошо помните историю, то кооперативная многозадачность в широких масштабах последний раз "упоминалась" в начале 90-х. У нее есть один существующий минус — она не работает на маленьком количестве исполнителей. Когда у вас один процессор легко может случится так, что какая-либо задача оккупирует его и другие задачи не смогут выполнятся. Тем не менее, у кооперативной многозадачности есть плюсы которые заставили нас опять обратить свой взор на эту модель...
Дело в том, что основная проблема с которой сталкиваются современные модели распараллеливания (которые преимущественно основаны на потоках) — это context switch. Доступ к памяти не является random'ным, чтобы ни говорили разработчики железа. Latency доступа к памяти на современных платформах составляет сотни тактов процессора. За это время процессор может сделать много работы. Эту проблему сейчас адресуют довольно простым путем — кеш, размер которого уже достигает 2Mb и больше на ядро. Проблемы кеша вам уже должны быть известны, — они одни и те же, неважно говорим ли мы о CPU cache или о memcached. Как только вы получаете промах кеша, вы платите performance penalty. Именно cache miss'ы являются источником деградации производительности в случае высокого context switching'а. Об этом говорило очень много умных дядек.
Решение довольно очевидно. Не переключатся между задачами лишний раз, только после завершения всей задачи целиком. Здравствуй кооперативная многозадачность. Не верите мне, поверьте сотрудникам Яндекса. Не зря в Windows существуют fiber'ы.
Справедливости ради, следует заметить, что скорость не единственный (и лично для меня не главный) плюс модели actor'ов. Эта модель гораздо проще в понимании и тестировании чем традиционные thread-based приложения. Но интерес к этой модели начал появляться как раз в тот момент, когда традиционные способы увеличения производительности исчерпали себя и мы начали искать новые методы обеспечения роста. А это означает одну простую вещь — программные абстракции используемые нами меняются в том числе и под "давлением аппаратных факторов".
У каждого технического решения есть свои причины влияющие на его формирование. Не всегда эти причины лежат непосредственно в области программной инженерии. Знание аппаратных факторов дает программисту более полную картину того, почему мир в котором мы живем таков каков он есть. Это основная идея, которую на мой взгляд не обосновано игнорирует большинство программистов.
Никто не просит вас уметь программировать на ассемблере и разбираться в opcode'ах микропроцессора. Но осознание того как работает процессор и какие bottleneck'и есть у существующих аппаратных платформ может дать вам очень хороший теоретический background для решения многих задач и ответа на многие вопросы.
Root of all evil
Иногда можно встретить мнение что изучение железа плохо уже потому что человек изучающий железо приобретает некоторую зашоренность мышления и начинает оценивать решения лишь с точки зрения их аппаратной производительности.
Верная предпосылка, но абсолютно не рациональный вывод. Это не проблема знаний, это проблема человека использующего эти знания. Чувство меры и здравоумие ни в коей мере не должно вас покидать, какие бы знания вы не получали.
Tradeoffs
С другой стороны надо трезво оценивать свои возможности. Существует масса знаний/умений которые для программиста важнее чем знание железа. Вот, на мой взгляд, всего лишь некоторые из них (перечислено в произвольном порядке):
- ОО анализ и проектирование (шаблоны/SOLID принципы);
- анализ и построение алгоритмов;
- классические структуры данных;
- диагностика типичных проблемных ситуаций;
- тестирование и поддержка legacy систем;
- умение писать корректный многопоточный код, а также находить ошибки в многопоточном коде (светлое будущее с Clojure, Erlang, Scala, whatever еще не наступило);
- функциональная декомпозиция;
- автоматизация процесса разработки;
- продолжите список...
Если вы чувствуете, что есть важные знания из области программной инженерии которые для вас на данный момент важнее чем знание железа, то выбор очевиден. В первую очередь мы должны развиваться в тех направлениях которые диктует нам наша сфера деятельности.
В итоге
Саморазвитие, как это ни странно, требует жертв. Иногда приходится тратить время на изучение вещей, изучать которые совсем не хочется. Лично для меня таким инструментом в свое время был git. Но, видимо, иногда для того чтобы стать лучше приходится "наступать своей песне на горло". Так что я верю, что рано или поздно, если вы хотите стать хорошим программистом, вам прийдется выйти из своего дома и посмотреть как живут ваши соседи.