From 9ab71a03bcd565e08f746af6bba7665dfcbcd7d0 Mon Sep 17 00:00:00 2001 From: felex67 Date: Sun, 26 Apr 2026 05:32:59 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=B4=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DAIRY.md | 145 +------------- PROGRESS.md | 6 + README.md | 4 +- {fxalloc => ccpp/fxalloc}/.gitignore | 0 {fxalloc => ccpp/fxalloc}/.vscode/tasks.json | 0 {fxalloc => ccpp/fxalloc}/CMakeLists.txt | 0 ccpp/fxalloc/DAIRY.md | 178 ++++++++++++++++++ ccpp/fxalloc/PREPROFILE.md | 20 ++ ccpp/fxalloc/PROGRESS.md | 36 ++++ {template => ccpp/fxalloc}/README.md | 0 ccpp/fxalloc/TODO.md | 21 +++ .../fxalloc/headers}/_FXAlloc.h | 0 .../fxalloc/includes}/FXAlloc.h | 0 {fxalloc => ccpp/fxalloc}/includes/fxmbmeta.h | 0 {fxalloc => ccpp/fxalloc}/includes/mword.h | 0 {fxalloc => ccpp/fxalloc}/src/FXAlloc.c | 0 {server => ccpp/fxxogame}/.gitignore | 0 {template => ccpp/fxxogame}/CMakeLists.txt | 0 {template => ccpp/fxxogame}/DAIRY.md | 0 {template => ccpp/fxxogame}/PROGRESS.md | 0 ccpp/fxxogame/README.md | 1 + {template => ccpp/fxxogame}/TODO.md | 0 {server => ccpp/fxxogame}/headers/_XOGame.h | 0 {template => ccpp/fxxogame}/headers/_header.h | 0 {server => ccpp/fxxogame}/includes/XOGame.h | 0 {template => ccpp/fxxogame}/src/source.c | 0 {template => ccpp/server}/.gitignore | 0 {server => ccpp/server}/CMakeLists.txt | 0 {server => ccpp/server}/headers/_Core.h | 0 {server => ccpp/server}/includes/Core.h | 0 {server => ccpp/server}/server.c | 0 {server => ccpp/server}/src/Core.c | 0 {server => ccpp/server}/src/XOGame.c | 0 {server => ccpp/server}/src/db/mysql.sql | 0 {server => ccpp/server}/src/db/queries.c | 0 {server => ccpp/server}/src/db/sqlite.sql | 0 ccpp/template/.gitignore | 2 + ccpp/template/CMakeLists.txt | 7 + ccpp/template/DAIRY.md | 1 + ccpp/template/PROGRESS.md | 30 +++ ccpp/template/README.md | 1 + ccpp/template/TODO.md | 15 ++ ccpp/template/headers/_header.h | 17 ++ .../template}/includes/include.h | 0 ccpp/template/src/source.c | 10 + server/includes/fxalloc.h | 15 -- 46 files changed, 349 insertions(+), 160 deletions(-) rename {fxalloc => ccpp/fxalloc}/.gitignore (100%) rename {fxalloc => ccpp/fxalloc}/.vscode/tasks.json (100%) rename {fxalloc => ccpp/fxalloc}/CMakeLists.txt (100%) create mode 100644 ccpp/fxalloc/DAIRY.md create mode 100644 ccpp/fxalloc/PREPROFILE.md create mode 100644 ccpp/fxalloc/PROGRESS.md rename {template => ccpp/fxalloc}/README.md (100%) create mode 100644 ccpp/fxalloc/TODO.md rename {fxalloc/includes => ccpp/fxalloc/headers}/_FXAlloc.h (100%) rename {fxalloc/headers => ccpp/fxalloc/includes}/FXAlloc.h (100%) rename {fxalloc => ccpp/fxalloc}/includes/fxmbmeta.h (100%) rename {fxalloc => ccpp/fxalloc}/includes/mword.h (100%) rename {fxalloc => ccpp/fxalloc}/src/FXAlloc.c (100%) rename {server => ccpp/fxxogame}/.gitignore (100%) rename {template => ccpp/fxxogame}/CMakeLists.txt (100%) rename {template => ccpp/fxxogame}/DAIRY.md (100%) rename {template => ccpp/fxxogame}/PROGRESS.md (100%) create mode 100644 ccpp/fxxogame/README.md rename {template => ccpp/fxxogame}/TODO.md (100%) rename {server => ccpp/fxxogame}/headers/_XOGame.h (100%) rename {template => ccpp/fxxogame}/headers/_header.h (100%) rename {server => ccpp/fxxogame}/includes/XOGame.h (100%) rename {template => ccpp/fxxogame}/src/source.c (100%) rename {template => ccpp/server}/.gitignore (100%) rename {server => ccpp/server}/CMakeLists.txt (100%) rename {server => ccpp/server}/headers/_Core.h (100%) rename {server => ccpp/server}/includes/Core.h (100%) rename {server => ccpp/server}/server.c (100%) rename {server => ccpp/server}/src/Core.c (100%) rename {server => ccpp/server}/src/XOGame.c (100%) rename {server => ccpp/server}/src/db/mysql.sql (100%) rename {server => ccpp/server}/src/db/queries.c (100%) rename {server => ccpp/server}/src/db/sqlite.sql (100%) create mode 100644 ccpp/template/.gitignore create mode 100644 ccpp/template/CMakeLists.txt create mode 100644 ccpp/template/DAIRY.md create mode 100644 ccpp/template/PROGRESS.md create mode 100644 ccpp/template/README.md create mode 100644 ccpp/template/TODO.md create mode 100644 ccpp/template/headers/_header.h rename {template => ccpp/template}/includes/include.h (100%) create mode 100644 ccpp/template/src/source.c delete mode 100644 server/includes/fxalloc.h diff --git a/DAIRY.md b/DAIRY.md index c2d97b0..1a317a0 100644 --- a/DAIRY.md +++ b/DAIRY.md @@ -1,4 +1,5 @@ # DAIRY.md + ## 22.04.2026 * Добавлена основа для проекта Android-приложения. * Заложен функдамент для модуля игры XOGame(neurox/XOGame): @@ -9,149 +10,7 @@ После 4-хчасового батла с YandexGPT 5.1 Pro и его настойчивого "непонятный аллокатор fxalloc()" решено отложить модуль игры и приступить к реализации модуля аллокатора. ## 23.04.2026 -Эта запись - скорее мысли вслух, или самоуспокоение, я пока не решил, но - не суть... -Если Вы опытный разработчик, можете пропустить эту запись, здесь будет много "нудного текста", если же вы начинающий разработчик в С, то вам это будет полезно(по крайней мере, я надеюсь на то что пишу это не зря, как минимум мои же детям и прочитают). -Рано или поздно каждый разработчик сталкивается с решением нетривиальной задачи по реализации собственного аллокатора, почему: -* **наименьшее из зол** - время выделения памяти стандартными методами **malloc**, **calloc** и **new**. -* **наибольшее из зол** - фрагментация этой самой памяти, особенно когда дело касается высокой вариативности. - -Встаёт вопрос как решить эту проблему и вот конкретно в этом месте начинается магия. Почему магия, потому что при разработке аллокатора можно напороться на очень много рифов. Итак, присаживайтесь по-удобнее, запасайтесь печеньками, а я - продолжу. ;) -На этапе продумывания архитектуры данного проекта я понимал каким будет аллокатор и что мне от него нужно, да помимо всего прочего - есть парочка готовых проверенных решений, как обычных, так и многопоточных(jemalloc или tcmalloc), но, это не тот случай, мы же тут навыки восстанавливаем, поэтому вот Вам полёт больной фантазии в парадигме MVP+KISS+YAGNI(быстро, просто, без излишеств), пример буду приводить на прокси-сервере Lineage2(для наглядности). -### Включаем режим "Архитектор lvl-80" MVP+KISS -#### Делаем себе своеобразное ТЗ: -1. Разнородные блоки(проектируем сервер) -2. Многопоточность(всё ещё проектируем сервер) -3. Один из важнейших аспектов - делегирование памяти между потоками(оптимизация: уменьшение дополнительных выделений и копирования). -#### Препрофилирование: расчёт предполагаемой нагрузки на стадии проектирования архитектуры -Для определения градаций блоков памяти серьёзные дяди-тёти берут статистику типовых нагрузок в схожих условиях, чтож последуем их примеру, открываем "Я.браузер" → "АлисаAI" → "Новый чат" и просим Алису расчитать максимальную нагрузку на аллокатор для прокси-сервера Lineage2 при 200 линий(400 подключений), получаем от неё примерную нагрузку: -#### Ключевые метрики нагрузки -|**Параметр**|**Средняя нагрузка**|**Пиковая нагрузка**| -|:-:|:-:|:-:| -|**Пакеты/сек**|4 000|30 000| -|**Трафик**|4 Мбит/сек|60 Мбит/сек| -|**Аллокации/сек**|4 000|30 000| -|**Потребление памяти**|3,2 МБ|10 МБ| - -Далее просим её вывести подробные градации для этой нагрузки , получаем следующие варианты: - -#### Сводная таблица по всем группам -|Группа|Диапазон размеров (байт)|Доля трафика (%)|Общая частота (пакетов/сек)|Фрагментация (%)|Оптимизация| -|:-:|:-:|:-:|:-:|:-:|:-| -|1.|Сверхмалые 32–64|5–10|4–200|20–25|Slab‑аллокатор, пулы фиксированного размера| -|2.|Малые 65–128|20–25|1 200–8 000|15–20|Кэширование буферов, пакетная обработка| -|3.|Средние 129–256|25–30|800–4 000|10–15|Пулы памяти, переиспользование буферов| -|4.|Крупные 257–512|15–20|200–2 000|5–10|Предварительное выделение буферов| -|5.|Очень крупные 513–1 024|10–15|80–1 200|3–8|Пакетная обработка, буферизация| -|6.|Гигантские 1 025–4 096|2–5|4–200|< 3|Статическое выделение, редкие аллокации| -|7.|Экстра‑крупные > 4 096|< 1|< 4|Отсутствует|Загрузка по частям, стриминг| - -Таким образом видим что в секунду примерно 60 МБит, и имеем приблизительное представление о градациях. Но, это ещё не всё, вспоминаем на какой системе работает сервер, какой принцип обработки соединений используется (WSAPoll | epoll), как правило - это асинхронный ввод-вывод, что нам даёт это знание - узкое место любого сервера, это основной фактор скорости, сервер не может работать быстрее сети, однако, подходы к работе у **IOCompletionPort(Windows)** и **epoll(Linux)** кардинально разные, тонкости их влияния на работу аллокатора раскроются немного позже, а пока нам нужна именно сетевая нагрузка. Снова вооружаемся АлисойAI(YandexGPT 5.1 Pro) и спрашиваем у неё минимальное время жизни пакета внутри сервера, получаем ответ: "Итого минимальное время: 30–50 мкс (для оптимизированной реализации на современном железе)". Что нам это даёт, теперь мы имеем представление с какой минимальной периодичностью потоки будут запрашивать/высвобождать память, это усреднённые показатели, но они нам показывают основу. Итогом данного этапа можно сделать вывод что примерным минимальным временем между **fxalloc()** и **fxfree()** будет не более 30мкс при такой нагрузке, "заблаговременно" делим это время на 2 и получаем 15мкс. -Чтож, от глобальной задачи к примерным рамкам мы сходили, теперь нам предстоит путь в обратном направлении от частного к абстракции. Открываем IDE, запасаемся кофе и приступаем. -Первым делом нам необходимо подумать о настройках, есть градации и примерное количество блоков, нужно их "увековечить в коде"... -Глотнув кофе и просмаковав его приятный аромат понимаем что нам нужна структура которая опишет каждый блок, отлично, пишем: -```C -/** - * @brief Структура преднастройки аллокатора задающая градации и количество блоков памяти - * - * @property +est_size: size_t - Предполагаемый размер блока - * @property +est_count: size_t - Предполагаемое количество блоков - */ -typedef struct FXGrade { - /// Предполагаемый размер блока - const size_t est_size; - /// Предполагаемое количество блоков - const size_t est_count; -} FXGrade; - -``` -Отлично, объединим их в целое, выделим переменную - массив градаций, и статически её проинициализируем: -```C -// neurox/fxalloc/includes/FXAlloc.h -extern const FXGrade* grades; -// neurox/fxalloc/src/FXAlloc.c -// В этой переменной настраиваем градации и предположительное количество блоков -static const FXGrade grades[] = { - { 32, 200 }, { 64, 200 }, { 128, 8000 }, - { 256, 4000 }, { 512, 2000 }, { 1024, 1200 }, - { 4096, 200 }, { 0x10000, 4 } -}; -``` -Что нам даёт такая переменная: мы можем спокойно проинициализировать глобальный пул памяти, но в таком исполнении нам придётся высчитывать количесво элементов в переменой через **sizeof()**, можно ли обойтись без этого, можно - есть прекрасная вещь ноль-терминант, дополняем переменную: -```C -// neurox/fxalloc/src/FXAlloc.c -// В этой переменной настраиваем градации и предположительное количество блоков -// Элемент: { размер, количество }; -static const FXGrade grades[] = { - { 32, 200 }, { 64, 200 }, { 128, 8000 }, - { 256, 4000 }, { 512, 2000 }, { 1024, 1200 }, - { 4096, 200 }, { 0x10000, 4 }, - { 0 } // Ноль-терминант -}; -``` -Теперь при обходе переменной grades в цикле мы можем быть уверены что дальше чем нужно - не уйдём и вполне себе спокойно можем использовать цикл **for**: -```C -for (size_t i = 0; grades[i].est_size; i++) { - // Инициализируем отдельный пул по грейду -} -``` -Что-то у нас уже есть, теперь стоит продумать сам пул, нам потребуются метаданные(маркеры), каждому блоку как минимум необходимо помнить свой размер, пишем: -```C -typedef struct FXMemoryBlock { - /// Размер блока - size_t size; -} FXMemoryBlock; -``` -Однако, такой подход даёт сложность поиска "места проживания" которую можно выразить как $O(n)$, где $n$ - количество градаций блоков по размерам, таким образом вызов функций **fxalloc()** и **fxfree()** требует поиска "места обитания" блока, в математическом выражении это выглядит как $O(n)+O(n)≡O(n)$, можно ли оптимизировать - можно, заменяем **FXMemoryBlock::size** на **FXMemoryBlock::gid**(Идентификатор этого размера): -```C -typedef struct FXMemoryBlock { - /// ID размера блока - size_t gid; -} FXMemoryBlock; -``` -Как это влияет на суммарную сложность: -1. **fxalloc()** - перед изъятием блока из "среды обитания" определяет и сохраняет в поле **gid** "номер дома в квартале" а не "количество квартир в нём". Алгоритмическая сложность $O(n)$. -2. **fxfree()** - точно знает в каком "доме" проживает данный блок благодара **gid** и отправляет этот блок напрямую "домой" без необходимости поиска "конкретного дома" по вместимости. Алгоритмическая сложность $O(1)$. - -Это микрооптимизация, но, на уровне архитектуры это важный нюанс, принцип хеширования - основа оптимизации. -Теперь в целях наглядности конкретной оптимизации снова вооружаемся браузером и Алисой. Просим её расчитать примерное время для новой концепции и наших градаций, радуемся результату и смакуем(эта радость будет недолгой): -Сводная таблица: расчётное время выполнения fxalloc() + fxfree() для приведённых градаций - -| Группа | Диапазон размеров (байт) | Кол‑во градаций (n) | Сложность fxalloc() | Сложность fxfree() | Суммарная сложность | Расчётное время (операции, худший случай) | -|:--|:----------|:-----:|:-:|:-:|:-:|:-:| -| 1. Сверхмалые | 32–64 | 7 | O(n) | O(n) (без gid) / O(1) (с gid) | O(n) / O(n)* | 14 (7+7) / 8 (7+1) | -| 2. Малые | 65–128 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | -| 3. Средние | 129–256 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | -| 4. Крупные | 257–512 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | -| 5. Очень крупные | 513–1 024 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | -| 6. Гигантские | 1 025–4 096 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | -| 7. Экстра‑крупные | > 4 096 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | - -Пояснения к таблице -* **Количество градаций (n)**: во всех случаях n=7 (по числу групп из исходной таблицы). Это определяет сложность линейного поиска. -* **Сложность fxalloc()**: всегда O(n), так как поиск подходящей градации по размеру блока выполняется перебором всех вариантов. -* **Сложность fxfree()**: - * **без оптимизации (size)**: O(n). Требуется повторный поиск градации по сохранённому размеру блока. - * **с оптимизацией (gid)**: O(1). Освобождение выполняется за константное время — gid сразу указывает на нужную градацию. -* **Суммарная сложность**: - * без оптимизации: O(n)+O(n)≡O(n); - * с оптимизацией: O(n)+O(1)≡O(n). Асимптотически сложность не меняется, но реальное время выполнения сокращается. -* **Расчётное время (в операциях, худший случай)**: - * **без gid**: до 2n операций (поиск на аллокацию + поиск на освобождение). Для n=7: 7+7=14 сравнений. - * **с gid**: n+1 операций (поиск на аллокацию + прямой доступ на освобождение). Для n=7: 7+1=8 операций. - -Порадовались, хорошо, выдохнули и почувствовали себя гигантами мысли, теперь у нас время на поиск в **fxfree()** имеет константную сложность $O(1)$, однако это только на поиск, вот мы и подошли к первому "рифу": **epoll** VS **IOCP**: -* **epoll** - мультиплексор позволяющий обрабатывать 1к+ соединений в одном потоке принципом уведомления потока только когда дескриптор готов к чтению/записи млм возникла ошибка оптимизированный на уровне ядра Linux. -* **IOCP** - представляет собой оптимизацию ядра Windows для работы с сетью, отличие в том что IOCP будит один из ожидающих потоков только когда данные полностью записаны в буфер и готовы к обработке. - -Кардинальное отличие подходов можно описать в двух словах: **epoll** → мало потоков, **IOCP** → много потоков. -#### Влияние парадигм работы с **epoll** и **IOCompletionPort** на аллокатор -С моей точки зрения как Linux-кодера и борца за эффективность IOCP имеет **жирнючий** минус - как правило это внушительный пул потоков ибо при их нехватке эффективность будет падать. Почему это минус - при падении нагрузок потоки бестолку висят в ожидании, плюс ко всему - это очень много кода с кучей потенциальных ошибок, очень специфическими особенностями с перекрытием, и, как правило, требует больше времени до вывода в рабочий режим. -Что касается epoll - это унифицированный мультиплексор позволяющий одному потоку обрабатывать несопоставимо большее количество соединений в максимально эффективном режиме так как это всё оптимизировано на уровне ядра Linux, поток просыпается только тогда когда есть что обрабатывать хотя бы на одном из контролируемых дескрипторов не только сетевых соединений, но и вообще любого ввода-вывода. -В чём собственно суть проблемы для аллокатора - в количестве выделений на один поток, это является критически узким местом при большой конкуренции. Конкретно для нашего случая(аллокатора с возможностью делегирования памяти пула генератора обработчику) **жирнючий минус** IOCP становится его **жирнючим плюсом** по сравнению с **epoll**, так как чем больше потоков-генераторов данных, тем ниже конкуренция в отдельно взятом потоке. -Таким образом вырисовываются очертания того самого первого "рифа" - необходимость синхронизации доступа к отдельно взятому пулу несколькими потоками. Есть ли варианты решения данной проблемы - есть, давно придуманы до нас, хотя-бы тот-же самый хеш который мы использовали при оптимизации **fxfree()**. Сделаем небольшое лирическое отступление и накидаем немного полей в структуру пула памяти: - - -#### Извиняюсь за прерывание, продолжение следует +Стартовала работа над аллокатором **FXAlloc** → [ccpp/fxalloc/DAIRY.md](ccpp/fxalloc/DAIRY.md) ## Этот блок исключительно для YandexGPT 5.1 Pro и прочих ИИ-ассистентов, сгенерирован YandexGPT 5.1 Pro(АлисаAI) и вставлен "как есть" ### Анализ формата DAIRY.md diff --git a/PROGRESS.md b/PROGRESS.md index 07604b3..40847ba 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -9,6 +9,12 @@ * ⚠️ — задача отложена * ❌ — задача отклонена +Структура таблицы + +| Статус | Задача | Описание | +|:-:|:-----|:-------| + + ## 22.04.2026 | Статус | Задача | Описание | |:-:|:-----|:-------| diff --git a/README.md b/README.md index 7029bcd..fa66715 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,8 @@ ## Прогресс -|➤|**Работа с документами**| Реорганизация документации, оптимизация каталогов проекта| -|:-:|:-----------------------|:------------------------| +| ➤ |**Работа с документами**| Реорганизация документации, оптимизация каталогов проекта| +|:--:|:-----------------------|:------------------------| | ⚠️ | **Работа над аллокатором** | Проектирование архитектуры. | | ✔️ | **Модуль игры** | Архитектура спроектирована. | | ✔️ | **Инфраструктура** | Развёрнуты NGINX, Postfix, MySQL. | diff --git a/fxalloc/.gitignore b/ccpp/fxalloc/.gitignore similarity index 100% rename from fxalloc/.gitignore rename to ccpp/fxalloc/.gitignore diff --git a/fxalloc/.vscode/tasks.json b/ccpp/fxalloc/.vscode/tasks.json similarity index 100% rename from fxalloc/.vscode/tasks.json rename to ccpp/fxalloc/.vscode/tasks.json diff --git a/fxalloc/CMakeLists.txt b/ccpp/fxalloc/CMakeLists.txt similarity index 100% rename from fxalloc/CMakeLists.txt rename to ccpp/fxalloc/CMakeLists.txt diff --git a/ccpp/fxalloc/DAIRY.md b/ccpp/fxalloc/DAIRY.md new file mode 100644 index 0000000..0f4f5d3 --- /dev/null +++ b/ccpp/fxalloc/DAIRY.md @@ -0,0 +1,178 @@ +# DAIRY.md + +# 23.04.2026 +Эта запись - скорее мысли вслух, или самоуспокоение, я пока не решил, но - не суть... +Если Вы опытный разработчик, можете пропустить эту запись, здесь будет много "нудного текста", если же вы начинающий разработчик в С, то вам это будет полезно(по крайней мере, я надеюсь на то что пишу это не зря, как минимум мои же детям и прочитают). +Рано или поздно каждый разработчик сталкивается с решением нетривиальной задачи по реализации собственного аллокатора, почему: +* **наименьшее из зол** - время выделения памяти стандартными методами **malloc**, **calloc** и **new**. +* **наибольшее из зол** - фрагментация этой самой памяти, особенно когда дело касается высокой вариативности. + +Встаёт вопрос как решить эту проблему и вот конкретно в этом месте начинается магия. Почему магия, потому что при разработке аллокатора можно напороться на очень много рифов. Итак, присаживайтесь по-удобнее, запасайтесь печеньками, а я - продолжу. ;) +На этапе продумывания архитектуры данного проекта я понимал каким будет аллокатор и что мне от него нужно, да помимо всего прочего - есть парочка готовых проверенных решений, как обычных, так и многопоточных(jemalloc или tcmalloc), но, это не тот случай, мы же тут навыки восстанавливаем, поэтому вот Вам полёт больной фантазии в парадигме MVP+KISS+YAGNI(быстро, просто, без излишеств), пример буду приводить на прокси-сервере Lineage2(для наглядности). +### Включаем режим "Архитектор lvl-80" MVP+KISS +#### Делаем себе своеобразное ТЗ: +1. Разнородные блоки(проектируем сервер) +2. Многопоточность(всё ещё проектируем сервер) +3. Один из важнейших аспектов - делегирование памяти между потоками(оптимизация: уменьшение дополнительных выделений и копирования). +#### Препрофилирование: расчёт предполагаемой нагрузки на стадии проектирования архитектуры +Для определения градаций блоков памяти серьёзные дяди-тёти берут статистику типовых нагрузок в схожих условиях, чтож последуем их примеру, открываем "Я.браузер" → "АлисаAI" → "Новый чат" и просим Алису расчитать максимальную нагрузку на аллокатор для прокси-сервера Lineage2 при 200 линий(400 подключений), получаем от неё примерную нагрузку: +#### Ключевые метрики нагрузки +|**Параметр**|**Средняя нагрузка**|**Пиковая нагрузка**| +|:-:|:-:|:-:| +|**Пакеты/сек**|4 000|30 000| +|**Трафик**|4 Мбит/сек|60 Мбит/сек| +|**Аллокации/сек**|4 000|30 000| +|**Потребление памяти**|3,2 МБ|10 МБ| + +Далее просим её вывести подробные градации для этой нагрузки , получаем следующие варианты: + +#### Сводная таблица по всем группам +|Группа|Диапазон размеров (байт)|Доля трафика (%)|Общая частота (пакетов/сек)|Фрагментация (%)|Оптимизация| +|:-:|:-:|:-:|:-:|:-:|:-| +|1.|Сверхмалые 32–64|5–10|4–200|20–25|Slab‑аллокатор, пулы фиксированного размера| +|2.|Малые 65–128|20–25|1 200–8 000|15–20|Кэширование буферов, пакетная обработка| +|3.|Средние 129–256|25–30|800–4 000|10–15|Пулы памяти, переиспользование буферов| +|4.|Крупные 257–512|15–20|200–2 000|5–10|Предварительное выделение буферов| +|5.|Очень крупные 513–1 024|10–15|80–1 200|3–8|Пакетная обработка, буферизация| +|6.|Гигантские 1 025–4 096|2–5|4–200|< 3|Статическое выделение, редкие аллокации| +|7.|Экстра‑крупные > 4 096|< 1|< 4|Отсутствует|Загрузка по частям, стриминг| + +Таким образом видим что в секунду примерно 60 МБит, и имеем приблизительное представление о градациях. Но, это ещё не всё, вспоминаем на какой системе работает сервер, какой принцип обработки соединений используется (WSAPoll | epoll), как правило - это асинхронный ввод-вывод, что нам даёт это знание - узкое место любого сервера, это основной фактор скорости, сервер не может работать быстрее сети, однако, подходы к работе у **IOCompletionPort(Windows)** и **epoll(Linux)** кардинально разные, тонкости их влияния на работу аллокатора раскроются немного позже, а пока нам нужна именно сетевая нагрузка. Снова вооружаемся АлисойAI(YandexGPT 5.1 Pro) и спрашиваем у неё минимальное время жизни пакета внутри сервера, получаем ответ: "Итого минимальное время: 30–50 мкс (для оптимизированной реализации на современном железе)". Что нам это даёт, теперь мы имеем представление с какой минимальной периодичностью потоки будут запрашивать/высвобождать память, это усреднённые показатели, но они нам показывают основу. Итогом данного этапа можно сделать вывод что примерным минимальным временем между **fxalloc()** и **fxfree()** будет не более 30мкс при такой нагрузке, "заблаговременно" делим это время на 2 и получаем 15мкс. +Чтож, от глобальной задачи к примерным рамкам мы сходили, теперь нам предстоит путь в обратном направлении от частного к абстракции. Открываем IDE, запасаемся кофе и приступаем. +Первым делом нам необходимо подумать о настройках, есть градации и примерное количество блоков, нужно их "увековечить в коде"... +Глотнув кофе и просмаковав его приятный аромат понимаем что нам нужна структура которая опишет каждый блок, отлично, пишем: +```C +/** + * @brief Структура преднастройки аллокатора задающая градации и количество блоков памяти + * + * @property +est_size: size_t - Предполагаемый размер блока + * @property +est_count: size_t - Предполагаемое количество блоков + */ +typedef struct FXGrade { + /// Предполагаемый размер блока + const size_t est_size; + /// Предполагаемое количество блоков + const size_t est_count; +} FXGrade; + +``` +Отлично, объединим их в целое, выделим переменную - массив градаций, и статически её проинициализируем: +```C +// neurox/fxalloc/includes/FXAlloc.h +extern const FXGrade* grades; +// neurox/fxalloc/src/FXAlloc.c +// В этой переменной настраиваем градации и предположительное количество блоков +static const FXGrade grades[] = { + { 32, 200 }, { 64, 200 }, { 128, 8000 }, + { 256, 4000 }, { 512, 2000 }, { 1024, 1200 }, + { 4096, 200 }, { 0x10000, 4 } +}; +``` +Что нам даёт такая переменная: мы можем спокойно проинициализировать глобальный пул памяти, но в таком исполнении нам придётся высчитывать количесво элементов в переменой через **sizeof()**, можно ли обойтись без этого, можно - есть прекрасная вещь ноль-терминант, дополняем переменную: +```C +// neurox/fxalloc/src/FXAlloc.c +// В этой переменной настраиваем градации и предположительное количество блоков +// Элемент: { размер, количество }; +static const FXGrade grades[] = { + { 32, 200 }, { 64, 200 }, { 128, 8000 }, + { 256, 4000 }, { 512, 2000 }, { 1024, 1200 }, + { 4096, 200 }, { 0x10000, 4 }, + { 0 } // Ноль-терминант +}; +``` +Теперь при обходе переменной grades в цикле мы можем быть уверены что дальше чем нужно - не уйдём и вполне себе спокойно можем использовать цикл **for**: +```C +for (size_t i = 0; grades[i].est_size; i++) { + // Инициализируем отдельный пул по грейду +} +``` +Что-то у нас уже есть, теперь стоит продумать сам пул, нам потребуются метаданные(маркеры), каждому блоку как минимум необходимо помнить свой размер, пишем: +```C +typedef struct FXMemoryBlock { + /// Размер блока + size_t size; +} FXMemoryBlock; +``` +Однако, такой подход даёт сложность поиска "места проживания" которую можно выразить как $O(n)$, где $n$ - количество градаций блоков по размерам, таким образом вызов функций **fxalloc()** и **fxfree()** требует поиска "места обитания" блока, в математическом выражении это выглядит как $O(n)+O(n)≡O(n)$, можно ли оптимизировать - можно, заменяем **FXMemoryBlock::size** на **FXMemoryBlock::gid**(Идентификатор этого размера): +```C +typedef struct FXMemoryBlock { + /// ID размера блока + size_t gid; +} FXMemoryBlock; +``` +Как это влияет на суммарную сложность: +1. **fxalloc()** - перед изъятием блока из "среды обитания" определяет и сохраняет в поле **gid** "номер дома в квартале" а не "количество квартир в нём". Алгоритмическая сложность $O(n)$. +2. **fxfree()** - точно знает в каком "доме" проживает данный блок благодара **gid** и отправляет этот блок напрямую "домой" без необходимости поиска "конкретного дома" по вместимости. Алгоритмическая сложность $O(1)$. + +Это микрооптимизация, но, на уровне архитектуры это важный нюанс, принцип хеширования - основа оптимизации. +Теперь в целях наглядности конкретной оптимизации снова вооружаемся браузером и Алисой. Просим её расчитать примерное время для новой концепции и наших градаций, радуемся результату и смакуем(эта радость будет недолгой): +Сводная таблица: расчётное время выполнения fxalloc() + fxfree() для приведённых градаций + +| Группа | Диапазон размеров (байт) | Кол‑во градаций (n) | Сложность fxalloc() | Сложность fxfree() | Суммарная сложность | Расчётное время (операции, худший случай) | +|:--|:----------|:-----:|:-:|:-:|:-:|:-:| +| 1. Сверхмалые | 32–64 | 7 | O(n) | O(n) (без gid) / O(1) (с gid) | O(n) / O(n)* | 14 (7+7) / 8 (7+1) | +| 2. Малые | 65–128 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | +| 3. Средние | 129–256 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | +| 4. Крупные | 257–512 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | +| 5. Очень крупные | 513–1 024 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | +| 6. Гигантские | 1 025–4 096 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | +| 7. Экстра‑крупные | > 4 096 | 7 | O(n) | O(n) / O(1) | O(n) / O(n)* | 14 / 8 | + +Пояснения к таблице +* **Количество градаций (n)**: во всех случаях n=7 (по числу групп из исходной таблицы). Это определяет сложность линейного поиска. +* **Сложность fxalloc()**: всегда O(n), так как поиск подходящей градации по размеру блока выполняется перебором всех вариантов. +* **Сложность fxfree()**: + * **без оптимизации (size)**: O(n). Требуется повторный поиск градации по сохранённому размеру блока. + * **с оптимизацией (gid)**: O(1). Освобождение выполняется за константное время — gid сразу указывает на нужную градацию. +* **Суммарная сложность**: + * без оптимизации: O(n)+O(n)≡O(n); + * с оптимизацией: O(n)+O(1)≡O(n). Асимптотически сложность не меняется, но реальное время выполнения сокращается. +* **Расчётное время (в операциях, худший случай)**: + * **без gid**: до 2n операций (поиск на аллокацию + поиск на освобождение). Для n=7: 7+7=14 сравнений. + * **с gid**: n+1 операций (поиск на аллокацию + прямой доступ на освобождение). Для n=7: 7+1=8 операций. + +Порадовались, хорошо, выдохнули и почувствовали себя гигантами мысли, теперь у нас время на поиск в **fxfree()** имеет константную сложность $O(1)$, однако это только на поиск, вот мы и подошли к первому "рифу": **epoll** VS **IOCP**: +* **epoll** - мультиплексор позволяющий обрабатывать 1к+ соединений в одном потоке принципом уведомления потока только когда дескриптор готов к чтению/записи млм возникла ошибка оптимизированный на уровне ядра Linux. +* **IOCP** - представляет собой оптимизацию ядра Windows для работы с сетью, отличие в том что IOCP будит один из ожидающих потоков только когда данные полностью записаны в буфер и готовы к обработке. + +Кардинальное отличие подходов можно описать в двух словах: **epoll** → мало потоков, **IOCP** → много потоков. +#### Влияние парадигм работы с **epoll** и **IOCompletionPort** на аллокатор +С моей точки зрения как Linux-кодера и борца за эффективность IOCP имеет **жирнючий** минус - как правило это внушительный пул потоков ибо при их нехватке эффективность будет падать. Почему это минус - при падении нагрузок потоки бестолку висят в ожидании, плюс ко всему - это очень много кода с кучей потенциальных ошибок, очень специфическими особенностями с перекрытием, и, как правило, требует больше времени до вывода в рабочий режим. +Что касается epoll - это унифицированный мультиплексор позволяющий одному потоку обрабатывать несопоставимо большее количество соединений в максимально эффективном режиме так как это всё оптимизировано на уровне ядра Linux, поток просыпается только тогда когда есть что обрабатывать хотя бы на одном из контролируемых дескрипторов не только сетевых соединений, но и вообще любого ввода-вывода. +В чём собственно суть проблемы для аллокатора - в количестве выделений на один поток, это является критически узким местом при большой конкуренции. Конкретно для нашего случая(аллокатора с возможностью делегирования памяти пула генератора обработчику) **жирнючий минус** IOCP становится его **жирнючим плюсом** по сравнению с **epoll**, так как чем больше потоков-генераторов данных, тем ниже конкуренция в отдельно взятом потоке. +Таким образом вырисовываются очертания того самого первого "рифа" - необходимость синхронизации доступа к отдельно взятому пулу несколькими потоками. Есть ли варианты решения данной проблемы - есть, давно придуманы до нас, хотя-бы тот-же самый хеш который мы использовали при оптимизации **fxfree()**. Открываем любимую IDE и накидаем немного полей в структуру пула памяти : +```C +/// @brief Группа блоков одной градации +typedef struct FXGradedMemoryPool { + /// @brief Указатель на последний свободный блок + FXMemoryBlock* free; + /// @brief Всего блоков в данной группе + umword_t total; + /// @brief Количество преаллоцированных блоков + umword_t count_pre; + /// @brief Количество используемых блоков + mword_t used; + /// @brief Количество свободных блоков + mword_t free; +} FXMemoryPoolGrade; +``` + +# 26.04.2026 +Чтож, примерное представление о работе сети и нагрузках на асинхронный ввод-вывод мы получили, но, мы брали в рассчёт Lineage2 где достаточно высокая вариативность пакетов, теперь вернёмся к нашему проекту прикинем примерную вариативность пакетов, учтём железо на котором будет работать сервер и посмотрим сколько он сможет выдержать клиентов в теории. На что стоит обратить внимание: +* Железо +* Ресурсы потребляемые ОС +* Ресурсы потребляемые сторонними сервисами при их наличии(Web-сервер, почтовый сервер и т.д.) +* Минимальный необходимый запас прочности + +## Железо: +Отсчётная точка любого инфраструктурного проекта +|Параметр|Значение| +|-------:|:-------| +|**OS:**|Ubuntu Server 24.04| +|**CPU:**|Intel Core i5‑3470, 4 @ 3.2 GHz| +|**RAM:**|8 GB| +|**ROM:**|noname 256 GB SSD| +|**WiFi:**|2.4 GHz, прямая видимость до 6 м (~32 Mbit/s)| + +1. Инфраструктура: +|Параметр|Значение|Нагрузка на одного клиента|| \ No newline at end of file diff --git a/ccpp/fxalloc/PREPROFILE.md b/ccpp/fxalloc/PREPROFILE.md new file mode 100644 index 0000000..b0bf520 --- /dev/null +++ b/ccpp/fxalloc/PREPROFILE.md @@ -0,0 +1,20 @@ +# README.md + +## Сервер: +* OS: Ubuntu Server 24.04; +* CPU: Intel Core i5‑3470, 4 @ 3.2 GHz; +* RAM: 8 GB; +* ROM: noname 256 GB SSD; +* WiFi: 2.4 GHz, прямая видимость до 6 м (~32 Mbit/s). + +## Вариативность траффика + +## Расчёт и таблица результатов proxy-Lineage2 30 MBits/s +Для расчёта принята пиковая нагрузка на сеть +Параметр Значение Обоснование +Пропускная способность канала 30 000 kbit/s Исходные данные +Доступная полоса (80 %) 24 000 kbit/s Запас на служебный трафик +С учётом накладных расходов 21 600 kbit/s Поправка на заголовки и ошибки +Пиковый трафик на клиента 100 kbit/s Массовые события в Lineage 2 +Макс. клиентов (прямой расчёт) 240 Без учёта накладных расходов сети +Макс. клиентов (реальный прогноз) 210 С запасом и поправкой на накладные расходы \ No newline at end of file diff --git a/ccpp/fxalloc/PROGRESS.md b/ccpp/fxalloc/PROGRESS.md new file mode 100644 index 0000000..1399ba8 --- /dev/null +++ b/ccpp/fxalloc/PROGRESS.md @@ -0,0 +1,36 @@ +# PROGRESS.md + +## Формат +* Даты следуют в обратном хронологическом порядке +* Формат таблиц: Статус → Задача → Краткое описание + +## Условные обозначения +* ➤ — задача выполняется +* ✔️ — задача выполнена полностью +* ⚠️ — задача отложена +* ☐ — задача ожидает выполнения + +Структура таблиц + +| Статус | Задача | Описание | +|:------:|:-------|:---------| + + +## Общий прогресс +| Статус | Задача | Описание | +|:------:|:-------|:---------| +| ✔️ |Препрофилирование|Расчёт предполагаемых нагрузок малые/средние/максимальные| +| ➤ |**Проектирование архитектуры**|Определение узких мест, API, внутреннего устройства| +| ☐ |Logic|Реализация внутренней логики модуля| +| ☐ |API|Реализация внешнего интерфейса| +| ☐ |Предварительное тестирование|Юнит-тесты, бенчмарки| +| ☐ |Нагрузочные тесты|Анализ работы при максимальных нагрузках| +| ☐ |Интеграция|Тестирование взаимодействия с другими модулями| +| ☐ |Документирование|Подготовка технической документации модуля| +| ☐ |Итоги|Финальная проверка, анализ работы, фидбэк(если предусмотрен)| + + +## 23.04.2026 +| Статус | Задача | Описание | +|:------:|:-------|:---------| +| ✔️ |Препрофилирование|Расчёт предполагаемых нагрузок малые/средние/максимальные| \ No newline at end of file diff --git a/template/README.md b/ccpp/fxalloc/README.md similarity index 100% rename from template/README.md rename to ccpp/fxalloc/README.md diff --git a/ccpp/fxalloc/TODO.md b/ccpp/fxalloc/TODO.md new file mode 100644 index 0000000..19890a9 --- /dev/null +++ b/ccpp/fxalloc/TODO.md @@ -0,0 +1,21 @@ +# TODO.md + +## Информация +* Файл для отслеживания текущих задач модуля **FXAlloc** +* Последнее обновление: [25.04.2026] + +## Условные обозначения +* 🔥 - Наивысший приоритет +* 🔴 - Высокий приоритет +* 🟡 - Средний приоритет +* 🟢 - Низкий приоритет + +## Задачи в процессе +|Срочность|Компонент|Задача|Описание| +|:-------:|:-------:|:-----|:-------| +|🔥|**FXAlloc**|**Проектирование архитектуры**|Интерфейс, внутреннее устройство| +|🔥|**FXAlloc**|**Документация**|Описание архитектуры решения, ведение DAIRY.md| +|🔥|**FXAlloc**|**Logic**|Реализации внутренних методов| +|🔥|**FXAlloc**|**API**|Реализация внешних методов| +|🔥|**FXAlloc**|**Тестирование**|Написание юнит-тестов| +|🔥|**FXAlloc**|**Оптимизация**|Анализ производительности| diff --git a/fxalloc/includes/_FXAlloc.h b/ccpp/fxalloc/headers/_FXAlloc.h similarity index 100% rename from fxalloc/includes/_FXAlloc.h rename to ccpp/fxalloc/headers/_FXAlloc.h diff --git a/fxalloc/headers/FXAlloc.h b/ccpp/fxalloc/includes/FXAlloc.h similarity index 100% rename from fxalloc/headers/FXAlloc.h rename to ccpp/fxalloc/includes/FXAlloc.h diff --git a/fxalloc/includes/fxmbmeta.h b/ccpp/fxalloc/includes/fxmbmeta.h similarity index 100% rename from fxalloc/includes/fxmbmeta.h rename to ccpp/fxalloc/includes/fxmbmeta.h diff --git a/fxalloc/includes/mword.h b/ccpp/fxalloc/includes/mword.h similarity index 100% rename from fxalloc/includes/mword.h rename to ccpp/fxalloc/includes/mword.h diff --git a/fxalloc/src/FXAlloc.c b/ccpp/fxalloc/src/FXAlloc.c similarity index 100% rename from fxalloc/src/FXAlloc.c rename to ccpp/fxalloc/src/FXAlloc.c diff --git a/server/.gitignore b/ccpp/fxxogame/.gitignore similarity index 100% rename from server/.gitignore rename to ccpp/fxxogame/.gitignore diff --git a/template/CMakeLists.txt b/ccpp/fxxogame/CMakeLists.txt similarity index 100% rename from template/CMakeLists.txt rename to ccpp/fxxogame/CMakeLists.txt diff --git a/template/DAIRY.md b/ccpp/fxxogame/DAIRY.md similarity index 100% rename from template/DAIRY.md rename to ccpp/fxxogame/DAIRY.md diff --git a/template/PROGRESS.md b/ccpp/fxxogame/PROGRESS.md similarity index 100% rename from template/PROGRESS.md rename to ccpp/fxxogame/PROGRESS.md diff --git a/ccpp/fxxogame/README.md b/ccpp/fxxogame/README.md new file mode 100644 index 0000000..c22da53 --- /dev/null +++ b/ccpp/fxxogame/README.md @@ -0,0 +1 @@ +# README.md \ No newline at end of file diff --git a/template/TODO.md b/ccpp/fxxogame/TODO.md similarity index 100% rename from template/TODO.md rename to ccpp/fxxogame/TODO.md diff --git a/server/headers/_XOGame.h b/ccpp/fxxogame/headers/_XOGame.h similarity index 100% rename from server/headers/_XOGame.h rename to ccpp/fxxogame/headers/_XOGame.h diff --git a/template/headers/_header.h b/ccpp/fxxogame/headers/_header.h similarity index 100% rename from template/headers/_header.h rename to ccpp/fxxogame/headers/_header.h diff --git a/server/includes/XOGame.h b/ccpp/fxxogame/includes/XOGame.h similarity index 100% rename from server/includes/XOGame.h rename to ccpp/fxxogame/includes/XOGame.h diff --git a/template/src/source.c b/ccpp/fxxogame/src/source.c similarity index 100% rename from template/src/source.c rename to ccpp/fxxogame/src/source.c diff --git a/template/.gitignore b/ccpp/server/.gitignore similarity index 100% rename from template/.gitignore rename to ccpp/server/.gitignore diff --git a/server/CMakeLists.txt b/ccpp/server/CMakeLists.txt similarity index 100% rename from server/CMakeLists.txt rename to ccpp/server/CMakeLists.txt diff --git a/server/headers/_Core.h b/ccpp/server/headers/_Core.h similarity index 100% rename from server/headers/_Core.h rename to ccpp/server/headers/_Core.h diff --git a/server/includes/Core.h b/ccpp/server/includes/Core.h similarity index 100% rename from server/includes/Core.h rename to ccpp/server/includes/Core.h diff --git a/server/server.c b/ccpp/server/server.c similarity index 100% rename from server/server.c rename to ccpp/server/server.c diff --git a/server/src/Core.c b/ccpp/server/src/Core.c similarity index 100% rename from server/src/Core.c rename to ccpp/server/src/Core.c diff --git a/server/src/XOGame.c b/ccpp/server/src/XOGame.c similarity index 100% rename from server/src/XOGame.c rename to ccpp/server/src/XOGame.c diff --git a/server/src/db/mysql.sql b/ccpp/server/src/db/mysql.sql similarity index 100% rename from server/src/db/mysql.sql rename to ccpp/server/src/db/mysql.sql diff --git a/server/src/db/queries.c b/ccpp/server/src/db/queries.c similarity index 100% rename from server/src/db/queries.c rename to ccpp/server/src/db/queries.c diff --git a/server/src/db/sqlite.sql b/ccpp/server/src/db/sqlite.sql similarity index 100% rename from server/src/db/sqlite.sql rename to ccpp/server/src/db/sqlite.sql diff --git a/ccpp/template/.gitignore b/ccpp/template/.gitignore new file mode 100644 index 0000000..1879d44 --- /dev/null +++ b/ccpp/template/.gitignore @@ -0,0 +1,2 @@ +build +sandbox diff --git a/ccpp/template/CMakeLists.txt b/ccpp/template/CMakeLists.txt new file mode 100644 index 0000000..9417e00 --- /dev/null +++ b/ccpp/template/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) +project(neurox) +set(HEADERS headers/_header.h) +set(INCLUDES includes/include.h) +set(SOURCES src/source.c) +add_library(template SHARED ${INCLUDES} ${HEADERS} ${SOURCES}) +target_include_directories(neurox PUBLIC includes PRIVATE headers) \ No newline at end of file diff --git a/ccpp/template/DAIRY.md b/ccpp/template/DAIRY.md new file mode 100644 index 0000000..6eb7d30 --- /dev/null +++ b/ccpp/template/DAIRY.md @@ -0,0 +1 @@ +# DAIRY.md \ No newline at end of file diff --git a/ccpp/template/PROGRESS.md b/ccpp/template/PROGRESS.md new file mode 100644 index 0000000..c8e213b --- /dev/null +++ b/ccpp/template/PROGRESS.md @@ -0,0 +1,30 @@ +# PROGRESS.md + +## Формат +* Даты следуют в обратном хронологическом порядке +* Формат таблиц: Статус → Задача → Краткое описание + +## Условные обозначения +* ➤ — задача выполняется +* ✔️ — задача выполнена полностью +* ⚠️ — задача отложена +* ☐ — задача ожидает выполнения + +Структура таблиц + +| Статус | Задача | Описание | +|:------:|:-------|:---------| + + +## Общий прогресс +| Статус | Задача | Описание | +|:------:|:-------|:---------| +| ☐ |Препрофилирование|Расчёт предполагаемых нагрузок малые/средние/максимальные| +| ☐ |Проектирование архитектуры|Определение узких мест, API, внутреннего устройства| +| ☐ |Logic|Реализация внутренней логики модуля| +| ☐ |API|Реализация внешнего интерфейса| +| ☐ |Предварительное тестирование|Юнит-тесты, бенчмарки| +| ☐ |Нагрузочные тесты|Анализ работы при максимальных нагрузках| +| ☐ |Интеграция|Тестирование взаимодействия с другими модулями| +| ☐ |Документирование|Подготовка технической документации модуля| +| ☐ |Итоги|Финальная проверка, анализ работы, фидбэк(если предусмотрен)| diff --git a/ccpp/template/README.md b/ccpp/template/README.md new file mode 100644 index 0000000..c22da53 --- /dev/null +++ b/ccpp/template/README.md @@ -0,0 +1 @@ +# README.md \ No newline at end of file diff --git a/ccpp/template/TODO.md b/ccpp/template/TODO.md new file mode 100644 index 0000000..888824e --- /dev/null +++ b/ccpp/template/TODO.md @@ -0,0 +1,15 @@ +# TODO.md + +## Информация +* Файл для отслеживания текущих задач модуля **Module** +* Последнее обновление: [дд.мм.гггг] + +## Условные обозначения +* 🔥 - Наивысший приоритет +* 🔴 - Высокий приоритет +* 🟡 - Средний приоритет +* 🟢 - Низкий приоритет + +## Задачи в процессе +|Срочность|Компонент|Задача|Описание| +|:-------:|:-------:|:-----|:-------| diff --git a/ccpp/template/headers/_header.h b/ccpp/template/headers/_header.h new file mode 100644 index 0000000..2d03a86 --- /dev/null +++ b/ccpp/template/headers/_header.h @@ -0,0 +1,17 @@ +#pragma once + +/** + * @author admin@felexdev.ru + * @version 1.0.0 + * + * @brief Header template + */ + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/template/includes/include.h b/ccpp/template/includes/include.h similarity index 100% rename from template/includes/include.h rename to ccpp/template/includes/include.h diff --git a/ccpp/template/src/source.c b/ccpp/template/src/source.c new file mode 100644 index 0000000..0896791 --- /dev/null +++ b/ccpp/template/src/source.c @@ -0,0 +1,10 @@ +/** + * @author admin@felexdev.ru + * @version 1.0.0 + * + * @brief Sourcecode template + */ + +int example() { + return 0; +} \ No newline at end of file diff --git a/server/includes/fxalloc.h b/server/includes/fxalloc.h deleted file mode 100644 index da798db..0000000 --- a/server/includes/fxalloc.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#ifndef _WIN32 - #include -#endif //_WIN32 - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - void* fxalloc(size_t _NBytes); - -#ifdef __cplusplus -} -#endif // __cplusplus