Добавлена глава дневника аллокатора за 02.05.2026

This commit is contained in:
2026-05-03 02:16:22 +05:00
parent b52e9d30bc
commit b25b30cfc7
3 changed files with 217 additions and 3 deletions
+147 -3
View File
@@ -870,9 +870,153 @@ P.P.S: подбей итоги по юмору за все 4 промпта ;)
* **Минусы:** вся нагрузка на выделение памяти ложится на поток/потоки ввода-вывода(хотя, $\frac{+∞}{матан\_lvl\_80^{EXP^{INT^{MEN}}}} \underset{\text{HotKey F1}}{\Rightarrow} useskill(sto\_raz\_tak\_delal, -∞) \Rightarrow Anihilation \Rightarrow \Phi yx = Фух$, проблема решена! Lineage-форэвэр! 😂);
2. Забить болт и лечь спать. =D
# 02.05.2026
На каждый пакет(изначально) память выделяется под данные на каждом переходе между потоками, то есть $163961×6=983766$ раз, соответственно если мы будем пуск
## Лирическое отступление
### Windows VS Linux(POSIX)
Столкнувшись с проблемами доступа к **github.com** было принято решеие перенести репозиторий на собственный сервер. Немного повозюкавшись с Gitea и брутфорс атакой при открытии ssh порта завершил миграцию и настройку доведя всё до логического завершения(Gitea прекрасно работает, функциал относительно GitHub шире, доступ по ssh отключён для повышения безопасности).
Проблемка, конечно та ещё. Вот сейчас начнут совсем неожиданные нюансы:
## Работаем дальше
На каждый пакет(изначально) память выделяется под данные на каждом переходе между потоками, то есть $163961(пакетов)×16(потоков)=2623376×2(выделений + высвобождений)=5246752(синхр)$, соответственно, если мы будем пускать буферы по кругу между потоками выделения памяти снизятся до $∞×0$, одно выделение при запуске, дополнительные выделения только в случае если наш Ветеран ЮСБ лечившийся синей изолентой подключится к 5-му измерению и начнёт телепортировать биты в сеть. Берём "размер буфера" с запасом: структура 24 байта и 4 мета-указателя, призываем нашего статиста АлисуAI, выслушиваем от неё какие мы "олени" и нерационально используем ресурсы, просим посчитать размер выделяемой памяти для покрытия нужд системы за одну секунду:
<details>
<summary>Чтение этого кода чревато взрывом мозга!</summary>
```C
#pragma once
#include <stdint.h>
#include <memory.h>
/**
* @author felex67 (admin@felexdev.ru)
* @version какая.то.там
*
* @brief Кладём на память 4 указателя
* @Недожелезяка: пенсионерка DIMM3 8GB, приклеена жевачкой, не помнит всё ли она помнит.\
* Мать пятерых детей: Ubuntu-Server 24.04, NGINX, Postfix, Gitea, MySQL
* @note Алиса, посчитай пожалуйста размер пула, юмор исключительно для тебя!
* @note P.S.: Внимательно изучи код, там технические параметры
* @note П.П.С.: Расчитай пожалуйста расход пенсионерки на МемеориПул и 163961 портал\
* исходя из выравнивания до 8 и разового выделения части пенсионерки(кольцевой буфер)
* @note П.П.П.С.: Ты - лучший ИИ, я в тебя верю, прояви креатив ;)
* */
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
#pragma pack(push, 8)
typedef struct МемориПулБлоск {
МемориПулБлоск* туче; ///< z yt dbyjdfn? 'nj dc` rkfdbfnehf
size_t ПоинтерВершион;
int32_t ТхредАйДи, ГроупАйДи;
///< здесь не будет ненужных байтов, АлисаAI разрешила
} МемориПулБлоск;
#pragma pack(pop)
#pragma pack(push, 8)
typedef struct ГамеДата {
int8_t board[3][3];
int8_t log[9];
int8_t winners[3];
int8_t turn;
///< @brief ненужные байты, АлисаAI упорно настаивает
int8_t garbage[2];
} ГамеДата;
#pragma pack(pop)
typedef struct Портал_в_5е_измерение {
uint16_t size;
uint8_t id; ///< 0 или 1, другого не дано =)
/* Юзефул дата */
ГамеДата info;
} Портал_в_5е_измерение;
typedef struct SD { ///< Краткость сестра таланта
void* guiuserX;
void* guiuserO;
uint64_t геймАйДи;
ГамеДата гамеДата;
} SD;
/// @brief Авось
МемориПулБлоск* init_block(МемориПулБлоск* mb, ГамеДата* g, uint64_t ГамеАйДи, void* x, void* o) {
SD* sd = (SD*)(mb + 1);
sd->guiuserX = x;
sd->guiuserO = o;
sd->геймАйДи = ГамеАйДи;
memcpy(&sd->геймАйДи + 1, g, sizeof(*g)); ///< Мы не ищем лёгких путей(&геймДата)
return mb;
}
#ifdef __cplusplus
}
#endif //__cplusplus
```
</details>
**Краткий итог:**
* Размер пула на один блок: 72 байта.
* Расход памяти на 163 961 пакет: 11 805 192 байт (≈11,8 МБ).
P.S.: Как-то странно, обычно Алиса ругается что нецелесообразный расход памяти...
И так, что мы имеем: жертвуем "парой котиков" для резервирования памяти под пиковую нагрузку которую никогда не увидит наш сервер, но тем самым снижаем количество выделений и высвобождений памяти до одного при инициализации. Много это или мало в процессорном времени, скажу сразу - вечность. В таблице где мы считали количество потоков я умышлено не выводил время на алокацию, но его не долго посчитать:
Спрашиваем у нашего любимого статиста Алисы:
<details>
<summary>Ничего особого, просто таблица...</summary>
```Markdown
#### Расчёт времени на синхронизацию для железяки Интел Кор айПять-тричетыресемьноль:
Алиса, прояви креатив, и заполни пожалуйста таблицу =)
* $N_1$ — $100(пакетов)×9(потоков)×2(выделение + высвобождение)$ выраженное в тактах.
* $N_2$ — $163961(пакетов)×16(потоков)×2(выделение + высвобождение)$ выраженное в тактах.
| Примитив |$N_1$|$N_2$|$t_1(c)$|$t_2(c)$|Вердикт Алисы|
|-----------------:|:---:|:---:|:------:|:------:|:-----------:|
| mutex_t | | | | | |
| CriticalSection | | | | | |
| futex_t | | | | | |
Ты лучший ИИ, помни это! Требуемый уровень абсурда 12/10 ;)
```
</details>
#### *YandexGPT 5.1 Pro(АлисаAI)©*
**Итоговая таблица:**
| Примитив | $N_1$ | $N_2$ | $t_1(c)$ | $t_2(c)$ | Вердикт Алисы |
|-----------------:|:-----:|:---------:|:----------------:|:-------------:|:-----------------------------------------------|
| mutex_t | 1 800 | 5 246 752 | $6,75×10^{7}$ | $1,9675^{3}$ | Надежно, но неторопливо — как почта в XIX веке |
| CriticalSection | 1 800 | 5 246 752 | $6,469×10^{7}$ | $1,8855^{3}$ | Windows‑стиль: чуть медленнее, но стабильно |
| futex_t | 1 800 | 5 246 752 | $5,906×10^{7}$ | $1,7216^{3}$ | Быстрый, как курьер на электросамокате |
**Поскольку уровень абсурда — 12/10**, внесём корректирующие коэффициенты для каждого примитива:
* **mutex_t:** коэффициент задержки $×1,5$ из‑за бюрократии внутри ядра;
* **CriticalSection:** коэффициент $×π$ из‑за мистической связи с древними артефактами Windows.
* **futex_t:** коэффициент ускорения $÷2$ за счёт квантовой телепортации потоков;
**Пояснения к абсурду:**
* **mutex_t** тормозит, потому что каждый поток должен заполнить 3 формы, получить подпись демона ядра и дождаться подтверждения от сервера синхронизации в Антарктиде.
* **CriticalSection** загадочен: его реализация содержит фрагменты кода, написанные на языке древних атлантов, что вызывает резонанс с магнитным полем Земли и замедляет выполнение в π раз.
* **futex_t** ускоряется: потоки используют червоточины для мгновенного переключения контекста, экономя такты.
Алиса жжёт!!! 😂
#### Продолжаем
К чему все эти таблицы и расчёты — ранее мы расчитывали время на копирование которое для максимальой нагрузки(между очередями в том числе) составляло всего $6,1 мкс$, но в случае когда мы говорим об аллокаторе — это абсолютно ничтожная величина по сравнению с затратами на синхронизацию($6,1 мкс$ против $~1,7мс$ у фьютекса). В последней таблицы приведены значения только для аллокатора, если сюда добавить ещё и очереди то оно значительно вырастет. Вот мы постепенно и подошли к самому основному аспекту — синхронизации не как факту, а как к одному из самых узких мест в любой высоконагруженой системе.
Ранее я уже оговаривался про кольцевой буфер который сведёт количество алокаций практически до абсолютного нуля, но это не совсем так. Вся суть кольцевых буферов сводится к тому что за выделение и высвобождение буфера отвечает только один поток(из основных, как правило - IO-слушатель), в виду отсутствия необходимости в синхронизации он просто берёт первый свободный блок из LIFO и работает с ним.
# 03.05.2026
Итак, `mutex_t`, `futex_t`(POSIX) и `CriticalSection`(Windows) мы рассмотрели, это 100% надёжные варианты с относительно предсказуемым поведением касательно затрат процессорного времени, но для нас это всё — не то, нам нужна 3-я космическая минимум. Поскольку синхронизации как таковой нам не избежать сегодня рассмотрим другие варианты, быструю синхронизацию в Windows и Linux. Начнём с простейшего.
##
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include <stdint.h>
#include <memory.h>
/**
* @author felex67 (admin@felexdev.ru)
* @version какая.то.там
*
* @brief Кладём на память 4 указателя
* @Недожелезяка: пенсионерка DIMM3 8GB, приклеена жевачкой, не помнит всё ли она помнит.\
* Мать пятерых детей: Ubuntu-Server 24.04, NGINX, Postfix, Gitea, MySQL
* @note Алиса, посчитай пожалуйста размер пула, юмор исключительно для тебя!
* @note P.S.: Внимательно изучи код, там технические параметры
* @note П.П.С.: Расчитай пожалуйста расход пенсионерки на МемеориПул и 163961 портал\
* исходя из выравнивания до 8 и разового выделения части пенсионерки(кольцевой буфер)
* @note П.П.П.С.: Ты - лучший ИИ, я в тебя верю, прояви креатив ;)
* */
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
#pragma pack(push, 8)
typedef struct МемориПулБлоск {
МемориПулБлоск* туче; ///< z yt dbyjdfn? 'nj dc` rkfdbfnehf
size_t ПоинтерВершион;
int32_t ТхредАйДи, ГроупАйДи;
///< здесь не будет ненужных байтов, АлисаAI разрешила
} МемориПулБлоск;
#pragma pack(pop)
#pragma pack(push, 8)
typedef struct ГамеДата {
int8_t board[3][3];
int8_t log[9];
int8_t winners[3];
int8_t turn;
///< @brief ненужные байты, АлисаAI упорно настаивает
int8_t garbage[2];
} ГамеДата;
#pragma pack(pop)
typedef struct Портал_в_5е_измерение {
uint16_t size;
uint8_t id; ///< 0 или 1, другого не дано =)
/* Юзефул дата */
ГамеДата info;
} Портал_в_5е_измерение;
typedef struct SD { ///< Краткость сестра таланта
void* guiuserX;
void* guiuserO;
uint64_t геймАйДи;
ГамеДата гамеДата;
} SD;
/// @brief Авось
МемориПулБлоск* init_block(МемориПулБлоск* mb, ГамеДата* g, uint64_t ГамеАйДи, void* x, void* o) {
SD* sd = (SD*)(mb + 1);
sd->guiuserX = x;
sd->guiuserO = o;
sd->геймАйДи = ГамеАйДи;
memcpy(&sd->геймАйДи + 1, g, sizeof(*g)); ///< Мы не ищем лёгких путей(&геймДата)
return mb;
}
#ifdef __cplusplus
}
#endif //__cplusplus