Сохранёнки программиста
6.55K subscribers
1.17K photos
59 videos
10 files
1.86K links
Заметки и ссылки на будущее, чтобы изучить когда будет время.

Разместить рекламу: @tproger_sales_bot

Правила общения: https://tprg.ru/rules

Другие каналы: @tproger_channels

Другие наши проекты: https://tprg.ru/med
Download Telegram
Отладочная сборка Firefox линкуется у lld за 4,44 секунды, у mold — за 0,89. Разница видна на графике: mold почти всю линковку держит занятыми 32 ядра, а lld преимущественно работает на одном ядре, изредка выходя в многоядерные всплески.

Руи Уэяма, автор mold, разобрал устройство своего линкера в статье от 24 августа. Ускоряли не отдельные проходы, а весь конвейер: разбор всех входных файлов, включая каждый элемент архивов, разрешение символов отдельным проходом через атомарный compare-and-swap, сборка мусора по секциям, слияние строк и запись результата. На девяти больших проектах mold быстрее lld в 2,4–16,1 раза и до 112 раз — GNU ld.

Одним проходом ускорение не объясняется: оставить последовательными копирование вывода и применение релокаций — и время линковки растёт на 542 процента.

Цена — эвристика: порядок разрешения конфликтов символов между общими библиотеками стандарт не описывает, mold выбирает его сам. Проверяли сборкой всего Gentoo: из 19 000 с лишним пакетов не собрались два.

@prog_stuff
Венсан Берна собрал введение в spanning tree, где схемы не нарисованы, а работают. В браузере крутится настоящий демон MSTPD, скомпилированный в WebAssembly через emscripten: код, который обычно говорит с ядром Linux, заменён на C API — он заводит мосты и порты, отдаёт состояние в JSON и двигает время детерминированно.

Дальше можно ронять кабели и смотреть по шагам, вперёд и назад, как дерево пересобирается и какой порт уходит в блокировку. Чтобы понять, сошлась ли топология, автор после каждого шага сохраняет снимок памяти симуляции, проигрывает пятьдесят секунд модельного времени и откатывается к снимку.

Сорок юнит-тестов на эту обвязку проходят за 396 миллисекунд.

Чтения на тридцать шесть минут, и RSS-читалка не подойдёт: примеры живут только на странице.

@prog_stuff
1👍1
«x86 выполняет обращения к памяти строго по порядку, а ARM и RISC-V переставляют их как хотят, поэтому слабая модель памяти масштабируется лучше». Фабиан Гизен объясняет, почему эта формула ошибочна с обеих сторон.

За исключением совсем маленьких ядер и микроконтроллеров без кэша, процессоры вообще не соблюдают собственную модель памяти на каждом обращении. Они обещают вести себя так, как если бы соблюдали, и разница здесь принципиальная. Реализуют это оптимистично: считают, что данные никто параллельно не менял и что запись никем не оспаривается, выполняют обращения в удобном порядке, а рядом держат ровно столько метаданных, чтобы задним числом заметить нарушение порядка. Заметили — инструкция не фиксируется, состояние откатывается на момент до её выполнения, дальше повтор.

Так устроены все, независимо от модели памяти. Разница в другом: сколько метаданных нужно хранить для каждой обращающейся к памяти инструкции в полёте и какие порядки разрешено фиксировать при конфликте. Сильная модель чаще признаёт конфликт и уходит на повтор, слабая допускает больше законных порядков для расслабленных чтений и записей. При этом конфликт — медленный случай на любой архитектуре.

Часто цитируемые 7 процентов на аппаратный TSO у Apple Silicon Гизен отдельно снабжает оговоркой: это цена конкретной реализации, где TSO включается ради Rosetta и не является основным режимом ядра, спроектированного под AArch64.

Личное правило автора для многопоточного кода: конкурировать за общие данные реже, а не быстрее.

@prog_stuff
Big Pineapple — платформа, на которой у Cloudflare работают 1.1.1.1, Gateway DNS, DNS Firewall и AS112, — держит в памяти больше 250 миллиардов записей кэша. Пять правок в том, как одна запись там лежит, вернули компании около 100 терабайт: столько оперативной памяти стоит примерно в 130 серверах их тринадцатого поколения.

Компания разобрала все пять по порядку. Vec и String заменили на Box<[T]> и Box<str>: поле capacity бесполезно там, где запись после создания уже не меняется, а это 8 байт на поле и 64 байта на запись. Три списка DNS-секций свернули в один список с двумя смещениями по u16 — минус ещё 28 байт. Крупные варианты enum убрали в кучу, чтобы размер записи не диктовал самый жирный из них.

@prog_stuff
Крейт из одной строчки показал, что Rustdoc делает лишнюю работу на каждой сборке

Перед каждым стабильным релизом Rust команда прогоняет Crater — инструмент, который собирает новой версией компилятора всю публичную экосистему. В прошлом месяце Crater нашёл, что бета-Rustdoc падает на крейте indented-blocks. Весь крейт — одна строчка #![recursion_limit = "8"], ничего больше.

Rustc такой код компилирует спокойно. Rustdoc падает с «reached the configured maximum number of stack frames», разбирая внутренний трейт NumBufferTrait из core::fmt.

Формально это не регресс: число кадров стека не входит в гарантии стабильности. Но Ноа Лев из команды Rustdoc зацепился за другое — с какой стати почти пустому крейту вообще инлайнить документацию трейта из core::fmt, который никто не переэкспортирует. Логи подтвердили, что Rustdoc действительно лезет за ним.

За неделю разбора и серии PR среднее время работы упало на 25%, то есть Rustdoc стал быстрее на 33%. На реальных крейтах вроде hyper и bitmaps выигрыш доходит до 40%, на микробенчмарках вроде helloworld — до 60%.

Автор прошёл весь путь по шагам: от странного сообщения на Zulip до профилирования и правок, попутно объясняя, как Rustdoc устроен изнутри и почему он так тесно завязан на внутренние API компилятора.

@prog_stuff
❤‍🔥2
«TUI лучше, потому что с клавиатуры» — аргумент, который держится на плохих GUI

На Hacker News неделю назад обсуждали призыв перестать делать текстовые интерфейсы в терминале и делать графические. Спор вышел живой, и в защиту TUI регулярно звучало одно и то же: их предпочитают, потому что они управляются с клавиатуры.

Хараламбос Кардарис разбирает именно этот довод. Наблюдение он не оспаривает: возьмите наугад графическое приложение и терминальное, второе с большей вероятностью окажется полностью клавиатурным. Но отсюда не следует, что делать надо TUI. Следует, что клавиатурная навигация во многих GUI сделана плохо.

Ничто не мешает графическому приложению управляться с клавиатуры так же полно, как терминальному, и лучше. Руководства по интерфейсам это прямо требуют: в GNOME Human Interface Guidelines написано, что всякое действие, выполнимое указателем, должно быть выполнимо и с клавиатуры, и что по любой части интерфейса нужно уметь пройти без мыши. Рекомендация есть, соблюдают её выборочно.

Сам автор — тяжёлый пользователь терминала и от TUI не отговаривает. Его претензия к рассуждению: клавиатурность зависит от того, как написана программа, и средой не задаётся.

@prog_stuff
Переписать с нуля — один из семи вариантов, и обычно худший

Легаси-сервис годами просто работает, потом одна старая зависимость тянет за собой другую, тесты начинают падать, и мелкий баг кладёт всё приложение. Первый порыв всегда один: переписать. Итог порыва тоже известен — месяцы без новых функций и никакой гарантии, что новая версия окажется лучше старой.

В разборе семь стратегий модернизации и ситуации под каждую: оставить как есть, заранее договорившись, какое событие станет сигналом к переделке; вывести ненужный модуль из эксплуатации; обновить стек без смены архитектуры; перенести на другую платформу; заменить готовым продуктом; рефакторить по частям; переписывать постепенно по схеме Strangler Fig, когда новая система оплетает монолит и забирает его задачи модуль за модулем.

Главная мысль: одной системе не обязан подходить один подход. Стабильный модуль не трогают, забытую интеграцию удаляют, авторизацию выносят постепенно. Перед тем как предлагать переписывание, каждый кусок сверяют по шести критериям: как часто меняется, сколько ест поддержки, от чего зависит, есть ли тесты, сколько может лежать и остались ли люди, которые его понимают.

В нашем канале Tproger разобрали подробнее.

@prog_stuff
Samsung встроила вычислительные блоки прямо в LPDDR5X

Samsung продолжила линию Processing-in-Memory: в LPDDR5X-9600 у каждого из 16 банков появился PIM-блок с MAC-деревом, регистрами инструкций и векторов активаций. Память выполняет часть вычислений там, где лежат данные, и работает через стандартный протокол LPDDR5X. Особые адреса строк включают режимы по принципу MMIO-регистров.

Устройство напоминает жёстко ограниченный SIMD: одна операция и один операнд распространяются на все 16 банков. Их суммарная внутренняя пропускная способность достигает 614 ГБ/с, тогда как обычное обращение к памяти через внешнюю шину упирается в 76,8 ГБ/с.

Каждый PIM-блок держит четыре операции MAC в INT8 или FP8 за такт данных, то есть восемь за такт без учёта удвоенной передачи. На четырёхбитных весах пропускная способность удваивается, и пакет выдаёт 2,4 TOPS. Восемь чипов дают 9,6 INT8 TOPS, как NPU Intel Meteor Lake, и требуют 128 ГБ системной памяти. Архитектуру разобрали по материалам Hot Chips 2026.

@prog_stuff
🤯1
OpenOffice не печатал по вторникам, и виноват был Erlang

В багтрекере Ubuntu годами висела жалоба: печать из OpenOffice иногда не работает. Пользователи спорили, чей это баг, потому что из других программ печаталось нормально. Один снёс и переставил пакет, отчитался в четверг, что помогло — и через две недели, во вторник, написал, что не помогло.

Через четыре месяца жена одного из разработчиков Ubuntu сформулировала точно: OpenOffice не печатает по вторникам. Показать мужу она этого не могла, потому что была среда.

Разгадка оказалась в утилите file, которая определяет тип файла по сигнатурам в начале. OpenOffice записывает в PostScript дату создания, и по вторникам она принимает вид %%CreationDate: (Tue MMM D hh:mm:...).

В базе сигнатур лежало правило для Erlang JAM-файла:

4 string Tue Jan 22 14:32:44 MET 1991 Erlang JAM file - version 4.2

Пробелы в образце не экранированы, поэтому сравнивались только первые три символа. Правильная запись — 4 string Tue\ Jan\ 22\ 14:32:44\ MET\ 1991. По вторникам PostScript опознавался как Erlang JAM и до принтера не доезжал.

Правил в file больше 1600, и порядок проверки решает: ложное совпадение с Erlang случалось раньше, чем проверка на PostScript. История целиком — в собрании курьёзных багов.

@prog_stuff
😁1
Одна JSON-колонка в SQLite вместо спроектированной заранее схемы

David Leadbeater показал приём, который сохраняет смысл спустя годы: документ целиком лежит в одной колонке SQLite, а востребованные поля постепенно становятся частью схемы. Заранее перечислять всю структуру JSON не требуется.

Поле извлекается через json_extract внутри generated column. Виртуальную колонку можно индексировать обычным CREATE INDEX, после чего запрос использует индекс как для привычного столбца. Когда появляется новый способ поиска, схема расширяется через ALTER TABLE; в статье план запроса проверяется командой EXPLAIN QUERY PLAN.

Generated columns есть в SQLite начиная с версии 3.31. Leadbeater разбирает варианты VIRTUAL и STORED, ограничение NOT NULL и индексы.

Границы у приёма явные. Колонку STORED таким способом через ALTER TABLE добавить нельзя, а некорректный JSON ломает вставку. Получается постепенная схема: исходный документ остаётся целым, а наружу выходят только поля, которым действительно понадобились ограничения или быстрый поиск.

@prog_stuff
Писать код и строить систему — разные работы

Эссе, которое разбирают на Hacker News, начинается с наблюдения: перевести идею в инструкции для машины и решить, какие инструкции вообще должны существовать, — два разных занятия. Пример автора: «обрабатывать входящие события и обновлять данные». Синхронно или через очередь? Ровно один раз или хотя бы один? Что делать, если потребитель лежал три часа? Ни один из вопросов не про синтаксис.

Главный тезис: каждая оптимизация тратит сложность где-то ещё. Кеш даёт задержку и забирает инвалидацией, микросервисы дают независимые деплои и приносят распределённые отказы. Работа инженера — решать, где этой сложности место.

В нашем канале «Tproger» разобрали подробнее.

@prog_stuff
Почтовый CSV Японии рвёт строки посередине слова — и парсить его приходится всей стране

Пол О'Лири МакКанн, автор питоновской библиотеки posuto, разобрал ken_all.csv — файл, в котором Почта Японии раздаёт базу всех почтовых индексов. Формат легендарный: о нём регулярно ругаются в соцсетях, а один твит описывает грешников, которых в аду заставляют парсить этот файл вечно.

Внутри: если название района длиннее 38 символов, строка разрезается на несколько — в случайном месте, не по границе слова, — а остальные поля дублируются в каждой. В поле названия встречаются скобочные примечания «для читателя», ссылающиеся на порядок строк, — в формате, где строки должны быть самодостаточны. Поля не квотируются. На один индекс может приходиться до 66 строк.

Причину разрезания никто не объяснил; автор предполагает буфер фиксированной длины в системе тридцатилетней давности. Урок переживёт сам файл: данные, которые готовили для человеческого глаза, мстят машинному парсеру.

Статья — Parsing the Infamous Japanese Postal CSV

@prog_stuff
Как разобрать чужой бинарный формат: полный проход от файла сохранения до готовой схемы

Вам достался файл без спецификации: двоичный файл настроек прибора, сохранение игры, кеш программы. Автор ImHex — бесплатного открытого hex-редактора — годами отвечал на этот вопрос одной фразой «посмотри декомпилированный код того, кто его пишет», и наконец разобрал процесс целиком на файле сохранения игры FEZ.

Первый взгляд в hex-редактор уже даёт три вывода: в файле видны читаемые строки вроде DOT_LOCKED_DOOR_A, значит он не сжат и не зашифрован; в начале нет сигнатуры, значит по ней формат не опознать. Дальше автор идёт к тому, кто файл пишет. FEZ написана на C#, её сборки декомпилируются почти в исходник; в EasyStorage находится класс PCSaveDevice, а в нём строка "SaveSlot" + index — то самое имя файла. Рядом метод Save, который заводит буфер на 40 960 байт и заполняет его через BinaryWriter. Порядок вызовов записи и есть структура файла.

Остаток разбора — перенос этого порядка в Pattern Language, встроенный язык описания форматов ImHex: struct с полями, размещение по адресу через оператор @, отдельные типы под строки, списки и перечисления. Схема одновременно и парсит файл, и документирует находки, и проверяет их: ошиблись в поле — данные перестанут сходиться.

Общий рецепт автор в конце сводит к четырём шагам: проверить, не известен ли формат (magic-детект ImHex, binwalk); найти код, который файл читает или пишет; опознать в нём привычные кирпичи — числа, строки, булевы значения, структуры; записать всё это схемой. Для .NET декомпилятор — Rider, для нативного кода — Ghidra, IDA или Binary Ninja, для JVM — Recaf.

@prog_stuff
👍2
Ваш бинарник в Alpine может быть на 26% медленнее из-за libc, а не из-за кода

musl — лёгкая libc, на которой собраны образы Alpine и самодостаточные статические бинарники «один файл — и работает». Джонатан Эллис, сооснователь DataStax и многолетний глава проекта Apache Cassandra, замерил цену этого удобства на своей рабочей нагрузке.

Со штатным аллокатором musl проигрывает разгромно, причём на обычных четырёхъядерных машинах, а не только под высокой конкурентностью. Замена на mimalloc убрала большую часть разрыва, но 26% остались: профилирование указало на базовые процедуры работы с памятью внутри самой libc.

В нашем канале «Tproger» разобрали подробнее, включая то, что автор оставил для musl.
METR разобрала, как у неё украли API-ключ: приложение, написанное с ИИ, при сбое проверки прав пускало всех

Лаборатория, которая тестирует модели для крупных разработчиков, опубликовала отчёт о двух инцидентах. В марте исследователь поднял на личном EC2-инстансе маленькое веб-приложение за входом через Google. В коде была ошибка fail-open: если проверка авторизации не срабатывала, приложение считало пользователя допущенным. Инстанс несколько дней смотрел в интернет, атакующий вытащил ключ к моделям, добавил свой SSH-ключ и три недели гонял чужие запросы.

Заметить было трудно: за кредиты METR не платила деньгами, так что счёт не рос, а внутренняя панель METR не показывала отклонённые по лимитам запросы. В мае пришла вторая волна: сканирование, перебор паролей, попытки через OAuth-токены и ошибка в публичном просмотрщике транскриптов с read-only SQL.

Выводы для любой команды: ключи только в секрет-хранилище, приложения с доступом к ключам делать fail-closed, на каждый ключ лимит и алерт по числу запросов, а не только по деньгам. Полный разбор цепочки на сайте.

@prog_stuff
Один параметр readahead сократил обращения к диску в 12 раз, а io_uring тут ни при чём

Фернандо Симойнс разбирал, почему его встраиваемая СУБД на io_uring не выигрывала у обычного чтения на сканах. Тест: запрос Q6 из TPC-H по таблице в 1,2 GiB, файл открыт с O_DIRECT, запросы на чтение уходят в очередь io_uring.

Без упреждающего чтения база отправила 195 тысяч запросов по 4 KiB, и почти столько же дошло до устройства: ядру нечего было склеивать. С окном readahead в 32 страницы запросов в очереди стало даже больше, 218 тысяч, но до диска добралось около 16 тысяч. Ядро объединило 93% соседних чтений, средний запрос вырос с 4,37 до 56,53 KiB.

Цена тоже есть: поток sqpoll, который опрашивает очередь вместо системных вызовов, съел 65% циклов процессора. Вывод автора в том, что io_uring не ускоряет сам по себе, а только даёт очередь, и без прогрева этой очереди последовательными чтениями выигрыша нет.

@prog_stuff
Посмотрите, как выглядят 128 килобайт, если каждый бит — отдельное ферритовое колечко

Spacelab — европейская лаборатория, которую возили в грузовом отсеке шаттла. Компьютеры NASA туда не поставили: программа была европейской, и внутри работали три одинаковых французских миникомпьютера Mitra 125 MS. Один вёл саму лабораторию, второй эксперименты, третий стоял резервом.

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

Кен Ширрифф разобрал модуль до отдельных проводов: как устроены плоскости сердечников, что делает плата драйверов и почему чтение здесь стирает прочитанное значение, так что его каждый раз приходится записывать обратно.

@prog_stuff
👍21
JetBrains не пропатчила собственный TeamCity, и через него вынесли бэкап сервиса Cadence, AWS-учётки и файлы из S3

Компания обновила отчёт об инциденте. Cadence, облачный сервис для запуска вычислений из PyCharm, использовал TeamCity для оркестрации задач. В нём была CVE-2026-63077 с выполнением команд без аутентификации, и с 8 по 24 августа атакующие сидели на api.cadence.jetbrains.com. Обнаружили 23-го, отключили 24-го. Формулировка JetBrains: «сервер должен был быть пропатчен, но не был».

Что утекло: логины, имена, почта и IP пользователей; полный бэкап сервера за 2024 год; AWS IAM-пользователи с учётными данными; файлы в S3-бакетах JetBrains. Мог быть доступен исходный код, который пользователи синхронизировали из PyCharm. Затронуты ли бакеты клиентов, компания пока не установила.

Рекомендация JetBrains тем, кто подключал Cadence: считать скомпрометированным всё, к чему у сервиса был доступ, от ключей облаков до SSH и ключей подписи. Хронологию и список секретов на замену разобрали на сайте.

@prog_stuff
🙈2
Спрячьте задержку автодополнения между нажатием и отпусканием клавиши

Рюртьян Пул развивает Wirewiki — сервис для просмотра DNS-записей доменов, их истории, делегирования и настроек почты. Главная навигация там ищет среди 240 миллионов доменных имён, поэтому автор добился появления подсказок уже в следующем кадре.

Запрос уходит на keyDown, сразу после нажатия клавиши. Сервер получает набранный префикс и подготавливает варианты для всех допустимых следующих символов, а интерфейс показывает результат на keyUp. В бюджет попадают длительность нажатия, пауза до следующей клавиши и длительность следующего нажатия.

Автор набрал 100 доменов в обычном темпе и получил p99 в 121 мс. Задержку он считает от keyUp до готовности результата: p99 в 0 мс означает, что в 99% случаев данные приходят раньше отпускания клавиши. Для сравнения, экран с частотой 60 Гц обновляется каждые 16,7 мс.

Перерасход трафика ограничен алфавитом домена: 38 символов дают максимум (38 + 1) × 8 = 312 имён в ответе. На практике запрос приносит до 5 КБ, после сжатия — около 2,5 КБ. Приём стоит проверить там, где сеть быстрее пальцев пользователя.
Не всё же работать, иногда можно и поиграть.

Разобрали пять способов пополнения баланса Steam. Один зачисляет деньги на кошелёк по логину за одну-две минуты с комиссией около 6%. Другой показывает итоговую сумму ещё до оплаты и подходит для регулярных небольших пополнений. Третий — маркетплейс, где условия зависят от выбранного продавца, но сделки защищены гарантиями площадки. Четвёртый предлагает пополнение и по логину, и через продажу игровых предметов почти без комиссии. Пятый не берёт свою комиссию, но добавляет разницу за счёт курса конвертации.

По каждому — реальная комиссия, лимиты и на что смотреть, чтобы не наткнуться на мошенников. Складывайте в сохранёнки, вдруг пригодится.
Интерпретатор WebAssembly ускорили в 2,2 раза, и автор объяснил каждую оптимизацию

Wasmi нужен там, где JIT запрещён или не помещается: плагины, микроконтроллеры, песочницы, смарт-контракты. Версия 2.0 по замерам проекта исполняет код в 2,2 раза быстрее 1.0 по геометрическому среднему, а пост Робина Фрайлера читается как учебник по устройству быстрых интерпретаторов.

Три источника ускорения: четыре режима dispatch вместо одного switch-loop (auto-dispatch сам выбирает threaded-код, где компилятор позволяет), новое IR с аккумуляторными регистрами вместо стековых смещений и append-only CodeMap, где вызов функции на горячем пути не делает ни одного lookup. Ячейки стека стали 64-битными, поэтому SIMD больше не стоит 8% обычному коду.

Отдельная история про компилятор: Rust 1.92 включил DestinationPropagation, и CoreMark у интерпретатора Stitch упал с 3000 до 2200. Похожую проблему нашли и в Wasmi, после исправления результат вырос с 2800 до 4200. Вывод: после обновления toolchain бенчмарки интерпретатора надо перепрогонять. Сравнение с Wasm3, WAMR и Wasmtime Pulley разобрали на сайте.

@prog_stuff
❤‍🔥2