[Перевод] Пишем медленный код на Go
Почему Вы должны оптимизировать читаемость, а не производительность
Подождите, что? Медленный код? Разве мы не должны беспокоиться об ускорении наших Go-программ?
На самом деле, нет. Оптимизация кода на Golang ради производительности — это попросту трата времени, и вот почему:
1. Производительность в большинстве случаев не имеет значения
2. Go и так быстрый
3. Читаемость важнее скорости
Эти аргументы нуждаются в объяснении, и я его дам. Для них есть исключения, как, собственно говоря, для всех нетривиальных утверждений. Честно говоря, стоит сказать, что эти 3 пункта вряд ли являются компромиссом среди программистов-инженеров. Так что, прежде чем начать снижать мне рейтинг и писать негативные комментарии («Худшая статья на Хабре»), прочитайте до конца.
Производительность не имеет веского значения
Действительно, для большинства программ, которые многие из нас делают, не критично важна скорость. Например, вот несколько программ, над которыми я недавно работал:
Проверка ссылок на сайты (поиск битых ссылок)
Провайдер Terraform
Анализатор данных (скачивает и высчитывает таблицу статистики по данным мониторинга)
Сервис миграции сайтов (перемещает файлы и базы данных веб-сайта между серверами)
Все эти программы запускаются относительно редко (возможно, 1 или 2 раза в день) и требуют для своего выполнения мало времени. Стоит ли тратить какие-либо усилия для их ускорения? Конечно же нет.
Медленный не Go
Даже в тех случаях, когда мы беспокоимся о скорости программы, мы не будем ускорять Go. Например, проверка ссылок тратит 99% времени на ожидание HTTP-запросов. Провайдер Terraform наибольшее количество времени тратит на «разговоры» с remote API. А слабым местом сервиса миграции является перемещение тяжелых tar-архивов по сети.
Так что, если надо увеличить производительность каких-либо программ, мы не будем этого делать на уровне Go. Процессоры невероятно быстры по сравнению с доступом к оперативной памяти, который намного быстрее доступа к жесткому диску, который намного быстрее сетевого доступа. Поэтому скорость выполнения кода — последнее, за что мы будем беспокоиться.
Когда нужно считать каждую ns/op
«Подожди-ка, я создаю 3D игру на Go, и у меня невероятно ограничено количество кадров в секунду. Каждая наносекунда на счету, а ты говоришь, что мне не стоит беспокоиться об оптимизации???!!!», — слышу я.
Нет. Игры являются исключением из этого правила, так как некоторые небольшие части кода (например, шейдеры) критичны к скорости. Заметьте, что даже тут мы ввели оговорку: производительность важна только для маленьких частей кода. И только если Вы не пишете 3D движок с нуля (не делайте так), Ваш код вряд ли станет слабым местом в отрисовке кадра.
Вы вряд ли будете писать код для спутников (наверное)
Но большинство программ на Go — не игры. Есть ли другие исключения? Есть: веб-серверы и API. Они имеют критически нуждающиеся в производительности разделы. В большой, сложной распределенной системе общая задержка запроса важна. В разных подсистемах задержка может достигать наносекунды.
И, конечно, существует много разных специализированных и встраиваемых систем, где скорость имеет значение, например: драйверы ввода-вывода, спутники и промышленные системы управления. Я допускаю все эти исключения, но утверждаю, что эти задачи не типичны для Go.
Когда производительность ну очень критична
Если производительность действительно критична для Вашей программы, то не тратьте время на настройку и доработку Go: просто используйте другой ЯП. C++ довольно хорош.
Читаемость важнее скорости
Большинство программ уделяют гораздо больше времени чтению их программистами, чем выполнению. А время программиста намного дороже, чем процессора (удачи найти разработчика за 5 центов в час). Так разве не имеет смысл оптимизировать программу в читаемости, а не скорости?)
Мы бы конечно хотели быстрые и при этом читаемые программы, и это зачастую возможно. Но когда программист начинает говорить «Я бы мог сделать это на несколько наносекунд быстрее, используя используя массив вместо словаря и вычисляя индекс с помощью младших битов ключа!», важно подумать о том, что мы отдаём взамен на наносекунды.
Пишите больше хорошего кода, меньше комментариев
Нужно, что бы практически любой Go-программист смог заглянуть в любую функцию из Вашего кода и понять то, зачем нужен этот код и как он работает. Говоря это, я не имею ввиду, что Вы должны заполонить свой код комментариями. Их стоит писать только когда сделали все возможное для упрощения кода, но он все равно не понятен, можно сказать, что в этот момент Вы признаете поражение. «Я знаю, что ты не поймешь то, что я написал, поэтому вот моя попытка объяснить это проще».
Да, признаю, что сказать намного легче, чем сделать. Писать простой и понятный код сложно. Во-первых, для того, чтобы кому-либо объяснить концепцию, нужно разобраться в ней самому. Для того, чтобы объяснить это компьютеру, нужно знать это вдоль и поперек.
Вывод: Пиши медленный код, не гонись за скоростью
Естественно, я не выступаю за то, что нужно делать программы излишне медленными. Я говорю о том, что, во-первых, не нужно так сильно гнаться за производительностью, как Вы вероятно думаете. Зачастую, когда Вы пытаетесь ускорить свой код, существуют более простые и лучшие способы улучшить его, чем написание запутанного и хитроумного алгоритма.
Во-вторых, нужно больше беспокоиться за простоту и читаемость кода, чем, Вы, возможно, беспокоитесь сейчас. Когда у Вас появляется мысль оптимизировать код, попробуйте потратить час на улучшение его читаемости.
В-третьих, будьте готовы на жертвы ради улучшения читаемости. Если Вы можете провести рефакторинг своей программы, чтобы сделать ее значительно понятнее и легче за счет того, что сделаете ее немного медленнее — ДЕЛАЙТЕ ЭТО. Если производительность действительно важна, помните, что она не дается бесплатно. Когда Вы подумываете об усложнении своего кода в погоне за скоростью, подумайте еще раз. Вы всегда можете купить более быстрое устройство, но вряд-ли купите программистов, которые будут понимать сложный код.
«Junior разработчик: Мой код простой и понятный.
Middle разработчик: Мой код гениальный, быстрый, инновационный.
Senior разработчик: Мой код простой и понятный.» — John Arundel (@bitfield) January 20, 2020