Читать в телеге. Когда-то там были посты не только от меня.
Вариации сна для компьютера
- Бездействие (aka S1, idle) — CPU остановлен, система потребляет чуть меньше энергии: почти все устройства переключены в режим низкого энергопотребления или выключаются (ЖД, подсветка экрана).
- Сон (aka S2) — как S1, только CPU отключен от питания (разница с S1 минимальна, на некоторых материнских платах его даже не реализовывают).
- Ждущий режим (aka S3, sleep, suspend) — почти все устройства, кроме RAM отключены, состояние хранится в памяти. RAM обновляется медленнее. Жрет питание/батарею, но чуть-чуть.
- Гибернация (aka S4, hibernate) — состояние сохраняется на диск (например, в swap), питание полностью отключается. Однако не все оборудование корректно может восстановить свое состояние, поэтому этот режим обычно отключен по умолчанию.
- Мягкое выключение (aka S5, soft off) — все выключено, но некоторые порты/контроллеры включены (чтобы включить комп по LAN или по нажатию кнопки на клавиатуре, например).
- Гибридная гибернация (aka hybrid suspend) — состояние сохраняется на диск, как в гибернации, а потом комп переводится в ждущий режим. Позволяет быстрее выходить из сна, но не терять состояние, если питание пропадет. Есть еще вариация, когда сначала выполняется suspend, а потом, по таймеру или по триггеру (например, низкий заряд батареи) кратенько просыпается и уходит в гибернацию.
- Modern Standby (aka S0 low power idle, S0ix) и PowerNap — маркетинговые названия для S0 (полностью работающий комп), просто часть функций системы не работают и часть периферии отключена. Но при этом выполняются фоновые задачи и остается подключение к сети (“как у смартфона”). Питание потребляет много, и ноутбук в таком режиме в сумку класть не стоит — может перегреться.
Псевдонимы для типов и value-классы
Почти бесплатно повысить читаемость кода можно за счет псевдонимов типов (type aliases). Например, можно заменить в бизнес-логике безликий UUID
на UserId
. Есть это почти во всех языках: typedef
или даже #define
в С, using
в C++ и C#, type alias
в Kotlin, type
в Scala/Haskell и даже в питоне, но не в Java. Можно еще использовать локально: импортировать тип с другим именем. Но у псевдонимов есть банальный недостаток: ничто не помешает в UserId
запихать другой UUID
, например ObjectId
.
Чтобы компилятор проверял подобные несоответствия, можно использовать value-типы (они же Single case discriminated union). Обычно это класс-обертка над целевым типом, который имеет единственное поле и при возможности заменяется после компиляции на целевой тип. Например, в Kotlin это будет
@JvmInline
value class UserId(val value: UUID)
В некоторых языках еще можно добавить логику (например, проверку в init
блоке или добавить методы-геттеры). Если value-типов нет, можно достичь примерно того же, используя data-классы с одним полем (а где-то может и вообще разницы не будет).
Очевидная проблема оберток в том, что к содержимому всегда придется обращаться через поле или паттерн-матчинг, а не напрямую (и какой-нибудь делегацией это, увы, не решается). А из-за потенциального оборачивания страдают библиотеки, использующие рефлексию, например, сериализация в JSON. В Scala это решили с помощью opaque-типов: рядом с объявлением класса он работает прозрачно, как type alias, а снаружи — непрозрачно, и доступа к целевому типу нет. При этом там есть и “обычные” value-классы, которые почти такие же, как в Kotlin.
А еще и в Scala и в Kotlin ждут Project Valhalla, чтобы можно было создавать пользовательские типы, доступ к которым осуществляется по значению, а не по ссылке. В комбинации с inline это позволит решить часть проблем с оборачиванием value-типов.
Help me, step-container, I'm stuck!
Иногда контейнер не может подняться, а в логах полезной информации нет. Надо бы провалиться в консоль контейнера, чтобы проверить все изнутри, но из-за постоянных рестартов это не получится сделать. В этом случае можно сохранить текущее состояние контейнера:
docker commit %container_id% %some_name%
и стартовать его с командной строкой:
docker run -it %some_name% /bin/sh
А потом уже дебажить, что там внутри не так.
Из-за чего команда профессионалов может работать фигово
В продолжение заметки про продуктивность — а почему в команде из кучи замотивированных сеньоров все равно может получиться что-то не то?
Причин может может быть масса [1, 2, 3]:
- Они делают не то, что важно (в т.ч. из-за того, что увлекаются чем-то интересным для себя), часто из-за отсутствия четких целей, отсутствует фокус или пытаются сделать все и сразу.
- Они ждут другую команду (из-за плохого разделения, из-за мутных процессов или из-за чрезмерной специализации, когда не можешь помочь другой команде).
- Нет четкого понимания, что нужно сделать, тратится время на выяснение этого (хорошо еще, если до разработки, плохо, если на миллионе встреч).
- Нет четких зон ответственности и ролей — непонятно, кто должен что сделать, чаще всего превращается в “не делает никто”. В том числе когда что-то можно было предотвратить малой кровью вместо героического расхлебывания последствий.
- Они не умеют говорить “нет”.
- Нет долговременного планирования, слишком большая сосредоточенность на скорости, а не качестве.
- Они плохо понимают предметную область и конечного потребителя.
- Нет признания результатов, чувства достижения, мотивация делать нормально падает, появляется куча новых проблем.
Визуализация explain для SQL-запроса и рекомендации по индексам
Хороший пост, в котором как для даунов объяснены основные типы индексов, даны советы, когда какие использовать, и приведены примеры запросов, которые можно оптимизировать. А еще там есть ссылка на сервис, в котором можно визуализировать выполнение запроса и получить рекомендации, какие индексы добавить.
Docker на Маке
brew install docker
поставит только клиенты докера, без демона. docker-machine
, который запускает докер в виртуалке (sic!), уже устарел и его хрен запустишь. Docker Desktop платный (ну, на честном слове платный, но все же) и требует GUI…
В итоге рабочий вариантом оказалась… установка кубера, а именно minikube. Можно еще туннелирование в локальную сеть настроить или поиграться с kubectl. Но после таких приколов хотя бы понимаешь, почему некоторые считают, что надо минимум 32 гига оперативки для разработки на маке :/
Даты изобретения сортировок и кроссбаузерность
После изучения всяких фронтенд штук захотелось сделать что-нибудь очень простое. Прям наговнякать. Решил сделать временную шкалу с датами изобретения алгоритмов сортировки на CSS, и собирать HTML на голом питоне.
Во-первых, я все еще офигеваю, как сложно сделать какие-то элементарные вещи. Во-вторых, что до сих пор в трех разных браузерах одно и то же форматируется по-разному, хотя я использую все из древних стандартов. Я честно пытался написать кросс-браузерно, но в итоге мне это надоело и я сделал браузеро-зависимый CSS на основе всяких трюков. Отправляйте пулл-реквест, если умеете верстать или хотите попытать силы :)
Наконец, самой тяжелой частью оказались собственно данные. Нашел статью, где вроде есть все, что нужно, но качество данных там просто впечатляющее. Например, в качестве авторов Stooge Sort указаны “Prof. Howard Fine and Howard”. А на самом деле она фигурирует в упражнении из Кормена: “Professors Howard, Fine, and Howard have proposed the following “elegant” sorting algorithm…” — и является отсылкой на комедийный коллектив. Изначально в планах было найти оригинальные источники для каждой сортировки, но это оказалось нетривиальной задачей, поэтому я отложил это до лучших времен.
Демка тут.
Статическая типизация — не панацея
Что выведет этот кусочек кода на Kotlin?
fun main() {
data class SomeClass(val someValue: Int)
val someNull = null
println(someNull + SomeClass(12))
}
Ответ
Выведет nullSomeClass(someValue=12)
. Почти как в JS:)
Все благодаря выводу типов: компилятор находит расширение operator fun String?.plus(other: Any?)
, вызывает у обоих аргументов toString()
и склеивает их.
Хорошо хоть, узнал это не из продакшен-кода, а из познавательного видео, в котором мужик рассказывает про “переопределение” оператора сложения для nullable типов.
Экономьте газ
В мире “современной” разработки, где бизнесу часто нужно чтобы “лишь бы работало”, компиляторы “сами все оптимизируют”, а проблемы с производительностью решаются методом “ну накинь там пока ресурсов на под”, оптимизация алгоритмов сверх банального “для поиска используй хэш-таблицу” кажется излишней, потому что “и так сойдет”.
Однако есть ниша, в которой экономить элементарные операции все еще актуально — смарт-контракты. Каждое вычисление чего-то стоит. Например, в Etherium каждая операция смарт-контракта тратит немного “газа”, не уложился в лимит — транзакция откатывается. В периоды большой нагрузки на сеть газ стоит дороже, в периоды низкой, соответственно, дешевле. Если алгоритм оптимизирован, то на его выполнение будет потрачено меньше газа и, как следствие, меньше криптовалюты.
Хотя некоторые паттерны неоптимального кода довольно тривиально обнаруживаются и исправляются, и вызваны скорее незрелостью технологии (например, расчет суммы, которая не зависит от внешних параметров, может быть заменен формулой). Есть несколько инструментов для оценки использования газа, а еще можно покрыть контракт тестами (например, с помощью hardhat или brownie) и выводить либо оценку, либо точное значение потраченного газа. Но чтобы оптимизировать — тут головой еще надо думать, увы.
Я немного поигрался с двумя последними инструментами, сделал пару тестовых контрактов и посмотрел на использование газа, в целом занятно. Думал даже как-нибудь поиздеваться над студентами, чтобы они оптимизировали в лабах использование элементарных операций, но экосистема nodejs в обоих случаях не очень к этому располагает. Хоть свою RAM-машину пиши…
Буфер обмена с консольной виртуалкой
Сымитировать буфер обмена в консоли без графического окружения (например, на виртуалке) можно, установив gpm
— демон для мышки. С помощью него можно выделять текст левой кнопкой, а правой — вставлять. Кто-то даже упоролся и сделал интеграцию с буфером хост-машины, но выглядит стремно. Древние фиче-реквесты прилагаются.