Комментарии 50
int apply_to_page_range(struct mm_struct *mm, unsigned long addr,
…
unsigned long end = addr + size;
int err;
if (WARN_ON(addr >= end))
return -EINVAL;
Я бы больше типизации добавил для наглядности, да и во избежание множества проблем при развитии продукта и ошибок от невнимательности (хотя при 100% тестовом покрытии второе не так критично)
result_t apply_to_page_range(struct mm_struct *mm, addr_t addr,
…
addr_t end = addr + size;
result_t err;
if (WARN_ON(addr >= end))
return -EINVAL;
Второй пример совершенного кода хотелось бы разобрать подробнее. Это код ядра Linux.Простите, но качество кода Linux весьма далеко от совершенного; приводить его в пример я бы точно не стал. Там неплохой код для промышленного проекта такого масштаба и круга разработчиков (включая множество компаний, в которых код драйверов и ядра пишут разномастные программисты на зарплате с различными отношением к своей работе и представлением о прекрасном), но не более того. Вот смотришь, например, на дифф (это фикс CVE-2017-14497) и видишь, что как был до фикса говнокод, таким после фикса и остался.
Как-то в одном интервью Костя Белоусов, один из ведущих разработчиков FreeBSD, хорошо сказал про код Linux:
После этого интересного опыта я уже даже подумывал бросить аспирантуру, и возможность “покрутить код” под Unix-like-системы выглядела интересно.
Но чтение кода Linux’ а было мне неприятно: я бы так не писал.
Я уже не помню детали, и в Linux’ е тот код, который вызвал у меня такое резкое неприятие, наверняка переписан уже несколько раз – нужно помнить, что это все происходило 12–15 лет тому назад.
Это вопросы масштаба. Как и любой другой код, код ядра содержит уязвимости разного кода. Уязвимости в коде не находят тогда, когда им не пользуются. Как и код Windows, код ядра распространен настолько широко и используется в настолько разных сценариях, что баги и уязвимости в нём находятся часто. Как сказал Глеб Жеглов, «Правопорядок в стране определяется не наличием воров, а умением властей их обезвреживать!».
Код ядра, подход коммюнити к разработке, подход к релизам (http://kroah.com/log/blog/2018/02/05/linux-kernel-release-model/) предоставляют всё, что нужно разработчикам, чтобы минимизировать количество подобных проблем.
procfs(5)
отключили по умолчанию из-за long history of security vulnerabilities and other problems, но в общем и целом, когда смотришь на фрёвые фиксы, почти всегда сразу понятно, в чем была проблема и как была пофикшена. В случае Linux зачастую приходится сидеть и раскуривать код (мне этим часто приходится заниматься, т.к. мы вынуждены поддерживать несколько версий ядра из разных веток, и вручную бэкпортить фиксы; постоянные изменения форматирования и переименования каких-нибудь констант, из-за чего оригинальный патч не прикладывается, — это отдельная грустная песня).Вот:
Качество кода — понятие субъективное, как я упомянул в начале статьи, у каждого разработчика свои примеры. Лично мне код ядра нравится и работать с ним было приятно, но дело не в этом. Этот код не идеален. Но это код, над которым работает, наверное, одно из самых больших и активных сообществ разработчиков. И это код, который, думаю, работает на наибольшем количестве устройств в мире. Я не могу с лету придумать другой пример кода, который работал бы в данный момент времени на сопоставимом количестве процессоров (я имею в виду процессоров в целом, всего, в мире), как код ядра Линукс. Это говорят о качестве кода и его сопровождаемости — плохой, некачественный код, который тяжело сопровождать, не получил бы такого распространения.
И другое дело — операционная система, которая «выстрелила», поскольку позволила закрыть целую нишу в системном ПО. Решения об использовании системы принимают все же не разработчики.
Но и как с какой-нибудь библиотекой — тут вопрос приоритетов:
1. она должна работать без багов,
2. она должна работать быстро
3. она должна быть хорошо написана
3 — это головная боль разработчиков этой библиотеки, т.к. качество кода сказывается на трудоемкости её развития, поддержки и тестирования для выполнения первых двух пунктов.
Так и с линуксом — я в его код не лезу и пока он развивается и работает — меня все устраивает.
Во многих компаниях разработчики вполне себе принимают решения какую систему использовать. Бизнесу зачастую не важно что там в технологическом стеке.
Быстро — это понятие относительное. С чем сравнивать? На каких задачах смотреть?
А вот хорошо написано — это не важно для ОС, но важно для библиотеки. Поскольку у библиотеки должен быть удобный и понятный интерфейс, грамотно выстроеная логика и удобство просмотра исходников (полностью или частично) сторонними программистами.
Хороший код — это не бизнес требование для ОС. Поэтому разработчики не должны давать советы, основанные на этом критерии бизнес-пользователям.
У Linux есть важные преимущества с точки зрения бизнеса, именно они обычно и влияют на принятие решения.
Хороший код — это не бизнес требование для ОС
С этим не поспоришь. Я имел ввиду выбор ОС как таковой. Например, под линукс проще собирать опен-сорс код, а под windows — головная боль, а многие проекты и не заработают вообще. Это уже аргумент бизнесу.
А насчет библиотеки — если потребовалось разбираться в исходном коде — это сигнализирует либо о недостаточно хорошей документации, либо обнаруженном баге. Ведь если api удобный и понятный, то в этом смысла нет, кроме как любопытство.
Насчет быстро (хотя лучше наверное эффективно, ведь надо брать в рассчет расход системных ресурсов тоже) — нужно рассматривать конкретную решаемую задачу. Например если библиотека читает данные из кафки, я ожидаю при среднем размере записи упереться в скорость сетевой карты. Если я вдруг не могу этого сделать и эта библиотека ест мой CPU или создает сотню потоков — что-то не так, правильно? Если явных косяков не заметно, то надо сравнивать с конкурентами. Если большой разницы нет (естественно гнаться за долями процента в ущерб коду не стоит) — то переходим к качеству кода. Если есть — оптимизируем.
Например, под линукс проще собирать опен-сорс код, а под windows — головная боль, а многие проекты и не заработают вообще.
Вы про проекты, изначально заточенные под Unix?
Я не раз сталкивался с багами под вин из-за того что разработчики банально не тестировали под нее (например рассчитывали только на линуксовые разделители файлов) и не собирались этого делать в будущем. По началу я думал: «ок, исправлю, сделаю пулл реквест», но потом надоело — зачем плыть против теченья?
Но это сервера и бэкенд. Наверное если бы я занимался каким-нибудь gamedev, то наоборот нужна была бы win.
static void tlb_remove_table_one(void *table)
Т.к. из одной функции просто вызывается другая. Ещё надо долго покликивать, чтобы понять что она всё-таки делает(если человек до этого не знает).
«Макрос BUG_ON обычно вызывает печать содержимого регистров» — но в коментариях к коду это не прописано. Может в данном случае он делает то, что «необычно».
— Красив визуально и эстетически, вне зависимости от языка. На него просто приятно смотреть!
— Красив алгоритмически. Решения и паттерны, используемые в коде, не должны быть корявыми, не должно быть разбитых окон.
— Лаконичен. Чем меньше комментариев — тем лучше, код должен стремиться к самодокументированию и емкости.
Я бы пожертвовал алгоритмической красотой в пользу простоты и понятности.
Я понимаю, о чем вы, сам не раз испытывал то чувство радости за чью-то красивую находку, но нередко это бывает не в пользу понятности кода. Бывает необходимо раскуривать такой код, чтобы в конце воскликнуть: "вот же круто". Лучше, когда можешь скользить глазами по коду, не цепляясь.
Конечно же, много других вещей здесь будут влиять, иногда красивый алгоритмический трюк очень даже оправдан.
Я бы пожертвовал алгоритмической красотой в пользу простоты и понятности.
А если нужна скорость?
Самая простая и понятная сортировка — это пузырьком, а быстрая сортировка — куда замороченнее.
А если нужна скорость?
Ну а если нужна, то не пожертвовал бы. Это же очевидно.
Ваш оппонент правильно написал: для современных быстрых компьютерных систем лучше писать понятнее, чем чтобы быстрее работало.
Нет, не от этого появляются тормозящие программы.
А от того, что мы с вами хотим покупать товары дешевле.
И бизнес сокращает свои издержки, в том числе и экономя на степени вылизанности кода. Компьютеры все равно быстрые, надежда на них, что вывезут.
Для меня быстрая сортировка — одновременно красивый и понятный алгоритм.
Как иначе можно оценить красоту алгоритма, не поняв его?
Вот оптимизированный код\алгоритм может быть сложнее и менее изящен.
Вообще, это частный случай правила «не используй в одном блоке разные уровни абстракций»
Отсутствие результата функции — как следствие не тестируемость — использование void — это плохо.
И еще несколько вещей делающих приведенные образцы антипримером.
А если ее заходят вызвать оттуда, где не проверяют?
Ее проверяют прямо перед вызовом или там есть еще пару строк кода, внутри которых могут изменить адрес функции?
А если это сделают через пару лет, когда забудут что она внутри не проверяются?
Такой код можно писать для себя, если уверен что им никто больше не воспользуется — и это должно быть документировано.
В функции, имеющую одну строку тоже нету смысла — зачем ее иметь? Вызывайте внутреннюю функцию из внешней или сделайте ее макросом.
Отсутствие результата у функции — это принт?
Если нет как вы собрались ее тестировать?
Приведенные образцы кода будут забракованы в любой более-менее приличной компании с mission critical кодом.
Однострочники в ядре — это часто обёртки над более общими вещами, а макросы не всегда уместны.
И далеко не всё можно тестировать, даже не так, далеко не всё нужно тестировать. Это тоже параноя.
Эти образцы могут быть забракованы только если рассматривать их вне контекста в котором они используются. И то, для этого нужно быть двинутым на всяких code complete настолько, что бы воспринимать их как обязательные к исполнению законы, а не как рекомендации.
Я вам привожу аргументы из практики компаний Forbs-100 где я обычно работаю.
Можно конечно заниматся абстрактным исскуством, только это обычно интересует 2.5 человека в мире и не практично.
Когда вaм приходится писать десятки мегобайт кода в каждом проекте и массу юнит-тестов для каждой функции — иначе оно элементарно не пройдет code review — то ваши аргументы, что програмист обязан помнить, просто смехотворны, сколько бы минусов вы мне не ставили.
Не дай бог. Мне с лихвой хватает уже того, что задачи ставят на естественном языке. Если мы ещё и код начнём на нём писать, то человечество обречено.
Честно скажу, не вижу разницы между естественным языком и языком программирование, если хорошо владеть и тем и другим…
лет через пять-десять миллиардов наступит тепловая смерть вселеннойНет! Через пару-тройку млрд лет Солнце гарантированно уничтожит Землю, но Вселенная просуществует до тепловой смерти ещё очень долго, на порядки больше. Правда не надо забывать вероятность большого разрыва или фазового перехода вакуума, что может случиться ещё раньше, если ускоренное расширение продолжится теми же темпами.
лет через пять-десять миллиардов наступит тепловая смерть вселенной
А чё так нереально быстро?!
Тепловая смерть — это то когда погаснут все звёзды.
А через пять, или даже десять миллиардов лет всё ещё будет материал для появления новых звёзд.
— хорошая новость: материала для новых звёзд хватит как минимум ещё на триллион лет,
— плохая новость: Большой Разрыв ожидается гораздо раньше — уже через 80 миллиардов лет, согласно Итану habr.com/post/369327 (у Итана оценка 2014 года, в Википедии оценка 2011 года, согласно которой ожидалось через 22 миллиарда лет)
{
return;
}
Ни одной ошибки! Шутка но отчасти. Из комментариев видно, что у каждого свои представления о прекрасном, спорить можно, переубеждать не стоит. Вы в статье попытались провести анализ темы, и это видно, я лично именно это оценил. От себя добавлю, я научился читать программы, где то лет через 5-6, как стал получать деньги за то что их пишу. Может я ошибаюсь, но думаю у многих было так же. Вспомните «мама мыла раму».
Или бывает: код очень хорош, но выглядит просто, и его считают простым, хотя это не так. При попытках изменения кода всё ломается, и другие говорят, что «вот, плохой код был!». Хоть это и не так.
У Linux — точно несовершенный код. Хотя бы из-за размеров и скорости разработки таковым его сделать не удастся.
Мой пример: код компилятора (или переводчика?) языка Eiffel от Бертрана Мейера. Обычно язык ограничен в средствах, т.к. создатель его смог написать только такой компилятор. У Eiffel же всё по максимуму.
Второй пример совершенного кода хотелось бы разобрать подробнее. Это код ядра Linux. Код, который на момент написания статьи управляет работой 500 суперкомпьютеров из top 500, код, который работает в каждом втором телефоне в мире и который управляет большей частью серверов в сети Интернет.
Это говорит о нетребовательности к ядру ОС в различных системах. Какая по сути разница что там? Linux, FreeBSD или Mach? Ну работает и работает, свои функции выполняет.
Просто проще поддерживать обвязку всего прочего программного обеспечения с одним ядром ОС. Без относительно качества этого ядра.
Linux используется везде — просто потому, что нужно хоть что-то использовать. Linux оказался в свое время, в своем месте, с достаточно активными продвиженцами его — и только. Никакого сверхкачества ядро Linux не имеет (да хоть по сравнению с выше упомянутыми).
Что по «качеству» ядра Linux — то просто посмотрите, что значит действительно мгновенная загрузка безо всяких там SSD и standby-in-memory на примере ОС Haiku.
Но нет, человечеству нужно не это. Человечеству нужно снижение себестоимости создания программного обеспечения. Пусть даже это программное обеспечение и будет более медленным.
Мне кажется, сначала нужно определить, что считать хорошим кодом. Быстрый? Понятный? Лаконичный?
В моей практике это определенно понятный, легко редактируемый код. Программа в идеале отработает всего пару раз, но может быть скопирована на другой проект, где потребуются некоторые изменения. Потому все сложное должно быть исключено по возможности. Регулярки — втопку. Редкие процедуры — туда же. Да, с ними можно писать красивее, но тут вопрос в целеполагании. И, хотя я нежно люблю регулярные выражения, я сознательно использую более простые функции моего языка, даже если их приходится вызывать несколько раз подряд. Только так я уверен, что мой код потом разберет другой программер.
Другими словами — прежде чем оптимизировать, давайте определимся с оптимизируемым параметром:)
Когда программный код вызывает восхищение?