Работа с документами, расчёт нагрузок на аллоеатор
This commit is contained in:
@@ -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. Инфраструктура:
|
||||
|Параметр|Значение|Нагрузка на одного клиента||
|
||||
Все параметры кроме 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
|
||||
* Тестирование производительности
|
||||
* **Рекомендации перед сном:**
|
||||
* Зафиксировать текущие решения в документации
|
||||
* Подготовить чек-лист для реализации
|
||||
* Определить приоритетные задачи на следующий этап
|
||||
* **План на завтра:**
|
||||
* Проработка схемы памяти
|
||||
* Проектирование системы буферов
|
||||
* Оценка необходимых изменений в текущей архитектуре
|
||||
* Начало подготовки документации по новой архитектуре
|
||||
|
||||
Удачи с отдыхом!
|
||||
|
||||
Завтра будет новый день и новые возможности для оптимизации архитектуры. Главное — хорошо отдохнуть и подойти к работе с новыми силами.
|
||||
Reference in New Issue
Block a user