Комментарии (34)
- Структура Strip занимает 8 байт, но автор утверждает, что 259×64+7296 ≈ 24 КБ, что вызывает сомнения в правильности подсчёта памяти.
- Участники обсуждения предполагают, что речь идёт о кэш-линии в 64 байта и false-sharing, а не о фактическом размере структуры.
- Появился вопрос о том, какие именно бенчмарки корректности используются, и как можно было бы проверить корректность рендеров.
- Также обсуждалось, что вывод рендерера является растровое изображение, что требует копирования на GPU, что может быть не нужно на UMA-системах.
Error ABI
Статья рассматривает проблемы ABI (Application Binary Interface) при обработке ошибок в программировании. Распространённое мнение, что заполнение информации об ошибках "бесплатно" из-за их редкости, неверно. Наивное составление ошибок из алгебраических типов данных (ADT) ухудшает "счастливый путь" выполнения кода. Объекты ошибок, рекурсивно составленные из перечислений, tend to be large, увеличивая size_of<Result<T, E>>, что заставляет функции по всей стеку вызовов использовать возврат больших структур через память. "Вирусность" ошибок означает, что даже одна большая ошибка на редко выполняемом пути ухудшает производительность везде.
Поэтому зрелые библиотеки обработки ошибок скрывают их за тонким указателем, как в Rust (failure и anyhow), но это требует глобального аллокатора, что тоже не бесплатно. Автор предлагает три подхода к возврату результатов: стандартный (как пользовательский тип), более умный (ABI как у T с зарезервированным регистром для E) и радикальный (полное совпадение ABI с -> T и разворот стека для ошибок). Последний, по мнению автора, может быть оптимальным, несмотря на отсутствие надёжных бенчмарков. Вывод: обработка ошибок должна быть специальной для компилятора, особенно в языках со средним уровнем абстракций.
Комментарии (31)
- Адаптивные ABI для статически линкуемых программ могут оптимизировать производительность за счёт контекстного анализа использования функций.
- Проблема "вирусности" больших типов ошибок: даже редкие большие ошибки могут ухудшить производительность всего стека вызовов.
- Альтернативные подходы к обработке ошибок включают тонкие указатели с vtable (anyhow/failure) и разделение Result<T,E> при значительном различии размеров T и E.
- Добавление исключений в Rust вызывает споры: одни видят в этом угрозу производительности, другие — потенциальное решение проблем обработки ошибок.
- Checked exceptions в Java критикуют за необходимость изменения кода при модификации исключений, хотя другие видят в этом преимущество для надёжности кода.
I built the same app 10 times: Evaluating frameworks for mobile performance
Разработчик создал одно и то же мобильное приложение 10 раз на разных фреймворках, чтобы сравнить их производительность. Новые фреймворки (Marko, SolidStart, SvelteKit, Qwik) показывают практически мгновенную загрузку с временем First Contentful Paint в диапазоне 35-39мс, что в 12-13 раз быстрее, чем у Next.js. Реальная разница между лидерами минимальна — все они ощущаются как мгновенные, а ключевым фактором становится размер бандла.
Марко стал чемпионом по размеру бандла, достигая всего 28.8 kB в сжатом виде, что в 6.36 раза меньше, чем у Next.js (176.3 kB). Qwik City использует паттерн "возобновляемости", устраняя традиционную гидратацию и обеспечивая мгновенную интерактивность для крупных клиентских приложений. Автор рекомендует выбирать фреймворки на основе приоритетов проекта, а не микро-разниц в метриках производительности.
Комментарии (87)
- Svelte/SvelteKit и Solid/SolidStart показали наилучшую производительность и удобство разработки, особенно в мобильных условиях.
- React критикуют за фундаментальные проблемы производительности и большие размеры бандлов, несмотря на его популярность.
- Многие разработчики предпочитают использовать знакомые стеки (например, Django/React) вместо поиска "самого быстрого" решения, ценя скорость и комфорт разработки.
- Статья вызвала споры о важности оптимизации для мобильных устройств и критику за игнорирование нативных разработок и PWA.
- Стиль и содержание статьи были раскритикованы как "ChatGPT-slop" за шаблонность и отсутствие глубины.
Why your social.org files can have millions of lines without performance issues
Org Social решает проблему производительности при работе с большими файлами лент через трехслойный подход. Традиционный метод загрузки всех лент последовательно приводит к потере пропускной способности и блокировке интерфейса, так как приходится скачивать до 300KB данных для обработки 1500 постов, когда пользователю нужны лишь 10 последних. Новая система использует одновременную обработку до 20 лент в очереди с автоматическим восстановлением при ошибках.
Второй слой - HTTP Range-запросы, позволяющие загружать только нужные части файлов вместо полной загрузки. Система сначала определяет заголовок и размер файла, затем скачивает только свежие посты. Для платформ без поддержки Range (Cloudflare, Codeberg) предусмотрен автоматический переход к полной загрузке. Третий слой - алгоритм, который находит нужные посты, минимизируя объем данных. В результате вместо 27KB загружается всего 3KB, что значительно повышает производительность.
Комментарии (8)
- @dietr1ch критикует использование HTTP range requests для выборочного доступа к данным, предлагая вместо этого использовать файловую систему для эффективной выборки документов.
- @ChrisArchitect предоставляет контекст, указывая, что Org-social — это децентрализованная социальная сеть, работающая на базе Org Mode.
- @mjmas исправляет опечатку, заменяя "Lines" на "Millions of Lines".
- @pshirshov задает вопрос о популярности проекта.
- @dang благодарит за исправление.
How memory maps (mmap) deliver faster file access in Go
Memory maps (mmap) в Go позволяют отображать файлы непосредственно в адресное пространство процесса, избегая копирования данных через буферы. Этот подход устраняет необходимость в системных вызовах read/write, позволяя процессору обращаться к файловой памяти так же, как к обычной памяти. Техника особенно эффективна для больших файлов, когда требуется частый доступ к разным участкам данных, так как mmap обеспечивает постоянное время доступа к любой части файла.
Тесты показали впечатляющие результаты: mmap обеспечивает до 25-кратное ускорение по сравнению с традиционным чтением файлов. В одном эксперименте обработка 1.2GB JSON-файла через заняла 0.4 секунды с mmap против 10 секунд с использованием стандартного пак ioutil. Однако mmap имеет ограничения: он не подходит для очень больших файлов, которые могут не поместиться в виртуальном адресном пространстве, и требует осторожного управления при работе с несколькими процессами. Для оптимальной производительности mmap лучше всего работает с файлами, которые считываются целиком или accessed случайным образом, а не последовательно.
Комментарии (118)
- Обсуждение показало, что mmap не всегда быстрее обычного чтения, особенно при последовательном чтении, и что его преимущество в основном в нишевых сценариях, таких как случайный доступ к большим файлам.
- Участники отметили, что mmap требует осторожности при работе с файлами, которые могут быть изменены или усечены, поскольку это может вызвать ошибки доступа.
- Также было отмечено, что в некоторых ситуациях, таких как чтение из файла в последовательном режиме, обычное чтение может быть предпочтительнее, особенно если файл больше размера оперативной памяти.
- Обсуждение также затронуло влияние различных файловых систем и их взаимодействия с mmap, включая то, что некоторые файловые системы могут не поддерживать mmap или могут вести себя неожиданно при его использовании.
- В конце обсуждение подвело к выводу, что выбор между использованием mmap и обычного чтения должен быть сделан на основе конкретного сценария и требований к производительности, а не на основе предвзятого убеждения в превосходстве одного над другим.
SourceFS: A 2h+ Android build becomes a 15m task with a virtual filesystem
SourceFS — высокопроизводительная виртуальная файловая система, которая ускоряет сборку Android в 9 раз, снижает вычислительные затраты в 14 раз и уменьшает использование диска в 83 раза. Современные кодовые базы огромны: Linux содержит 40 миллионов строк кода, Android AOSP — 140 миллионов+, а автомобильные системы — до 500 миллионов строк. Медленные сборки и выгрузки кода отнимают часы времени разработчиков и милли долларов на CI-ресурсах.
SourceFS виртуализирует всё, материализуя файлы по требованию, что ускоряет выгрузку кода более чем в 10 раз. Система создает виртуальное представление всей кодовой базы и материализует файлы только при необходимости, экономя сотни гигабайт дискового пространства. Для сборки SourceFS использует легковесные песочницы, записывая все шаги и повторяя их для идентичных операций. В результате сборка ускоряется до 10 раз, а на обычном разработческом компьютере — более чем в 9 раз по сравнению с традиционными методами.
Комментарии (52)
- Обсуждение в основном вращается вокруг производительности сборки, кэширования и ценообразования, а также того, насколько продукт действительно уникален по сравнению с другими инструментами.
- Участники обсуждают, насколько продукт может быть полезен для больших кодовых баз, которые не помещаются в памяти, и как он справляется с инкрементальной сборкой.
- Некоторые комментаторы подчеркивают, что продукт, похоже, не открытый исходный код, и что это может быть препятствием для его принятия.
- Также обсуждается, что продукт может быть полезен для больших кодовых баз, но неясно, будет ли он работать с другими языками программирования, кроме Android.
The death of thread per core
В асинхронных рантаймах, таких как async Rust, задачи могут приостанавливаться и порождать новую работу, что приводит к двум основным подходам: thread-per-core и work-stealing. При work-stealing потоки могут "воровать" задачи друг у друга, обеспечивая лучшую балансировку нагрузки, хотя это требует возможности перемещения задач между потоками (что вызывает сложности в Rust с требованием Send) и может нарушать локальность данных. В обработке данных долгое время доминировал подход thread-per-core, так как он минимизирует перемещение данных между ядрами и упрощает реализацию, особенно при случайных ключах.
Однако в последние годы наблюдается сдвиг в сторону динамического перераспределения работы на уровне обработки данных. Растущее количество ядер делает неравномерное распределение данных более болезненным, а улучшение производительности ввода-вывода снижает значимость старых ограничений. Подход Morsel-Driven Parallelism предлагает, что системы обработки данных могут быть лучшим местом для динамического перераспределения работы. Это подкрепляется культурными факторами: при масштабировании и мультиарендности проблемы неравномерной нагрузки становятся сложнее для решения на верхних уровнях, требуя встроенной гибкости в самих системах.
Комментарии (50)
- Обсуждение вращается вокруг вопроса, что межъядерная коммуникация может быть настолько дорогой, что даже при наличии 255 ядер лучше всего использовать только одно ядро.
- Участники обсуждают, что важно не только количество ядер, но и то, как они используются, и что важнее всего - это архитектура приложения и то, насколько оно может быть распараллелено.
- Также обсуждается, что важно учитывать, что не все задачи одинаково подходят для параллельной обработки, и что важно правильно оценивать, когда стоит распараллеливать задачу, а когда нет.
- Участники также обсуждают, что важно иметь в виду, что современные языки программирования и среды разработки предоставляют инструменты, которые могут помочь в управлении потоками и задачами, и что важно использовать их правильно.
The future of Python web services looks GIL-free
Python 3.14 представляет значительный прорыв для многопоточного Python, так как его "free-threaded" вариант достиг фазы II и больше не считается экспериментальным. Производительность улучшилась с 35% штрафом до всего 5-10%, а реализация теперь использует адаптивный интерпретатор без обходных решений из Python 3.13. Автор, разработчик веб-фреймворка и веб-сервера, провел сравнительное тестирование для веб-приложений, так как большинство существующих сервисов являются I/O-bound, а многопоточность критически важна.
Для тестирования были созданы ASGI-приложение на FastAPI и WSGI-приложение на Flask, каждый с двумя конечными точками: генератором JSON и имитацией I/O-операции с задержкой 10мс. Использовался сервер Granian, который работает с потоками вместо процессов, и инструмент rewrk для нагрузки. Цель - определить, можно ли наконец отказаться от гигабайтов памяти, тратимых на multiprocessing для параллельной обработки запросов в веб-сервисах.
Комментарии (78)
- Появление free-threading в 3.13-3.14 сделало C-расширения, не обновлённые под free-threading, небезопасными и требует их обновления.
- Сообщество обсуждает, что отсутствие GIL в 3.14 может привести к тому, что старые библиотеки будут вести себя непредсказуемо, и предлагает, чтобы CPython поставлял инструмент для сканирования и обновления кода.
- Участники обсуждают, что влияние free-threading на производительность варьируется от «ничего не изменилось» до «значительно лучше» в зависимости от IO vs CPU bound кода, и что влияние на реальные приложения будет варьироваться от «ничего» до «значительно лучше».
Cache-Friendly B+Tree Nodes with Dynamic Fanout
Для высокой производительности B+Tree узлы должны размещаться в памяти как единый непрерывный блок, что улучшает локальность данных и повышает вероятность попадания в кэш процессора. В C++ это требует отказа от std::vector из-за дополнительной косвенности через отдельное выделение памяти, и вместо этого используется гибкий массив (flexible array member) — техника, унаследованная из C. Массив переменной длины объявляется как последний член структуры без указания размера, что позволяет динамически выделять память под весь узел и его записи единым блоком.
Такой подход устраняет фрагментацию памяти, но усложняет реализацию: требуется ручное управление освобождением памяти, сложности с наследованием и добавлением новых полей, а также необходимость переизобретать функциональность стандартных контейнеров. Несмотря на эти недостатки, метод остаётся необходимым компромиссом для достижения максимальной производительности в системах, критичных к скорости доступа к данным.
Комментарии (14)
This pattern was officially standardized in C99,No it wasn't; the C99 flexible array uses [] not [1] or [0].When using the [1] hack, you cannot use the sizeof the structure to get the offset, because it includes the [1] array.When using C99, you also cannot use sizeof to get th
86 GB/s bitpacking with ARM SIMD (single thread)
Проект демонстрирует технику оптимизации производительности через упаковку данных в байтовые структуры для минимизации памяти и ускорения обработки. Основная идея — использование примитивных типов и битовых операций вместо высокоуровневых структур, что особенно эффективно в системах с ограниченными ресурсами или требующих высокой пропускной способности.
В примерах показано, как сократить размер данных в 4–8 раз по сравнению с традиционными подходами, например упаковывая несколько булевых значений в один байт или используя битовые маски для хранения состояний. Это не только экономит память, но и улучшает производительность за счёт лучшей локализации данных и уменьшения аллокаций. Практический вывод: для критичных к производительности задач стоит рассматривать низкоуровневую оптимизацию данных, даже если она усложняет код.
Комментарии (24)
- Проблемы совместимости кода для x86 и ARM архитектур, включая необходимость использования библиотеки SIMDe для эмуляции x86 intrinsics на ARM.
- Обсуждение особенностей и ограничений NEON (ARM SIMD) по сравнению с SSE (x86 SIMD), включая отсутствие инструкции movemask и предложенные альтернативы.
- Потенциальные применения алгоритма для эффективной битовой упаковки и распаковки данных в задачах, чувствительных к пропускной способности памяти (например, в data warehouses).
- Критика методологии бенчмарков и сравнений в исходном исследовании, анонс собственной работы по схожей тематике.
- Рекомендации к дополнительным материалам по теме: научная статья обобщающая алгоритмы и статья о симуляции bit packing на NEON.
PEP 810 – Explicit lazy imports 🔥 Горячее 💬 Длинная дискуссия
Предлагается добавить в Python явный синтаксис для ленивого импорта с ключевым словом lazy, которое размещается перед import или from. Это позволяет отложить загрузку модуля до момента первого использования импортированного имени, сокращая время запуска и потребление памяти. Особенно полезно для CLI-инструментов с подкомандами, где даже вызов --help может загружать десятки ненужных модулей.
Ленивые импорты сохраняют полную обратную совместимость — обычные импорты работают как раньше, а новые семантика активируется только при явном указании. Механизм использует прокси-объекты, которые заменяются реальными значениями при первом обращении (реификация). Это решает проблему ручного переноса импортов в функции, что уже делается в 17% случаев в стандартной библиотеке.
Комментарии (222)
- Предложение о ленивых импортах (PEP 690) поддерживается для ускорения запуска CLI-инструментов и уменьшения накладных расходов, но вызывает опасения по поводу непредсказуемости и ошибок времени выполнения.
- Критики указывают на проблемы с безопасностью потоков, сложность отладки и потенциальное ухудшение читаемости кода из-за нового синтаксиса (
lazy import). - Альтернативы включают ленивую загрузку на уровне модуля (с явным объявлением модулем), использование существующих методов (импорт внутри функций) или инструментов вроде
LazyLoader. - Некоторые участники выступают за ленивые импорты по умолчанию, но признают, что это нарушит обратную совместимость из-за побочных эффектов при импорте.
- Обсуждаются вопросы совместимости с линтерами, влияние на стандартную библиотеку и необходимость обновления парсеров и редакторов для поддержки нового ключевого слова.
What .NET 10 GC changes mean for developers 🔥 Горячее 💬 Длинная дискуссия
В .NET 10 сборщик мусора получает серьёзные улучшения, которые могут вдвое или втрое сократить использование памяти и повысить производительность. Ключевые изменения включают расширенный escape-анализ для выделения объектов на стеке, оптимизацию делегатов и настройку размеров регионов кучи. Также активирована система DATAS, автоматически адаптирующая сборку мусора под поведение приложения, особенно в контейнерах.
Однако эти улучшения требуют осторожного подхода: они доступны через runtime-флаги и могут иметь компромиссы, например, увеличение пауз или нагрузки на CPU. Разработчикам стоит тестировать новые настройки в боевых сценариях, а не включать их вслепую. Инструменты мониторинга, такие как счетчики GC и дампы памяти, помогут оценить эффект для конкретного приложения.
Комментарии (196)
- Пользователи отмечают значительное повышение производительности в .NET 10 по сравнению с .NET 8, особенно в приложениях для анализа аудио и текста.
- Высказываются опасения, что оптимизации .NET могут отдалить его от совместимости с WASMGC, что критично для использования в браузере.
- Обсуждаются потенциальные риски, такие как переполнение стека в программах, которые ранее работали стабильно, и сложность настройки GC.
- Упоминаются альтернативные фреймворки для кроссплатформенной разработки (Avalonia, Flutter, MvvmCross) на фоне скептического отношения к стабильности и будущему MAUI.
- Поднимаются вопросы о применимости .NET для high-frequency trading и оптимизации LINQ, а также о сравнении с JVM и другими языками (F#, Java).
Safe zero-copy operations in C#
В C# операции с массивами автоматически проверяют границы для безопасности, что может снижать производительность. Компилятор способен убрать проверки в идеальных условиях, например, в цикле с известными границами, но при передаче индексов извне проверки остаются, что видно по ассемблерному коду.
Можно использовать небезопасный код с указателями для полного избежания проверок, но это рискованно: ошибки ведут к сбоям или уязвимостям. Решение — тип Span<T>, который объединяет ссылку на данные и их длину, обеспечивая безопасный нулевой копирование доступ без дополнительных проверок. Это позволяет писать высокопроизводительный код без ущерба для безопасности, сочетая низкоуровневую эффективность с управляемыми гарантиями.
Комментарии (60)
- Использование
Span<T>иReadOnlySpan<T>позволяет избежать лишних выделений памяти и повысить производительность, особенно при работе со строками и массивами. - Эти структуры предоставляют безопасные, безграничные представления памяти, заменяя необходимость в небезопасном коде и указателях.
- Оптимизации с помощью
Span<Tнаиболее эффективны в сценариях с интенсивной обработкой данных (например, парсинг, игры), а не в типичных CMS, где узкие места обычно в БД или кэше. - Внедрение
Span<Tв .NET стандартизировало ранее фрагментированные подходы к работе с памятью, улучшив interoperability между библиотеками. - Хотя
Span<Tпредлагает контроль, сравнимый с Rust, он ограничен моделью сборки мусора C#, тогда как Rust предоставляет более строгие гарантии времени жизни на уровне компилятора.
Optimizing a 6502 image decoder, from 70 minutes to 1 minute
Изначальный алгоритм декодирования изображений с камеры Quicktake 150 на процессоре 6502 работал 70 минут. Основная проблема — сложный формат сжатия на основе кодирования Хаффмана и 16-битной математикой, что крайне неэффективно для 8-битного процессора.
Оптимизация началась с отказа от декодирования цвета, поскольку итоговое изображение было монохромным. Это сократило обработку только до зелёных пикселей матрицы Байера. Далее удалили ненужные буферы и промежуточные шаги, включая интерполяцию, что уменьшило размер вывода до 320×240. В итоге остался лишь один используемый буфер из трёх. Эти изменения сократили количество инструкций с 301 млн до 25 млн, а время декодирования — до минуты. Ключевой вывод: алгоритмическая оптимизация даёт больший выигрыш, чем низкоуровневая оптимизация кода.
Комментарии (28)
- Обсуждение визуального парадокса: изображение с чередующимися черными пикселями кажется более четким, чем изображение без них, несмотря на одинаковый объем информации.
- Ностальгия по низкоуровневому программированию и работе с аппаратными ограничениями (память, скорость) как к refreshing практике для инженеров.
- Технические гипотезы о причинах визуальных артефактов (масштабирование браузером, адаптивная подсветка, HDR-режим).
- Критика современного ПО: несмотря на миллионократный рост мощности hardware, воспринимаемая скорость и отзывчивость интерфейсов часто снизились.
- Идея о необходимости целенаправленной оптимизации кода для повышения эффективности, возможно, с использованием ИИ.
Baldur's Gate 3 Steam Deck – Native Version 🔥 Горячее 💬 Длинная дискуссия
Larian Studios выпустила нативную версию Baldur's Gate 3 для Steam Deck, которая работает без слоя совместимости Proton. Это снижает нагрузку на процессор и потребление памяти, улучшая производительность. Игроки могут проверить установку через настройки Steam, выбрав инструмент совместимости с Linux Runtime.
Сохранения в нативной версии хранятся в папке /home/deck/.local/share/Larian Studios/, а не в compatdata. Steam Cloud автоматически синхронизирует последние сейвы, но старые можно перенести вручную через режим рабочего стола. Larian подчёркивает, что поддержка Linux ограничена только Steam Deck.
Комментарии (386)
- Обсуждается выпуск нативной версии Baldur's Gate 3 для Steam Deck, отмечаются усилия Larian и улучшение производительности (~10% FPS в Act 3) по сравнению с версией на Proton.
- Поднимаются вопросы о термине "нативная" версия: означает ли она Linux-бинарник, оптимизацию под железо Steam Deck или просто правильную конфигурацию, а также о возможности её запуска на других Linux-дистрибутивах.
- Участники делятся личным опытом игры на Steam Deck: одни хвалят работу через Proton, другие сталкивались с проблемами производительности и теперь рады нативному релизу.
- Критикуется позиция Larian о непредоставлении поддержки для платформы Linux в целом, при этом выпуске сборки под конкретное устройство (Steam Deck) на Arch Linux.
- Затрагиваются смежные темы: проблемы с производительностью старого железа, сравнение с другими играми, баги в процессе обновления через Steam на Linux.
Context Engineering for AI Agents: Lessons
Контекстная инженерия для AI-агентов — это ключевой подход, позволяющий быстро итеративно улучшать производительность без переобучения моделей. Опыт разработки Manus показал, что вместо обучения end-to-end модели эффективнее использовать способность современных LLM к обучению в контексте, что сокращает цикл улучшений с недель до часов и делает продукт независимым от прогресса базовых моделей.
Важнейший метрикой для продакшн-агентов является hit rate KV-кеша, напрямую влияющий на задержки и стоимость. Агент работает итеративно: на каждом шаге контекст растёт за счёт добавления действий и наблюдений, в то время как вывод остаётся коротким. Оптимизация этого процесса через структурирование контекста позволяет снизить вычислительные расходы и ускорить выполнение задач.
Комментарии (4)
- Предлагается использовать файловую систему как память для агентов через директорию
.agent/для хранения задач, планов и других данных. - Проводятся параллели между лучшими практиками для AI-агентов и управления кодом: избегать раздувания, не удалять плохие коммиты, не рефакторить слишком часто.
- Отмечается разница в стимулах для кеширования: на фиксированных тарифах выгодно провайдеру, на поминутных — пользователю.
- Рекомендуется простота в инструментарии, согласующаяся с подходом OpenAI Codex, например, использование
update_planдля отслеживания прогресса.
Wild performance tricks
В Wild-линковщике для Rust применяют несколько продвинутых техник оптимизации параллельной работы. Например, используют split_off_mut для безопасного разделения мутабельных срезов Vec<SymbolId> между потоками, что позволяет обрабатывать символы каждого объекта параллельно без блокировок, сохраняя кэш-локальность.
Для инициализации вектора без задержек на основном потоке задействуют крейт sharded-vec-writer: предварительно аллоцируют память, разбивают её на сегменты по числу символов в объектах и заполняют их параллельно через VecWriter, что ускоряет стартовую фазу.
В случаях, когда требуются случайные записи в общий вектор (например, для обработки дубликатов символов в C++), переходят на атомарные операции. Вместо стабильного, но ограниченного AtomicU32::from_mut_slice (только nightly) или постоянного использования атомиков (что снижает производительность), временно преобразуют &[SymbolId] в &[AtomicSymbolId] через unsafe-конверсию, экономя на издержках синхронизации в основном коде.
Комментарии (67)
- Обсуждаются оптимизации Rust, такие как преобразование
VecвIntoIterи обратно для эффективного повторного использования аллокации, что реализовано в стандартной библиотеке как специальный случай. - Высказываются предостережения против некоторых "трюков производительности", например, перемещения аллокации в другой поток для освобождения, из-за особенностей работы аллокаторов и сомнительной выгоды.
- Поднимается вопрос о надёжности оптимизаций компилятора (LLVM) в релизных сборках, которые могут меняться между версиями и сложны для верификации, что контрастирует с медленными debug-сборками.
- Отмечается, что многие трюки направлены на обход ограничений borrow checker для получения разрешения на выполнение операций, а не на решение аппаратных задач производительности.
- Обсуждается преимущество Rust в безопасном параллелизме (например, с Rayon) по сравнению с C/C++, где обеспечение потоковой безопасности значительно сложнее.
Default musl allocator considered harmful to performance
musl-аллокатор тормозит в 7 раз
Добавь в main.rs:
#[cfg(target_env = "musl")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
и в Cargo.toml:
[target.'cfg(target_env = "musl")'.dependencies]
mimalloc = "0.1"
Проблема — конкуренция потоков за malloc. Чем больше потоков, тем хуже.
Замена аллокатора нужна даже для однопоточных программ: забудешь — потом дорого.
Почему musl? Статика + 2 МБ distroless-контейнер = старые RH и быстрый cold-start.
Кейс: сервер обрабатывал данные в 7 раз медленнее хоста.
Виноват 200 000 контекст-переключений/сек vs 1 200 у glibc.
strace показал 6,7 с в futex у musl против 0,5 с у glibc.
Комментарии (53)
- musl-аллокатор медленен в многопоточных Rust-программах из-за одного глобального замка.
- Альпийские образы компактны, но «размер» ≠ «скорость»; в проде чаще берут Debian/RHEL.
- Замена: jemalloc, mimalloc или Wolfi (glibc, apk, busybox) без смены менеджера пакетов.
- glibc тоже фрагментирует память; MALLOC_ARENA_MAX=jemalloc спасает.
- Новый mallocng musl не решает конкуренцию потоков: цель musl — минимум кода и харднинг, а не perf.
Hitting Peak File IO Performance with Zig
Как выжать максимум из файлового IO в Linux на Zig + io_uring
Тест
- Железо: Ubuntu 24.04, 6.14, NVMe, 32 ядер, 756 ГБ ОЗУ (не влияет,
direct_io). - Параметры: 512 КБ блок, очередь 64, 16 ГБ файл, один поток.
Результаты
| fio | Zig | |
|---|---|---|
| write | 4.08 ГБ/с | 3.80 ГБ/с |
| read | 7.33 ГБ/с | 7.00 ГБ/с |
Zig на 5–7 % медленнее fio, но близко к пределу SSD.
Ключевые фишки реализации
- Полл-режим (
IOPOLL) +nvme.poll_queues=16→ прерывания не нужны. - Два экземпляра io_uring: один с
IOPOLL(только read/write), второй – для остального. - Зарегистрированные буферы: память выделяется заранее, пользователь получает/возвращает готовые блоки.
- Выравнивание:
- чтение – внутренняя «перехватка» невыравненных запросов;
- запись – пользователь сам выравнивает, иначе лишние read-modify-write.
- Без слияния IO внутри библиотеки – проще и гибче на уровне приложения.
Код: steelcake/csio
Комментарии (11)
- Пользователи указывают, что 7 ГБ/с при 512 КБ блоках — это всего ~14 000 IOPS (~70 мкс/IO), и требуется лишь 1 предзагрузка для полной полосы.
- Напоминают, что Zig 0.15.1 всё ещё меняет IO-API, поэтому версию нужно явно указывать в посте.
- Рекомендуют проверить/увеличить логический блок NVMe до 4 КБ через nvme-format вместо хардкода ALIGN=512.
- Разница 4,08 ГБ/с (fio) и 3,80 ГБ/с (Zig) объясняется путаницей GiB ↔ GB.
- Для io_uring важно использовать registered fds — дают заметный прирост.
- Интересуются, как будет работать тот же код на FreeBSD AIO.
- Предлагают вместо «овераллокации» брать выровненную память через page-аллокатор.
Speeding up Unreal Editor launch by not spawning unused tooltips
Как ускорить запуск Unreal Editor: не создавать 38 000 тултипов
Unreal Editor запускается долго. Epic борется с этим кэшами, live-coding и прочими оптимизациями, но одна простая проблема оставалась незамеченной: на старте движок генерирует 38 000 виджетов-тултипов, хотя за сессию пользователь видит лишь десяток.
Профилирование показало, что SetToolTipText тратит ~0,2 мс на каждый тултип, но главное — он не просто сохраняет текст, а сразу создаёт полноценный виджет. В итоге:
- 2–5 с потеряно в дебаг-сборке
- до 1 с в development
- ~40 МБ ОЗУ занято невидимыми виджетами
Решение
- Заменить немедленное создание виджета на ленивое: сохранять только
FText. - Создавать виджет в момент первого обращения (
GetToolTip).
Патч — пара строк: убрать Spawn из сеттера, перенести его в геттер.
Результат: стартовое время падает на ~1 с, ОЗУ экономит десятки мегабайт, а в рантайме задержки не заметны — тултипы всё равно редко вызываются пачками.
Комментарии (79)
- UE создаёт 38 000 тултипов при старте редактора, что занимает до 2–5 с в дебаг-сборке и почти 1 с в дев-сборке.
- Каждый «тултип» — полноценный UI-виджет с саб-объектами, а не просто строка текста.
- Проблема решается ленивым или единичным созданием экземпляров, как в IMGUI/Unity/React-порталах.
- Участники жалуются на медленную итерацию UE: 10 мин компиляция пустого проекта, бесполезный блюпринт-бол, жадность до железа.
- Альтернативы: Godot (GDScript, быстрая итерация), Unigine, форк Hazelight с AngelScript.
How is Ultrassembler so fast?
Ultrassembler — библиотека RISC-V-ассемблера, встроенная в проект Chata.
В отличие от as и llvm-mc, она вызывается прямо из C++, без system() и временных файлов, что критично для встроенных систем.
Скорость
Тест на 16 тыс. инструкций:
- Ultrassembler ≈ 10× быстрее
as, 20× быстрееllvm-mc. - 1 RISC-V инструкция ≈ 1000 x86-инструкций (у конкурентов 10–20 тыс.).
Код на чистом C++; можно добавить ассемблерные вставки.
Ключевые оптимизации
Исключения
GCC-реализация «zero-overhead»: штрафа нет, пока исключений нет.
Ошибки встречаются редко и видны человеку, поэтому даже 1 с на обработку незаметна.
std::expected дал −10 %, так как нормальный путь стал дороже.
Быстрые структуры
2000+ RISC-V-инструкций требуют мгновенного поиска.
Вместо std::unordered_map используется perfect-hash таблица от gperf, генерирующая O(1) без коллизий.
Размер таблицы компактен, кэш-эффективен.
Парсинг
- Регистры идентифицируются по первым 2–3 символам через
switch. - Нет
std::string, толькоstd::string_viewи статические буферы. - Лексемы разбираются за один проход без регулярных выражений.
Кодогенерация
- Шаблоны на этапе компиляции формируют битовые маски инструкций.
- Варианты одной инструкции разворачиваются в
constexpr-таблицы, что убирает ветвления в рантайме.
Память
- Все выделения через стековые
std::array/std::string_view. - Нет
new/malloc, следовательно, нет аллокационных штрафов и кэш-промахов.
Платформенные трюки
[[likely]]/[[unlikely]]для подсказок ветвления.__builtin_expectтам, где компилятор не догадывается.- LTO + PGO дают ещё 5–7 %.
Итог
Ultrassembler показывает, что «низкоуровневый» C++ без искусственных ограничений может обгонять даже оптимизированные GNU-утилиты.
Комментарии (34)
- В обсуждении разобрали миф о «системном вызове при каждом росте контейнера» — реальные аллокаторы переиспользуют память и делают syscall лишь при нехватке.
- Участники напомнили, что исключения в C++ не «zero-overhead»; есть компромисс между временем и памятью, и g++ выбирает экономию места.
- Автор статьи подтвердил: пробовал хеширование, но дерево разбора оказалось быстрее; flex/bison тут не при чём, скорее gperf.
- Некоторые посоветовали LLVM C++ API, memory-mapped I/O и std::pmr для ускорения и упрощения кода.
- Большинство сходится: современные ассемблеры и так быстрые, задача скорее академическая, но как «посмотреть, насколько можно ускорить» — интересна.
How to make things slower so they go faster
Как замедлить, чтобы ускорить
Синхронный спрос — когда множество клиентов действуют одновременно.
Пропускная способность μ, фоновая нагрузка λ₀, запас H = μ − λ₀.
M клиентов, синхронизированных по крону, кэшу или перезапуску, превышают H, образуют очереди, таймауты, ретраи и инцидент.
Цель — размазать пик или безопасно его слить, сохраняя справедливость и лимиты.
Источники выравнивания:
- естественные — часы, TTL, SDK-таймеры;
- индуцированные — деплой, рестарт, сброс кэша, обновление токенов;
- внешние — DDoS, флеш-крауд.
Ограничения:
- мягкие — задержка в очереди;
- жёсткие — пулы соединений, дескрипторы, потоки.
Превышение даёт обрыв: ещё одно соединение → таймаут → ретраи → ещё больше нагрузки.
Важно, кто ждёт: онлайн-запросы требуют справедливости, офлайн — только пропускной способности.
Математика размагничивания
M действий равномерно в окне [0, W]:
ожидаемый пик M/W, средняя задержка W/2, произведение M/2 — константа.
Чем шире W, тем ниже пик, но выше средняя задержка.
Для выпуклой функции стоимости C(r) равномерное r(t)=M/W минимизирует ∫C(r)dt (неравенство Йенсена).
Равномерный джиттер оптимален и справедлив.
Практика
-
Детерминированные границы:
- M/W ≤ H ⇒ W ≥ M/H;
- (M/W)·s ≤ K ⇒ W ≥ Ms/K (s — p95 времени обработки, K — свободная конкурентность).
-
Вероятностные границы:
Пусть N ~ Poisson(M/W). Требуем Pr{N > H} ≤ ε.
Для H ≳ 50 нормальное приближение даёт
λε ≈ ((-z₁₋ε + √(z₁₋ε² + 4(H+0.5)))/2)²,
W ≥ M/λε.
Для малых H или ε считать точный хвост Пуассона. -
Подсказки от сервера:
- Retry-After: Δ → джиттер в [Δ, Δ+W];
- rate-limit: R оставшихся запросов до сброса через Δ → λadm = min(H, R/Δ) → W ≥ M/λadm.
-
Продуктовые ограничения:
- дедлайн D ⇒ W ≤ D;
- p95 добавленной задержки ≤ L ⇒ W ≤ L/0.95.
Минимально-ожидающая политика
Выбрать наименьший W, удовлетворяющий всем нижним границам и не превосходящий верхние.
Если невозможно — добавить мощность или ослабить ограничения.
Комментарии (26)
- Участники обсуждают парадоксы, при которых «ускорение» ведёт к замедлению: парадокс Браесса, парадокс Джевонса.
- Подчёркивается мысль «slow is smooth, and smooth is fast»: медленные, точные действия итогом быстрее и эффективнее, будь то код, строительство или музыка.
- Пример Facebook: задержки и очереди используются как ресурс — задачи выполняются ближе к максимально допустимому времени отклика, повышая общую пропускную способность.
- Упоминаются практические техники регулировки скорости: nanosleep по объёму данных, параметризуемые «тормоза» в долгих процессах.
- Итог: оптимизация — это не просто «делать быстрее», а баланс скорости, точности и использования ограниченных ресурсов.
Going faster than memcpy
Как обогнать memcpy
Профилируя Shadesmar, увидел: при больших (>512 КБ) сообщениях 97 % времени уходит на memcpy между процессной и разделяемой памятью. Решил ускорить копирование.
Разбор memcpy
perf показал:
__memmove_avx_unaligned_erms — это memcpy через memmove, AVX, 32 байта за раз, поддержка не выровненных блоков и ERMS (железный цикл REP MOVSB).
memmoveвыбран, т.к. допускает перекрытие.- Для <4 КБ используется SSE2, иначе —
REP MOVSB+ AVX. - Не-временные (
NT) инструкции иprefetcht0уменьшают кэш-промахи.
Способ 1: простой REP MOVSB
void _rep_movsb(void *d, const void *s, size_t n) {
asm volatile("rep movsb"
: "=D"(d), "=S"(s), "=c"(n)
: "0"(d), "1"(s), "2"(n)
: "memory");
}
Тот же цикл, что и в glibc, но без лишней логики.
Комментарии (63)
- Часть выгоды даёт отказ от лишнего копирования: часто данные можно использовать на месте.
- Несколько участников отмечают, что без контроля кэшей и правильной сериализации бенчмарки теряют смысл.
- График в конце вызывает сомнения: скачки пропускной способности выглядят неправдоподобно.
- Для IPC обсуждают zero-copy через размещение данных сразу в shared memory (Iceoryx, Boost.Interprocess, DPDK).
- Большинство сходится к выводу: для обычных задач лучше довериться стандартному
memcpy/std::memcpy, особенно в glibc.
Automerge 3.0 🔥 Горячее
Automerge — это движок синхронизации данных с приоритетом локальной работы, упрощающий создание коллаборативных приложений. Выпущена версия 3.0.
Главное обновление — резкое снижение потребления памяти. Ранее хранение полной истории документов могло приводить к гигабайтам в ОЗУ. В 3.0 память сокращена более чем в 10 раз (иногда значительно больше), что делает Automerge применимым в куда большем числе сценариев.
Также упразднены избыточные API, особенно при работе со строками.
Если вы уже используете Automerge, обновляйтесь: формат файлов тот же, API почти полностью обратно совместим. Подробности — в руководстве по миграции. Если вы ещё не пробовали, сейчас хорошее время — производительность и надежность сильно выросли.
Чтобы узнать, как достигнуты улучшения, читайте далее.
-
Улучшенное использование памяти
- Automerge хранит каждое изменение для офлайн-работы, конфликтов и истории; это требует большого объёма метаданных.
- Раньше: сжатый колоночный формат «на диске», но при загрузке в память — несжатый вид, из-за чего ОЗУ раздувалось.
- Теперь: сжатое представление используется и во время выполнения, давая огромную экономию. Пример: вставка «Моби Дика» — было ~700 МБ в v2, стало ~1,3 МБ в v3.
- Меньше памяти — стабильнее нагруженные сервера синхронизации.
- Для документов с длинной историей существенно ускорена загрузка (пример: с «не загрузилось за 17 часов» до 9 секунд).
-
Упрощение API
- Два типа строк: «коллаборативные» (сливают правки) и «неколлаборативные».
- В 1.0: обычные строки для неколлаборативных, класс Text — для коллаборативных.
- В 2.0 (namespace next): сделали коллаборативный текст по умолчанию — строки для него, RawString для неколлаборативного.
- В 3.0: закрепили новый подход — удалён Text, API next стал дефолтным; RawString переименован в ImmutableString.
-
Попробовать
- Automerge 3.0 используется по умолчанию в последних
@automerge/automerge-repoи@automerge/react(версия2.1.0). - Новичкам — туториал. Существующим кодовым базам — руководство по миграции; если зависите от
@automerge/automerge-repo, выполнитеnpm update @automerge/automerge. - Проблемы — создавайте issue; вопросы — в Discord.
- Automerge 3.0 используется по умолчанию в последних
Комментарии (29)
- Обсуждение вокруг Automerge 3.0: многие впечатлены скачком производительности и «local‑first» подходом к CRDT; сравнивают с Yjs, ElectricSQL, Convex, Zero и интересуются бенчмарками.
- Ключевой апгрейд: сжатое представление данных теперь используется на рантайме — память и время загрузки резко снижены (пример: «Моби Дик»: ~700 МБ → ~1,3 МБ в v3).
- Вопросы по применимости: когда выбирать Automerge/Yjs (совместное редактирование, rich text) vs ElectricSQL (сервер — источник истины, синхронизация приложения). Также интерес к настройкам для «одиночной» кросс‑девайс синхронизации.
- Технические вопросы: структура полурешётки, тип регистра карт (MV-Register vs LWW), поддержка перемещений в деревьях, permissioned-блоки в документе, интеграция с TipTap/ProseMirror, терминальные UI, C/Rust API и состояние C-обёртки.
- Ответы/подсказки: TipTap можно использовать, обернув схему атрибутами Automerge; undo/redo меняется соответствующим образом; ссылки на конфликты в доках; перемещения в деревьях прототипировались (Клепманн), но, похоже, ещё не в основном релизе.
- Практические интересы: какие железо/серверные ресурсы нужны для синка и сколько чтений/записей выдержит; запрос бенчмарков против Yjs и рекомендации по альтернативам (jsonjoy для перформанса).
- Сообщество делится опытом кастомных CRDT, типобезопасностью, бизнес‑правилами и тем, как это вписать в Automerge; часть аудитории всё ещё ищет простое объяснение, «что именно делает» инструмент.