Pull to refresh
1989
150.8

Переводчик-фрилансер

Send message

Структуры данных на практике. Глава 4: Массивы и локальность кэша

Level of difficultyMedium
Reading time10 min
Reach and readers5.7K

«Массив — самая важная структура данных в computer science», — Дональд Кнут (вольное изложение цитаты)

Простейшая структура данных

Массивы настолько просты, что мы иногда воспринимаем их, как нечто само собой разумеющееся. Смежная память, доступ за O(1): что тут ещё оптимизировать?

Всё.

Я работал над конвейером обработки пакетов сетевого коммутатора. Код был простым: считываем пакеты из кольцевого буфера (массива), обрабатываем их и записываем результаты в другой массив. Всё просто, правда?

Но производительность была ужасной. Мы обрабатывали 100 тысяч пакетов в секунду, хотя оборудование должно было справляться с 1 миллионом.

Профилировщик показал нечто странное:

$ perf stat -e cache-misses,instructions ./packet_processor

Performance counter stats:

450,000 cache-misses

1,000,000 instructions

450000 промахов кэша на 1000000 команд? То есть промах происходил раз в 2-3 команды. При простых операциях с массивами это не имело никакого смысла.

Проблема заключалась не в самих массивах, а в том, как мы их использовали.

Читать далее

Компилируем Quake, как будто на дворе 1997 год

Level of difficultyEasy
Reading time4 min
Reach and readers13K

Первые исполняемые файлы Quake (quake.exe и vquake.exe) программировали на HP 712-60 с NeXT и кросс-компилировали при помощи DJGPP, запущенного на DEC Alpha server 2100A. В июне 1996 года, после выпуска игры, id Software, озабоченная стагнацией NeXT, решила поменять стек разработки.

Сразу после выпуска Quake мы перешли на оборудование Intergraph с Windows NT.

- Джон Кармак[1]

Следующие версии Quake (winquake.exe, glquake.exe) и QuakeWorld (qwcl.exe и qwsv.exe) разработаны и скомпилированы в Windows NT с помощью Visual C++ 4.X.

В этой статье описываются этапы по воссозданию процесса сборки двоичных файлов Quake win32 в том виде, в котором он происходил в 1997 году.

Читать далее

Ковёр навахо в виде интегральной схемы: таймер 555

Level of difficultyMedium
Reading time6 min
Reach and readers12K

Известная ткачиха Мэрилу Шульц из племени навахо недавно закончила сложный ковёр, состоящий из толстых белых линий на чёрном фоне, испещрённых красновато-оранжевыми ромбами. Хоть это творение может показаться абстрактным, в нём представлена внутренняя схема крошечного кремниевого чипа таймера 555. Этот чип имеет сотни областей применения, от звукового генератора до контроллера автомобильных дворников. Когда-то 555 был самой продаваемой в мире интегральной схемой, счёт шёл на миллиарды устройств. Но как чип превратился в ковёр?

Читать далее

Главная цель Continuous Integration — это провал

Level of difficultyEasy
Reading time4 min
Reach and readers7.3K

CI (непрерывная интеграция) ценна только тогда, когда проваливается. Когда она проходит успешно, то становится просто оверхедом: того же результата можно было добиться и без CI.

Что такое Continuous Integration?

Разработка ПО следует по цикличному итеративному паттерну. Разработчики вносят изменения, коммитят их в систему управления версиями, развёртывают их для пользователей и повторяют этот процесс. Этап continuous integration (CI) расположен между коммитами и развёртыванием, это выполнение автоматизированных проверок каждого коммита. Если проверка проходит успешно, мы говорим «CI пройдена», после чего изменение развёртывается. Если проверка проваливается, мы говорим «CI не пройдена», и изменение не развёртывается.

Если вы опытный разработчик, то, возможно, думаете: «Ну это само собой!». Чтобы по-настоящему осознать предназначение CI, нужно посмотреть, что происходит с CI и без неё.

Читать далее

Самый тупой способ повышения производительности

Level of difficultyEasy
Reading time5 min
Reach and readers13K

Введение

Сегодня мне немного грустно, поэтому чтобы подбодрить себя, расскажу вам историю, самой, наверно, смехотворной задачи по оптимизации, которую мне поручали. Не знаю, извлечёте ли вы из неё что-то полезное, но, по крайней мере, кого-то она развеселит…

Читать далее

Взламываем 40-летний донгл защиты от копирования

Level of difficultyEasy
Reading time6 min
Reach and readers17K

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

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

Это ПО было написано на языке программирования RPG (Report Program Generator), который старше Кобола (!); он использовался в компьютерах IBM среднего ценового диапазона наподобие System/3, System/32 и вплоть до AS/400. Похоже, позже RPG портировали в MS-DOS, поэтому те же программные инструменты, написанные на RPG, могут работать на персональных компьютерах. Так фирма и оказалась в этой ситуации.

Эта бухгалтерская фирма работала на компьютере с Windows 98 (да, в 2026 году) и запускала написанное на RPG ПО в консольном окне DOS. Оказалось, что для работы ПО требовалось подключить к параллельному порту компьютера специальный аппаратный донгл защиты от копирования! В те времена это было достаточно распространённой практикой, особенно у поставщиков «корпоративного» ПО, защищавшего свои очень важные™ программы от неавторизованного применения.

Читать далее

Я создал вдвое более быстрый лексер, но обнаружил, что узким местом был ввод-вывод

Level of difficultyMedium
Reading time18 min
Reach and readers14K

Я создал лексер ассемблера ARM64 (ну, точнее, сгенерировал его из моего собственного генератора парсера, но пост не об этом), обрабатывающий код на Dart вдвое быстрее официального сканера. Этого результата я добился при помощи статистических методик надёжного измерения малых различий в производительности. Затем я провёл его бенчмарк на 104000 файлов и обнаружил, что узким местом был не мой лексер, а ввод-вывод. Это история о том, как я случайно узнал, почему pub.dev хранит пакеты в виде файлов tar.gz.

Читать далее

Структуры данных на практике. Глава 3: Бенчмаркинг и профилирование

Level of difficultyMedium
Reading time9 min
Reach and readers5.7K

Проблема измерений

Узнав из Главы 2 об иерархии памяти, вы, возможно, захотите оптимизировать свой код. Но есть одна проблема: как понять, что оптимизация на самом деле сработала?

Этот урок дорого мне обошёлся.

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

Я запустил код. Мне показалось, что он быстрее. Я закоммитил изменения.

Неделю спустя коллега провёл бенчмарки и выяснил, что моя «оптимизация» замедлила код на 15%. Я оптимизировал не то, но у меня не было данных, чтобы подтвердить мои предположения.

Вывод: никогда не доверяйте своей интуиции, всегда проводите замеры.

В этой главе я расскажу, как измерять правильно. Мы создадим комплексный фреймворк бенчмаркинга и научимся эффективно использовать инструменты профилирования.

Читать далее

Я решил написать ухудшенный UUID по ничтожнейшим из причин

Level of difficultyEasy
Reading time7 min
Reach and readers5.8K

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

В зависимости от версии и варианта в UUID есть множество разной информации, но по большей мере это куча случайных битов, которые и обеспечивают свойство «универсальной уникальности», которое нам так нравится. Но если по какой-то причине вам не нравится проверенный временем стандарт с огромной экосистемой нативной поддержки... то эта статья для вас!

Читать далее

Структуры данных на практике. Глава 2: Иерархия памяти

Level of difficultyMedium
Reading time8 min
Reach and readers6.7K

«Память — это современный диск, диск — это современная лента», — Джим Грей

Проблема ста тактов

В Главе 1 мы говорили о том, что промахи кэша стоят 100-200 тактов, а попадания в кэш — всего 1-4 такта. И это не какая-то мелкая деталь, а самый важный фактор современной производительности.

Ниже я расскажу, почему это так.

Однажды я оптимизировал драйвер устройства для встраиваемой системы на RISC-V. Драйвер должен был обрабатывать пакеты от сетевого интерфейса, но при большой нагрузке мы теряли пакеты. CPU работал с частотой 1 ГГц, а для обработки каждого пакета требовалось около 500 команд. Простая математика:

500 команд ÷ 1 ГГц = 500 наносекунд на пакет

При скорости 500 нс на пакет мы могли бы обрабатывать 2 миллиона пакетов в секунду. Однако мы справлялись всего с 200 тысячами пакетов в секунду, то есть в десять раз меньше, чем ожидалось.

Профилировщик показан следующее:

$ perf stat -e cycles,instructions,cache-misses ./driver_test Performance counter stats:

5,000,000 cycles

500,000 instructions

45,000 cache-misses

Постойте-ка: 500000 команд должны занимать 500000 тактов (при 1 IPC). Но мы видим 5 миллионов тактов. Куда подевались лишние 4,5 миллиона тактов?

Читать далее

Гигабитный Ethernet через телефонную проводку

Level of difficultyEasy
Reading time7 min
Reach and readers26K

Я наконец-то разобрался, как реализовать гигабитный Ethernet по телефонным проводам.

Powerline-адаптер и огорчения

В последние годы я по большей мере пользовался адаптерами Интернета через электропроводку (powerline-адаптерами). Некоторые работали неплохо, другие нет. Тот, которым я пользовался долго, обеспечивал мне стабильные 30 Мбит/с: маловато, но этого было вполне достаточно для меня на то время. Мне важнее всего стабильно низкие задержки в играх, а не пропускная способность.

Вернёмся в настоящее: серьёзной проблемой было то, что powerline-адаптер регулярно терял соединение. Я купил новые, поддерживающие последний и самый лучший стандарт G.hn 2400 (можно купить несколько и вернуть те, которые не подойдут). Окончательно выбранный вариант обеспечивал скорость примерно 180 Мбит/с до моего домашнего офиса (с большим разбросом от 120 до 280 Мбит/с) и примерно 80 Мбит/с до верхнего этажа. Достаточно, чтобы смотреть YouTube/телевизор, но далеко неидеально.

Любопытная деталь: Интернет-провайдеры Великобритании не предлагают гигабитный Интернет. Они продают диапазоны скоростей наподобие 30 Мбит/с – 75 Мбит/с – 150 Мбит/с – 300 Мбит/с – 500 Мбит/с – 900 Мбит/с, каждый из которых стоит на несколько фунтов в месяц дороже предыдущего. Из-за этого Великобритания одновременно страна и с одним из самых дешёвых, и самых дорогих тарифов Интернета в мире.

Прошло время, я сменил место жительства, «железо», тариф, и теперь Интернет работает на скорости 500 Мбит/с.

Каждый апдейт Helldivers 2 на 50 ГБ (потому что эти идиоты выпускают один и тот же контент с пятикратным дублированием) становится мучительным напоминанием о том, что система работает не в полную силу.

Задача: как доставить 500 Мбит/с до моей комнаты?

Читать далее

Нет никаких доказательств успешности «браузерного эксперимента» Cursor

Level of difficultyEasy
Reading time4 min
Reach and readers15K

14 января 2026 года Cursor опубликовала пост «Scaling long-running autonomous coding» (https://cursor.com/blog/scaling-agents).

В этом посте компания рассказала о своих экспериментах с «автономной работой кодинг-агентов в течение нескольких недель» со следующей чётко поставленной целью:

понять, насколько далеко мы можем продвинуться в сфере кодинга в проектах, для выполнения которых командам разработчиков обычно требуются месяцы

Компания рассказала о подходах, которые она попробовала, о предполагаемых причинах их провала и о том, как решались эти проблемы.

Наконец она достигла этапа, на котором нечто «решило большинство наших проблем с координацией и позволило масштабироваться до очень больших проектов», что, в свою очередь, привело к следующему:

Чтобы протестировать эту систему, мы поставили перед собой амбициозную цель: создание веб-браузера с нуля. Агенты работали примерно неделю и написали больше миллиона строк кода в тысяче файлов. Исходный код можно посмотреть в GitHub (https://github.com/wilsonzlin/fastrender)

И здесь объяснения становятся немного запутанными и нечёткими. Компания заявляет, что «несмотря на размер кодовой базы, новые агенты всё равно понимают её и добиваются существенного прогресса» и что «сотни воркеров работают конкурентно, выполняя пушинг в одну и ту же ветвь с минимальными конфликтами», но ни разу не говорит, был ли проект успешным, работает ли он на самом деле. Можем ли мы сами запустить этот браузер? Это неясно, и компания ни разу не сделала подобных чётких заявлений.

Читать далее

Как удаление сорока строк увеличило производительность в 400 раз

Level of difficultyMedium
Reading time12 min
Reach and readers25K

У меня есть привычка раз в несколько недель вкратце просматривать лог коммитов OpenJDK. Многие коммиты слишком сложны для того, чтобы я мог разобраться с ними за то ограниченное время, которое я выделил для своего... специфичного хобби. Но иногда мне удаётся найти нечто любопытное.

На прошлой неделе моё внимание привлёк этот коммит:

858d2e434dd 8372584: [Linux]: Замена чтения proc для получения CPUtime потока на clock_gettime

diffstat выглядел интересно: +96 вставок, -54 удалений. В changeset был добавлен бенчмарк JMH из 55 строк, что означало реальное уменьшение кода продакшена.

Читать далее

Javascript: прощай, Date, здравствуй, Temporal

Level of difficultyEasy
Reading time13 min
Reach and readers19K

Время выставляет нас всех дураками, и JavaScript в этом не исключение. Честно говоря, меня это особо не задевало: по большей мере меня радуют маленькие странности JavaScript.

Мне нравится, когда можно увидеть обратную сторону; какой бы формальной и железобетонной ни казалась спецификация ES-262, мы всё равно замечаем (если знать, куда смотреть) в ней все хорошие и плохие решения, принятые сотнями людей, разрабатывавших язык. У JavaScript есть характер. Да, он не всегда делает всё в точности так, как можно ожидать, но на мой взгляд, JavaScript обладает настоящим очарованием, которое можно оценить, если глубоко его изучить.

Впрочем, существует одна часть языка, которая мне кажется совершенно нелогичной: это конструктор Date.

Читать далее

Баги в ядре Linux в среднем прячутся по 2 года. Некоторые скрываются до 20 лет

Level of difficultyEasy
Reading time16 min
Reach and readers7.3K

Прямо сейчас в вашем ядре есть баги, которые не найдут ещё многие годы. Я знаю это, потому что проанализировал 125183 бага с отслеживаемой меткой Fixes: за 20-летнюю историю Git ядра Linux.

Прежде чем баг обнаружат, он в среднем живёт в ядре 2,1 года. Но в некоторых подсистемах ситуация гораздо хуже: для драйверов шины CAN этот срок в среднем составляет 4,2 года, для сетевого протокола SCTP — 4,0 года. Самый долгоживущий баг в моём датасете (переполнение буфера в ethtool) прятался в ядре 20,7 года. Баг, который я проанализирую в статье подробно (утечка refcount в netfilter), прожил 19 лет.

Я создал инструмент, перехватывающий 92% исторических багов в тестовом датасете на этапе коммитов. Ниже я расскажу, какую информацию мне это дало.

Читать далее

А король-то голый! Как написать свой Claude Code в 200 строках кода

Level of difficultyEasy
Reading time7 min
Reach and readers25K

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

Но вот что я вам скажу: в основе этих инструментов не лежит магия. Для них достаточно примерно двухсот строк простого Python.

Давайте с нуля напишем собственный функциональный кодинг-агент.

Читать далее

Структуры данных на практике. Глава 1: Разрыв в производительности

Level of difficultyEasy
Reading time9 min
Reach and readers18K

Часть I: Основы

«В теории теория и практика одинаковы. На практике это не так». — авторство приписывается разными специалистам по computer science

Загадка

Два часа утра. Я смотрю на совершенно нелогичные данные профилирования.

В процессе работы над загрузчиком для SoC RISC-V у нас возникла проблема с производительностью. Загрузчик должен был искать конфигурации устройств в таблице: примерно пятьсот элементов, каждый с 32-битным ID устройства и указателем на данные конфигурации. Всё просто.

Мой коллега реализовал эту систему с помощью хэш-таблицы. «Поиск за O(1), — сказал он уверенно, — лучше уже некуда».

Но загрузчик работал медленно. Недопустимо медленно. Время загрузки должно было находиться в пределах 100 мс, но превышало это значение на три порядка.

Я попробовал использовать очевидную оптимизацию: заменить хэш-таблицу двоичным поиском по отсортированному массиву. Двоичный поиск занимает O(log n), что теоретически хуже, чем O(1). Так написано в учебниках. Мой преподаватель алгоритмов был бы разочарован.

Но в результате загрузчик оказался на 40% быстрее.

Как O(log n) смогло победить O(1)? Что происходит?

Читать далее

Насколько быстро браузеры могут обрабатывать данные в Base64?

Level of difficultyEasy
Reading time3 min
Reach and readers8K

Base64 — это схема кодирования двоичных значений в текст, преобразующая произвольные двоичные данные (например, изображения, файлы или любые байтовые последовательности) в безопасную печатную ASCII-строку, состоящую из 64-символьного алфавита (A–Z, a–z, 0–9, +, /). Браузеры применяют эту схему в JavaScript для встраивания двоичных данных непосредственно в код/HTML или для передачи двоичных данных в виде текста.

Недавно в браузерах появились удобные и безопасные функции для обработки Base64: Uint8Array.toBase64() и Uint8Array.fromBase64(). Хоть у них и есть множество параметров, смысл их сводится к кодированию и декодированию.

При кодировании они берут 24 бита из входных данных и разделяют их на четыре сегмента по 6 бит, и каждое 6-битное значение (в интервале от 0 до 63) соотносится с конкретным символом из алфавита Base64: первые 26 символов — это буквы A-Z в верхнем регистре, следующие 26 — a-z в нижнем, затем идут цифры 0-9 и, наконец, символы «+» и «/» в качестве 62-го и 63-го символов. Если длина входных данных не кратна трём байтам, то в качестве заполнителя используется знак «=».

Насколько же быстро могут работать эти функции?

Читать далее

Как должно выглядеть ревью кода в эпоху LLM

Level of difficultyEasy
Reading time3 min
Reach and readers10K

Недавно я активно занимался отправкой и проверкой пул-реквестов проекта PyTorch, созданных с существенной помощью LLM. Этот процесс сильно отличается ситуации в начале года, когда было понятно, что LLM вполне подходит для проектов, создаваемых с нуля, но для кодовой базы в продакшене их код оставался безнадёжно низкокачественным. Можете посмотреть мои смердженные PR, в описании которых упоминается Claude Code; у Джейсона Энсела тоже был подобный опыт (ссылка на Meta*; также есть список issue, на которые он ссылался в совей статье). Сейчас всё активнее обсуждается (Саймон УиллисонLLVM) то, как процесс код-ревью должен адаптироваться к этой новой эпохе LLM. Мой вклад в эти обсуждения таков: внутри команд код-ревью должен поменяться, превратившись в первую очередь в механизм согласования с человеком.

Читать далее

Как на самом деле выглядит необработанное фото

Level of difficultyEasy
Reading time3 min
Reach and readers31K

Вот фотография новогодней ёлки в том виде, в котором видит матрица камеры.

Она даже не чёрно-белая, а серо-серая.

Причина этого в том, что хотя аналогово-цифровой преобразователь (АЦП) камеры теоретически способен выдавать значения от 0 до 16382, данные не покрывают весь этот диапазон.

Читать далее
1
23 ...

Information

Rating
Does not participate
Location
Россия
Registered
Activity