diff --git a/DAIRY.md b/DAIRY.md index 0f4f5d3..fbf0da0 100644 --- a/DAIRY.md +++ b/DAIRY.md @@ -1,5 +1,8 @@ # DAIRY.md +**Дисклеймер:** +Дневник не является технической документацией. Его основная цель показать как работает больная фантазия автора проекта. Вся документация по проекту описана в файлах README.md соответствующих модуей и самого проекта. + # 23.04.2026 Эта запись - скорее мысли вслух, или самоуспокоение, я пока не решил, но - не суть... Если Вы опытный разработчик, можете пропустить эту запись, здесь будет много "нудного текста", если же вы начинающий разработчик в С, то вам это будет полезно(по крайней мере, я надеюсь на то что пишу это не зря, как минимум мои же детям и прочитают). @@ -68,7 +71,7 @@ static const FXGrade grades[] = { { 4096, 200 }, { 0x10000, 4 } }; ``` -Что нам даёт такая переменная: мы можем спокойно проинициализировать глобальный пул памяти, но в таком исполнении нам придётся высчитывать количесво элементов в переменой через **sizeof()**, можно ли обойтись без этого, можно - есть прекрасная вещь ноль-терминант, дополняем переменную: +Что нам даёт такая переменная: мы можем спокойно проинициализировать глобальный пул памяти, но в таком исполнении нам придётся высчитывать количесво элементов в переменой через **sizeof()**, можно ли обойтись без этого, можно - есть прекрасная вещь ноль-терминант, которая не то что позволяет обойтись без **sizeof()**, но и настраивать непосредственно перед инициализацией, дополняем переменную: ```C // neurox/fxalloc/src/FXAlloc.c // В этой переменной настраиваем градации и предположительное количество блоков @@ -79,6 +82,11 @@ static const FXGrade grades[] = { { 4096, 200 }, { 0x10000, 4 }, { 0 } // Ноль-терминант }; +int main(int argc, char* argv[]) { + fxalloc_init(grades); + // что-нибудь шкодим + return 0; +} ``` Теперь при обходе переменной grades в цикле мы можем быть уверены что дальше чем нужно - не уйдём и вполне себе спокойно можем использовать цикл **for**: ```C @@ -156,16 +164,18 @@ typedef struct FXGradedMemoryPool { mword_t free; } FXMemoryPoolGrade; ``` +**Таракан отвечавший за раздел свалил в неизвестном направлении...** # 26.04.2026 -Чтож, примерное представление о работе сети и нагрузках на асинхронный ввод-вывод мы получили, но, мы брали в рассчёт Lineage2 где достаточно высокая вариативность пакетов, теперь вернёмся к нашему проекту прикинем примерную вариативность пакетов, учтём железо на котором будет работать сервер и посмотрим сколько он сможет выдержать клиентов в теории. На что стоит обратить внимание: +Что ж, примерное представление о работе сети и нагрузках на асинхронный ввод-вывод мы получили, но, мы брали в рассчёт Lineage2 где достаточно высокая вариативность пакетов, теперь вернёмся к нашему проекту прикинем примерную вариативность пакетов, учтём железо на котором будет работать сервер и посмотрим сколько он сможет выдержать клиентов в теории. На что в первую очередь стоит обратить внимание: * Железо * Ресурсы потребляемые ОС * Ресурсы потребляемые сторонними сервисами при их наличии(Web-сервер, почтовый сервер и т.д.) -* Минимальный необходимый запас прочности +* Минимальный необходимый запас прочности(как правило вкладывается 20-30% из-за деградации и вариативности нагрузок как отдельно взятого элемента, так и системы в целом) ## Железо: Отсчётная точка любого инфраструктурного проекта + |Параметр|Значение| |-------:|:-------| |**OS:**|Ubuntu Server 24.04| @@ -174,5 +184,183 @@ typedef struct FXGradedMemoryPool { |**ROM:**|noname 256 GB SSD| |**WiFi:**|2.4 GHz, прямая видимость до 6 м (~32 Mbit/s)| -1. Инфраструктура: -|Параметр|Значение|Нагрузка на одного клиента|| \ No newline at end of file +Все параметры кроме Wi-Fi устанавливаютя заводом изготовителем и имеют ±заявленные(скорее — минус, но на данном этапе считаем заявленный максимум) характеристики, производим замеры: + +**Ключевые параметры оборудования(маршрутизатор) TP-Link Archer-A8(заявленые изготовителем):** + +| Параметр | Значение | +|----------------------------:|:----------------------------------------------------------------| +| Тип оборудования | Маршрутизатор (роутер) / точка доступа | +| Стандарты | Wi‑Fi 802.11a/b/g/n (2.4 ГГц), 802.11ac (5 ГГц) | +| Частотные диапазоны | Dual‑Band: 2.4 ГГц и 5 ГГц | +| Скорость Wi‑Fi (макс.)| 2.4 ГГц: до 600 Мбит/с — 5 ГГц: до 1 300 Мбит/с Суммарно: AC1900| +| Порты | 1 × RJ‑45 WAN (1 Гбит/с) — 4 × RJ‑45 LAN (1 Гбит/с) | +| Технологии | MU‑MIMO (3×3), Beamforming, Smart Connect (автовыбор диапазона), Airtime Fairness| +| Поддержка VPN | PPPoE, PPTP, L2TP | +| QoS (приоритизация трафика) | Поддерживается (включая WMM) | +| Процессор | 1.2 ГГц (1 ядро) | + +**Оборудование сервера:** TP-Link Wi-Fi свисток, всё что изветно - 2.4 GHz + +Показатели **iperf3** в режиме **\*спокойной** сети(03:00, ночь): + +**[ 5]local 192.168.0.104 port 34696 connected to 192.168.0.101 port 5201** + +|[ ID]| Interval | Transfer | Bitrate |Retr| Cwnd | +|:---:|:---------------|:-----------:|:--------------:|:--:|:----------:| +|[ 5]| 0.00-1.00 sec | 9.25 MBytes | 77.5 Mbits/sec | 0 | 329 KBytes | +|[ 5]| 1.00-2.00 sec | 8.62 MBytes | 72.4 Mbits/sec | 0 | 421 KBytes | +|[ 5]| 2.00-3.00 sec | 8.88 MBytes | 74.4 Mbits/sec | 0 | 450 KBytes | +|[ 5]| 3.00-4.00 sec | 7.75 MBytes | 65.0 Mbits/sec | 0 | 539 KBytes | +|[ 5]| 4.00-5.00 sec | 8.25 MBytes | 69.2 Mbits/sec | 0 | 632 KBytes | +|[ 5]| 5.00-6.00 sec | 7.00 MBytes | 58.8 Mbits/sec | 0 | 735 KBytes | +|[ 5]| 6.00-7.00 sec | 8.38 MBytes | 70.2 Mbits/sec | 0 | 735 KBytes | +|[ 5]| 7.00-8.00 sec | 8.25 MBytes | 69.2 Mbits/sec | 0 | 735 KBytes | +|[ 5]| 8.00-9.00 sec | 7.00 MBytes | 58.7 Mbits/sec | 0 | 735 KBytes | +|[ 5]| 9.00-10.00 sec | 8.25 MBytes | 69.2 Mbits/sec | 0 | 735 KBytes | + +|[ ID]| Interval | Transfer | Bitrate |Retr| Side | +|:---:|:----------------|:-----------:|:--------------:|:--:|:----------| +|[ 5]| 0.00-10.00 sec | 81.6 MBytes | 68.5 Mbits/sec | 0 | sender | +|[ 5]| 0.00-10.01 sec | 78.9 MBytes | 66.1 Mbits/sec | | receiver | + +Весьма оптимистично, но, это спокойная сеть(ночью без помех), днём показатели будут существенно проседать и итоговая возможность сети будет существенно проседать, однако это — мелочь, что касается стабильности системы - мы должны исходить из максимальных возможностей сети и вкладывать в архитектуру на этом этапе именно это значение, берём максимальное из таблицы(77.5 МБит/с) и добавляем к нему о-о-очень оптимистичные 10%, итог(85.3 МБит/с), TP-Link свисток с 7-милетним стажем показывает топовые результаты не то чтобы для своего возраста, а вообще в принципе(порадовал старика, видимо он "поднатаскался" за 7 лет, как говорят - "Опыт не пропьёшь!"). В нашем случае сеть это узкое место(домашний Wi-Fi за средненьким роутером). +Для чего это: показывает предел сетевого стека, то есть пропускную возможность сети. + +В чём особенность в проектировании алокатора(с моей колокольни), это самый основной элемент серверной части. С неправильной архитектурой памяти сервер — не сервер а "кукурузник", а нам нужна 3-я космическая скорость. По это му нам необходимо расчитать по максимуму частот и по минимуму памяти, такой подход считаю наиболее верным, конечно можно зацепиться за тактовую частоту этой самой памяти, но, на данном этапе — это лишнее. + +Продолжим рассматривать наши железяки со всех сторон и как они определяют требования к аллокатору, начертаем небольшую таблицу опираясь на максимальные возможности сначала каждого компонента в отдельности, найдём слабое звено и посмотрим что на самом деле выйдет. Чего стоит ожидать от такого подхода — максимальные требования к алокатору по скорости, максимальные требования по локализации пулов, и самое ключевое — потолок для выделения блока в плане времени. С чего начнём расчёт Wi-Fi и RAM(ёмкость), почему именно так — способности процессора переварить нагрузку будет зависить от сложности самой функции выделения памяти, в идеале бы уложиться в один атомарный своп, но,— это не реально, скорость света недостижима, по крайней мере для меня — точно(разве что только для тараканов в моей голове это норма). Как мы будем считать: У нас уже есть заголовочные файлы модуля **XOGame** с вот такой вот архитектурой: +```C + /** + * @brief Клетка игры крестики-нолики + */ + typedef struct XOCell { + /// Координата X + int8_t x: 3; + /// Координата Y + int8_t y: 3; + /// Сторона + int8_t side: 2; + } XOCell; + + /** + * @brief Игровое поле крестики-нолики + */ + struct XOGame { + /// Идентификатор игры + const size_t id; + /// Сделать ход. + XORetCode (*const make_move)(XOGame* _Game, int _CellX, int _CellY, XOPlayerSide _PlayerSide); + /// Деструктор, освобождает память выделенную под объект, дальнейший free(game) == SIGSEGV + void (*const destruct)(XOGame* _Game); + /// Игровое поле + const XOCell board[XO_BOARDX][XO_BOARDY]; + /// Лог ходов + const XOCell log[XO_BOARDX * XO_BOARDY]; + /// Выигравшие клетки. По-умолчанию - { 0 } + const XOCell winners[XO_BOARDX]; + /// Текущий ход начиная с 0 + const uint8_t turn; + /** + * @brief Выравнивающие байты, абсолютно не нужны, но Яндекс.АлисаAI настояла - { 0 } + * Если вы решите изменить тип полей в XOCell, то просто удалите это поле, модуль не сломается ;) + */ + uint8_t padding[2]; + }; + +``` +Что мы можем почерпнуть из такой архитектуры: +* ожидаемая вариативность размеров пакетов крайне низкая. +* даже если в пакет напихивать все ±значимые для сети поля - board, log, winners и turn — это всего $9 + 9 + 3 + 1 = 22$ байта + метаданные(размер + идентификатор пакета) ещё $2 + 1$ байта, итого имее всего 25 байт. +* большие пакеты будут связаны исключительно с инициализацией шифрования и аутентификацией. + +Что мы можем ещё расчитать — скорость жмаканья юзерами-абьюзерами по полям, даже самый продвинутый guiuser врядли нажмакает чаще чем 0.5 секунды. Тут даже и вычислений не требуется чтобы понять что, оказывается, Wi-Fi тут далеко не самое узкое место, ну или вернее — сопоставимо узкое, к примеру с CPU или RAM. Берём кулюкулятор, пренебрегаем большими пакетами в виду их большой редкости, выбираем режим "Инженер"(мы же тут — архитектуру планируем) и начинаем считать: + +| Параметр | Формула | Лимит guiuser-ов | Описание | +|:--------:|:------------------------:|:----------------:|:------------------------------------| +|**Wi-Fi:**|$⌈\frac{85,3×10^6}{25×8}⌉$| 426 500 | Не то что бы много, но дох-х-ходчиво| + +Очешуеваем от того сколько абьюзеров одновременно никогда не увидит наш сервер и понимаем что старый свисток — далеко не самое слабое место нашего проекта, почему, потому что даже топовое железо не всегда тянет столько линий, не говоря уже о том что сама ось(Операционная система) не выделит нам столько подключений, хотя: +```console +guiuser@felexdev:~$ cat /proc/sys/fs/file-max +9223372036854775807 < Максимальное количество файловых дескрипторов всего +``` +Подбираем челюсть, смотрим дальше: +```Console +guiuser@felexdev:~$ ulimit -n +open files (-n) 1024 < Максимальное количество файловых дескрипторов на процесс +``` +Ничего, лично я,— "пингвин со стажем", можем поднять и до $65 535$ на процесс, однако на данном этапе нам нет такой необходимости. + +# 27.04.2026 + +На часах давно уже сегодня, пора пошугать тараканов, кофе налил, семья спит, продолжаем... + +О сути расчётов по вафляю, что они нам дают 426к+ клиентов - весьма сомнительное число, но почему-бы на вложить его в основу, а вот почему: не учтены заголовки TCP/IP, а это не много ни мало - минимум 40 байт, соответственно - количество полезной нагрузки на сетевой трафик из наших расчётов будет 25:40, пересчитываем размер пакета: `$\frac{85,3×10^6}{65×8}≈163961$` guiuser-ов, вот, это уже ближе к теме. + +**Предварительный итог профилирования сети:** расчётное время между аллокациями оценено ~2,5 мкс, что критически мало, это, конечно не самое худшее что может быть, но, и не так уж красиво, среднее время на выделение оптимизированными аллокаторами ~10-15 мкс, есть куда расти. + +## RAM + +Примерные цифры по сети мы получили, пора заняться RAM. Очередной раз обращаемся к нашему любимому критику-статисту Алисе(она "очень могёт" в статистике), идём препрофилировать память, на глаз прикиываем примерное минимальное потребление памяти на одного абьюзера: + +**Таблица потребления памяти с выравниванием до 8 байт** +| Компонент | Размер (байт) |Выравнивание|Итого с padding| Описание | +|:--------------------------|:-------------:|:----------:|:-------------:|:-----------------------------------------| +|**Сокетные структуры ядра**| | | | | +|epoll_event | 12 | 8 → 16 | 16 | Структура события epoll | +|file descriptor | 8 | 8 → 8 | 8 | Указатель на файловый дескриптор | +|socket struct | 200 | 8 → 200 | 200 | Базовая структура сокета | +|**Метаданные соединения** | | | | | +|Указатель на игру | 8 | 8 → 8 | 8 | Указатель на структуру игры | +|XOR-ключи | 16 | 8 → 16 | 16 | 2 ключа шифрования | +|Состояние игры | 64 | 8 → 64 | 64 | Данные состояния игры | +|Метаданные блока памяти | 16 | 8 → 16 | 16 | Дополнительные данные управления памятью | +|**Шифрование** | | | | | +|Указатель на шифровщик | 8 | 8 → 8 | 8 | Указатель на модуль шифрования | +|**Дополнительные указатели**| | | | | +|Указатель на клиента | 8 | 8 → 8 | 8 | Указатель на структуру клиента | +|Резерв | 8 | 8 → 8 | 8 | Дополнительный резерв | +|Указатель на пул | | | | | +|Указатель на клиента в пуле| 16 | 8 → 16 | 16 | Хранение клиента в пуле | +**Итого с выравниванием** | | | **360** | **Суммарное потребление** | + +**Расчет общей производительности** +| Параметр | Значение | Описание +|:----------------------|:-------------:|:--------------------------------------| +| Доступная RAM 6.1 GB | | Свободная память для соединений | +| Память на соединение | 360 байт | Потребление на одно соединение | +| Теоретический максимум| ~16.9 млн | Максимальное количество соединений | +| Реальный максимум | ~12.7 млн |С учетом системных накладных расходов | + +Реальный максимум ~12.7 млн гуюзеров, не дурно, собственно это и ожидалось. Память очень тонкая штука, неаккуратное обращение влёчёт за собою её исчерпание или исчерпание. Что при утечках, что при сегментации рискует обернуться большой бедой для прода =D + +### Промежуточный итог над принципом рабботы с памятью(вставлено как есть): +**Итоги обсуждения архитектуры** +* **Основные решения приняты:** + * Разделение потоков ввода/вывода + * Циклическое управление памятью + * Минимизация аллокаций вне TLS + * Классификация пакетов по размеру +* **Следующий шаг:** + * Проработка деталей реализации + * Документирование принятых решений + * Подготовка к имплементации +* **Важные моменты для дальнейшей работы:** + * Детальное проектирование системы памяти + * Реализация механизма кругового буфера + * Оптимизация работы с TLS + * Тестирование производительности +* **Рекомендации перед сном:** + * Зафиксировать текущие решения в документации + * Подготовить чек-лист для реализации + * Определить приоритетные задачи на следующий этап +* **План на завтра:** + * Проработка схемы памяти + * Проектирование системы буферов + * Оценка необходимых изменений в текущей архитектуре + * Начало подготовки документации по новой архитектуре + +Удачи с отдыхом! + +Завтра будет новый день и новые возможности для оптимизации архитектуры. Главное — хорошо отдохнуть и подойти к работе с новыми силами. \ No newline at end of file