Медленный код, зато красивый — вообще не проблема, пока ты знаешь, как его ускорить

mloltfdej3smwbfwex0yenx5e9c.png

Я много раз слышал, как программисты смеются над тиммейтами, которые написали медленный код. Резкие, самодовольные фразы в стиле «этот болван четыре раза пробежался по коллекции, хотя можно было один», и тому подобное. Когда слышишь такое, сразу думаешь — ну тут все по делу, зачем делать лишние итерации? Почему нельзя изучить пару элементарных вещей, вроде принципов работы LINQ выражений в C#, и писать нормальный код? Ты смеешься над некомпетентными тупицами до тех пор, пока смеяться не начнут над тобой. И можете мне поверить — никакие знания в программировании не спасут вас от ситуации, когда вы по незнанию зафигачили квадратичный алгоритм вместо линейного.

Я не знаю, смеялся ли кто-то над моим кодом, но я точно уверен — не раз и не два я писал решение, которое было в сто раз медленнее, чем могло бы быть. Но в этих случаях я сознательно писал неоптимально. Например в C# есть цикл for, и есть метод коллекции Select (в других япах он чаще называется map). Цикл быстрее, но я считаю функциональный подход более эстетичным — и использую его. Я знаю, что данных мало, тут не будет ботлнека, и пишу код, который по-моему красивей, читабельней и потому лучше. Таких решений при разработке очень много — и я выбираю писать производительный код только когда точно знаю, что иначе просадки станут проблемой.

Есть очень много разрабов, которые делают по-другому, и искренне считают, что такие как я — дерьмовые инженеры. У меня есть друг, опытный разраб высокого класса, который на днях выпнул человека с собеса, потому что тот предоставил медленное решение. Задача была — реализовать специфичный LINQ метод. В C# эти методы следует делать ленивыми — через итераторы и yeild —, а парень просто зафигачил новый список, и стал пихать в него значения, чем сломал ленивость. На больших срезах данных это может создать крупные — и главное неожиданные — просадки в производительности, достаточные, чтобы на доске завелся баг. По большому счету, тут дело даже не в снижении производительности, а в перемещении ботлнека в неожиданное место — не самая приятная хрень.

Я бы никогда не поставил крест на разработчике из-за такого, потому что сам не заморачиваюсь с yield, пока данных не становится по-настоящему много. С новым листом сделать интуитивно проще, принципы работы LINQ я хорошо понимаю. У меня нет задачи писать какой-то совершенный, абсолютно отказоустойчивый код заранее, и даже тогда, когда мне оптимизация ничего не стоит, я не всегда её применю. И я такой не один. В майкрософтской доке по LINQ пример добавления своего метода предложен именно с новым списком — потому что у создателя примера были дела поважнее, чем возиться с итераторами.

Мы позвали в подкаст Андрея Акиньшина — человека, который действительно разбирается в производительности. Создатель BenchmarkDotNet, автор книги Pro .NET Benchmarking, топовый докладчик на темы производительности, поведения рантайма и бенчмаркинга в дотнете, и просто очень, очень, очень крутой инженер. Мой друг, который прогнал человека с собеса — слепой котеночек в вопросах перформанса по сравнению с ним.


Знаете, что нам сказал Андрей? Что эстетика важнее производительности, пока у тебя нет ботлнеков.

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

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

И эти спичечники считают, что они крутые парни, а все остальные — вшивые говноделы. Мне есть что им сказать. Если ты взял for вместо select, хотя оба варианта нас устраивают по скорости, то почему ты тогда остановился на этом? Какой ещё C#, братан, C# — это медленно. Давай возьмем C++. А лучше ассемблер. Нет, стоп, ассемблер неоптимален — давай сразу фигачить машинные коды. Эстетика, поддерживаемость, читабельность — это же все херня собачья. Важно, чтобы все четыре элемента связного списка были выведены на экран наиболее быстрым способом.

Но машинные коды мы не хотим, потому что на них невозможно делать большие и сложные проекты. Мы хотим приятный, удобный и понятный C#. Его создатели разрулили сто тысяч трейдофов за нас. Мы получили дофига плюшек — надежность, поддерживаемость, инструменты для переиспользования кода — и заплатили за это производительностью. И наши приложения все ещё достаточно быстры, чтобы не думать об этом.


Хорошо, когда скорость и красивость не противоречат друг другу. Если по условиям задачи тебе больше подходит Queue, а не List — взять очередь будет и эстетичнее, и производительнее. Но люди, которым совершенно не интересно быстродействие, никогда об этом не узнают. Вы легко отличите их шарповый код — там везде будут листы.

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

Все эти понятия — «преждевременные оптимизации», «экономия на спичках» — создают иллюзию, что оптимизации — штука неважная, и поэтому разрабы могут их не изучать. Такая иллюзия опасна, ведь изучать тонкости рантаймов, устройства ЯПов, алгоритмы и структуры данных — долго и сложно. И если ты выращиваешь поколение разработчиков, которые это не делали — ты получаешь отвратительную инженерную культуру, ужасные, супер медленные инструменты и приложения. И людей, которые в сто раз опаснее спичечников.

За примерами далеко ходить не надо — достаточно просто посмотреть на современных фронтендеров. Большая часть из них, когда увидят «производительность, C#, LINQ» захлопнут эту статью, и не дочитают до этого места. Так что теперь мы можем поговорить, что у них нет никакой инженерной культуры. Они делают самые тормозные вещи в мире — вроде всяких электронов — у них в языке из коробки всего два вида коллекций — они про другие и знать не знают. Их инструменты для билдов и управления пакетами работают так медленно, что после ввода «yarn start» можно смело идти смотреть сериал. Итоговые файлы, которые получаются после компиляции весят раз в сто больше, чем должны были. Создается эффект снежного кома. Как теперь не переучивай их, какую культуру не прививай — с большинством проблем фронтенд инфраструктуры особо ничего и не сделаешь — если только не переписать её с нуля.

Мы не можем забить на производительность совсем — это крайность. Но и сказать, что все должны полностью разбираться в ней разбираться, мы тоже не можем. Вопросов перфоманса — бесконечность. Да, окей, я могу запомнить, какие структуры данных для чего подходят, усвоить, какие оптимизации умеет делать конкретный компилятор, и как не наступить в общеизвестные грабли в конкретном ЯПе. Но это самая верхушечка гигантского айсберга. И в мире нет человека, который бы продвинулся в этом направлении достаточно, чтобы с уверенностью писать код без потенциальных проблем с быстродействием. И вот поэтому я говорю про инженерную культуру.


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

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


Смотрите мой подкаст

© Habrahabr.ru