I made a real-time C/C++/Rust build visualizer 🔥 Горячее
Я написал What the Fork — кроссплатформенный визуализатор сборки C/C++ (и не только).
Запуск: wtf make
, wtf cargo build
, wtf gradle build
, wtf -x
для Xcode и т.д.
Инструмент показывает все процессы, включая скрытые вызовы ld
, и ищет типичные проблемы:
- отсутствие
-j
уmake
, - однопоточная компиляция,
- повторяющиеся cmake/make-шаги,
- непараллельные CI-сборки.
Как работает
Сборка = дерево команд. Чтобы увидеть всё, ловим системные вызовы fork/exec/exit
:
- macOS — Endpoint Security API,
- Linux —
ptrace
, - Windows — Event Tracing (самое мерзкое API).
Что уже нашли
- cargo собирал зависимость одним потоком вместо 10× ускорения.
- ninja при сборке LLVM держит 12 задач на 10 ядрах — почти идеал.
- CMake 85 раз подряд вызывает
xcode-select
,sw_vers
, cmake/make → clang, не используя параллелизм.
Инструмент открыт для тестов — попробуйте на своём проекте.
Комментарии (82)
- Пользователи восторженно реагируют на новый визуализатор сборки, особенно те, кто застрял на CMake/GCC/Make без clang/ninja и не может понять, почему сборка тормозит.
- Просят сразу показать GIF-демонстрацию под заголовком статьи и спрашивают, будет ли macOS-версия и открытый код.
- Некоторые делятся опытом: strace/dtruss, ninjatracing, vcperf, cargo --timings, Instruments и другие инструменты уже решали похожие задачи.
- Предложения расширить функциональность: добавить flame-графы процессов, поддержку fork(), интеграцию с Bazel Build Event Protocol, оценку «осталось времени» по историческим данным.
- Отдельные комментарии касаются маркетинга (сменить название), сравнения с VS/Xcode, а также шуток про TEEP/OEE завода и «LLVM, завари кофе».
Compiler Bug Causes Compiler Bug: How a 12-Year-Old G++ Bug Took Down Solidity
Краткий обзор
- Проблема: компилятор Solidity (solc) падает на Ubuntu 22.04 при компиляции корректного кода.
- Причина: сочетание трёх факторов
- 12-летний баг G++ (< 14) в разрешении перегрузок.
- Устаревший паттерн сравнения в Boost.
- Новые правила симметричных сравнений C++20.
Цепочка событий
- Баг G++ (2012, GCC-53499): при
boost::rational<T> == 0
компилятор до 14-й версии выбирает нечлен-шаблон вместо член-шаблона. - C++20 добавляет автоматическую перестановку аргументов:
0 == rational<T>
→rational<T> == 0
. - Boost 1.74 предоставляет обе версии оператора, что приводит к бесконечной рекурсии и переполнению стека.
Минимальный пример
template<typename T>
struct rational {
template<class U>
bool operator==(const U&) const { return true; }
};
template<class U, class T>
bool operator==(const rational<T>&, const U&) { return false; }
int main() {
rational<int> r;
return r == 0; // g++11 выбирает free-функцию
}
Как починить
- Обновить GCC ≥ 14 или Clang, или
- Собрать Solidity без C++20 (
-std=c++17
), или - Патч Boost/использовать свежий Boost ≥ 1.82.
Итог
Ни один компонент по отдельности не «сломан», но их комбинация приводит к крашу компилятора на валидном коде.
Комментарии (32)
- Участники сетуют, что C++ стремительно усложняется ради «чуть более удобного» синтаксиса.
- @twoodfin возражает: главная цель — не синтаксис, а упрощение создания абстракций.
- @immibis сравнивает рост сложности C++ с переходом от ассемблера к C: каждая новая фича множит сложность, но и выгоду.
- @mgaunard предупреждает: даже минорные обновления компиляторов могут тихо ломать код, поэтому апгрейд требует тщательного тестирования.
Mistral Integration Improved in Llama.cpp
- Добавлена поддержка моделей Mistral-Small-3.1-24B-Instruct-2503 и Mistral-Small-24B-Instruct-2501
- Улучшена работа с Mamba-2 и Sliding Window Attention
- Новые правила конвертации:
convert-hf-to-gguf.py
теперь корректно обрабатываетsliding_window
,mamba2
,attention_bias
,tie_word_embeddings
- Обновлён
llama_model_loader
иllama_model
: добавлены поляmamba2
иsliding_window
, упрощена логика KV-cache - Поддержка
mamba2
вllama_context
иllama_decode
- Удалены устаревшие
llama_model
иllama_vocab
- Добавлены тесты
test-mistral.py
иtest-mistral-vision.py
Комментарии (11)
- Mistral предлагает mistral-common как официальный токенизатор, но пока только через Python-библиотеку и временный REST-обвязанный FastAPI.
- Сообщество жалуется: «cpp-бинарь, зависящий от Python-сервера — временное и грустное решение», ждут нативный C++ порт.
- Пользователи расстроены, что Mistral, выпуская веса, не сразу поддерживает llama.cpp, на котором держится большинство «домашних» запусков.
- Некоторые замечают, что llama.cpp и так тянет Python для шаблонов, но это не отменяет желания увидеть полноценную C++ реализацию.
- Сторонники Mistral отвечают: компания маленькая, пока не ясно, какие именно инференс-фреймворки поддерживать, зато открыли собственный mistral-inference.
Compiling a Lisp: Lambda lifting
Переписал Ghuloum-туториал на Python (~300 строк). Убрал читалку S-выражений и бинарный код — теперь текстовая ассемблерная печать.
Lambda-lifting требует:
- знать связанные переменные;
- собирать свободные переменные лямбд;
- накапливать создаваемые
code
-объекты.
Связывают let
и lambda
; для них обновляем окружение.
Lifter
class LambdaConverter:
def __init__(self):
self.labels = {}
def convert(self, expr, bound, free):
match expr:
case int() | Char() | bool():
return expr
case str() if expr in bound or expr in BUILTINS:
return expr
case str():
free.add(expr)
return expr
case ["if", t, c, a]:
return ["if",
self.convert(t, bound, free),
self.convert(c, bound, free),
self.convert(a, bound, free)]
lift_lambdas
запускает обход и возвращает (labels …)
.
Lambda
Лямбда:
- связывает параметры;
- выделяет код;
- захватывает внешнее окружение.
Пример:
(lambda () x) ; x свободна
превращается в
(labels ((f0 (code () (x) x)))
(closure f0 x))
Даже если x
связан снаружи, внутри лямбды он считается свободным.
Комментарии (15)
- Участники рекомендуют три современные книги по компиляторам, вдохновлённые статьёй Ghuloum: «Writing a C Compiler» (Sandler), «Essentials of Compilation» на Racket и Python (Siek).
- Обсуждали «lambda lifting»: преобразование, выносящее замыкания наверх, уменьшая их размер вплоть до полного исчезновения.
- Уточнили, что «lambda lifting» в статье связан с разделом 3.11 о сложных константах в Scheme.
- Разбирали, почему современный ИИ использует Python, а не Lisp: удобство как «клея» для C++/CUDA, упадок доли рынка Lisp и смена парадигмы ИИ.
A fast, growable array with stable pointers in C
Моя предыдущая статья о обобщённых структурах данных в C готовила почву к теме: структура, которая заменяет динамические массивы, даёт стабильные указатели и хорошо работает с аренными аллокаторами. Её переоткрывали много раз под разными именами: “levelwise-allocated pile” (2001), в Zig — Segmented List, частично похожая на C++ std::deque. Мне нравится название Per Vognsen — Segment Array.
Скачать мой однофайловый заголовок segment_array.h можно, подписавшись на рассылку.
Идея проста: фиксированный массив указателей на сегменты; каждый следующий сегмент вдвое больше предыдущего; новые сегменты выделяются по мере необходимости. Поскольку элементы не двигаются, указатели на них стабильны, не остаются “дыры” в арене, а доступ по индексу — за O(1).
Реализация
Структура на C:
typedef struct { u32 count; int used_segments; u8 *segments[26]; } SegmentArrayInternal;
Почему всего 26 сегментов? Из 64 бит указателя обычно реально используются 48, так что 49 сегментов уже перекрывают адресное пространство (~256 ТиБ). Я предпочитаю индекс u32 (до ~4 млрд элементов) — это даёт 32 сегмента. Ещё убираем 6 маленьких (1..32), начинаем с 64, остаётся 26 сегментов — хватает для 4 294 967 232 элементов (чуть меньше UINT32_MAX). Фиксированный массив рядом со структурой снижает риск промаха кэша.
Размеры сегментов — степени двойки: проще математика и быстрые сдвиги для индексов.
#define SMALL_SEGMENTS_TO_SKIP 6
#define log2i(X) ((u32) (8*sizeof(unsigned long long)
- __builtin_clzll((X)) - 1))
u32 capacity_for_segment_count(int segment_count) { return ((1 << SMALL_SEGMENTS_TO_SKIP) << segment_count) - (1 << SMALL_SEGMENTS_TO_SKIP); }
void *_sa_get(SegmentArrayInternal sa, u32 index, size_t item_size) { int segment = log2i((index >> SMALL_SEGMENTS_TO_SKIP) + 1); u32 slot = index - capacity_for_segment_count(segment); return sa->segments[segment] + item_sizeslot; }
log2i использует __builtin_clzll (подсчёт ведущих нулей) для быстрого вычисления номера сегмента.
Clang оптимизирует _sa_get до ~10 инструкций x86-64 (-O3), так что узким местом будет память, а не вычисления индекса. При последовательной итерации можно обходить сегменты напрямую; в segment_array.h есть макрос.
Выделение нового элемента:
u32 slots_in_segment(int segment_index) { return (1 << SMALL_SEGMENTS_TO_SKIP) << segment_index; }
void *_sa_alloc(SegmentArrayInternal *sa, size_t item_size) { if (sa->count >= capacity_for_segment_count(sa->used_segments)) { size_t segment_size = item_size * slots_in_segment(sa->used_segments); sa->segments[sa->used_segments++] = malloc(segment_size); } sa->count++; return _sa_get(sa, sa->count-1, item_size); }
Замечание: можно сделать ёмкость строго степенью двойки, если первые два сегмента одинакового размера. Код станет менее изящным, но это спасает от ~50% потерь памяти при использовании как массива бакетов в хеш-таблице со степенью двойки.
Дженерики
Я применяю технику из прошлой статьи для типобезопасного хранения любого типа. Макрос связывает тип с общей структурой:
#define SegmentArray(type)
union {
SegmentArrayInternal internal;
type *payload;
}
Дальше макросы используют payload, чтобы передавать сведения о типе…
Комментарии (77)
- Обсуждается структура «сегментированный массив» (экспоненциальные сегменты), её плюсы и минусы, и сравнение с существующими решениями: std::deque, ropes, Zig std.SegmentedList, rust-array-stump, plf::colony.
- Критика терминологии: это не «массив» в классическом смысле из‑за неконтигуозной памяти; многие API ожидают сплошной/страйдовый буфер и не подойдут.
- Производительность: при локальных L1-итерациях вычислительная часть индексации может быть ощутима; для больших объёмов память становится бутылочным горлышком. Предлагаются оптимизации итерации по сегментам и замечания про clz/bsr/lzcnt и опции компилятора.
- Виртуальная память как альтернатива: резервирование большого диапазона и по мере роста коммит страниц/guard pages; отмечены плюсы на Linux (MAP_POPULATE, mremap), но плохо для embedded/WASM.
- Сравнение с deque: фиксированные блоки vs экспоненциальные, поддержка prepend, рандом-доступ есть; реализация MSVC критикуется за малый размер блока, GNU/libc++ лучше.
- Недостатки сегментов: ухудшение предвыборки/кэш-локальности при линейной итерации, отсутствие стабильной непрерывности для API, сложность с хеш-таблицами при росте (rehash), потенциальный перерасход памяти при экспоненциальных размерах.
- Предложения: настраиваемый минимальный размер сегмента, функции «склейки» мелких сегментов, разбор условий, когда экспоненциальные сегменты оправданы, и замечания о чрезмерной макротрюковости в C/C23.
Комментарии (112)
Whenever I start to feel like a real programmer making games and webapps and AI-enhanced ETL pipelines, I inevitably come across the blog post of a C++ expert and reminded that I am basically playing with legos and play-doh. I had to do a UML thing for the first time in years for
Carbon Language: An experimental successor to C++ 💬 Длинная дискуссия
—
Комментарии (193)
I think this page describes "what" but not "why" of Carbon.Carbon exists so that it's possible to migrate a large C++ code base, like Chrome, from C++ to something saner, incrementally.The most important attribute of Carbon is not the specifics of the syntax but the fact that it'