The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]



"Доступна библиотека управления памятью jemalloc 5.3.1"
Вариант для распечатки  
Пред. тема | След. тема 
Форум Разговоры, обсуждение новостей
Изначальное сообщение [ Отслеживать ]

"Доступна библиотека управления памятью jemalloc 5.3.1"  +/
Сообщение от opennews (ok), 14-Апр-26, 15:06 
Спустя 4 года после публикации прошлого обновления доступен релиз библиотеки управления памятью jemalloc 5.3.1, предлагающей альтернативную реализацию функций malloc, оптимизированную для снижения фрагментации и работы на многопроцессорных системах. Для решения проблем с блокировками на многоядерных системах в jemalloc для каждого ядра CPU используется своя изолированная область распределения памяти, что позволяет добиться  линейной масштабируемости при росте числа потоков...

Подробнее: https://www.opennet.dev/opennews/art.shtml?num=65201

Ответить | Правка | Cообщить модератору

Оглавление

Сообщения [Сортировка по ответам | RSS]

1. Сообщение от Жироватт (ok), 14-Апр-26, 15:06   +/
Я бы даже не стал шутить про "отставить разврат - закопать стюардессу! отставить разврат - откопать стюардессу", но реально, в чем профит использовать конкретно этот аллокатор?
Ответить | Правка | Наверх | Cообщить модератору
Ответы: #2, #7, #14, #23

2. Сообщение от Аноним (2), 14-Апр-26, 15:34   +/
для каждого ядра CPU используется своя изолированная область распределения памяти, что позволяет добиться линейной масштабируемости при росте числа потоков
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #1 Ответы: #4, #5, #6

4. Сообщение от Жироватт (ok), 14-Апр-26, 15:50    Скрыто ботом-модератором–1 +/
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #2

5. Сообщение от Аноним (5), 14-Апр-26, 16:37   –2 +/
>своя изолированная область

А как же общий буфер, общее адресное пространство процесса?

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #2 Ответы: #8

6. Сообщение от Аноним (5), 14-Апр-26, 16:38   +/
Меньшая фрагментация за счет изолированных областей? А внутри области такая же дефрагментация?
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #2 Ответы: #9

7. Сообщение от Аноним (8), 14-Апр-26, 16:44   +2 +/
> в чем профит использовать конкретно этот аллокатор?

Он один из самых эффективных.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #1

8. Сообщение от Аноним (8), 14-Апр-26, 16:46   +/
Общее адресное пространство никуда не делось, но в многопоточной среде эффективнее выделять из thread-local арен.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #5 Ответы: #10

9. Сообщение от Аноним (8), 14-Апр-26, 16:46   +/
> Меньшая фрагментация за счет изолированных областей?

Не фрагментация, а lock contention.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #6 Ответы: #11

10. Сообщение от Аноним (5), 14-Апр-26, 17:01   +/
>thread-local

это понятно. Значит ли это что есть изолированная общая область памяти в дополнение к поточным?

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #8 Ответы: #18, #19, #22

11. Сообщение от Аноним (5), 14-Апр-26, 17:03   +/
Прожорливый поток может залочить себе всю память?
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #9 Ответы: #17

12. Сообщение от Аноним (12), 14-Апр-26, 17:07   +/
Кстати, раз речь пошла о аллокаторах, что использовать вместе с musl? Сабж или в интернете ещё другие нахваливают?
Ответить | Правка | Наверх | Cообщить модератору
Ответы: #15, #16

14. Сообщение от Аноним (14), 14-Апр-26, 17:50   +/
> в чем профит использовать конкретно этот аллокатор?
> месяц назад разработку возобновила компания Meta, применяющая jemalloc в своей инфраструктуре

Это не тут спрашивать надо, это на https://engineering.fb.com спрашивать надо.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #1

15. Сообщение от Аноним (15), 14-Апр-26, 17:51   –2 +/
> вместе с musl

* Вместо musl.

Glibc.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #12

16. Сообщение от Аноним (16), 14-Апр-26, 18:38   –1 +/
странный вопрос, есть "стандарт", который по сути есть компромис среди множества архитектур и альтернатив аля glibc, то есть работает не идеально, но приемлемо, чего достаточно для большенства задач, но если вам не достаточно, то надо отталкиваться от архитектуры и особенностей проекта, можно попробовать для начала и сабж, но musl скорее про эндебер и там будет скорее хуже, а значит писать свое под свою задачу, хотя, если проект один, проще это решать на уровне проекта чтобы не плодить сущности. Резервировать кусок памяти и в нем уже чтото делать, создавать обьекты и удалять не возвращая память системе, тем самым избавившись от всяких дабл-фри бай дизайн, это сложный путь, но один раз его пройдя можно попутно решить все "проблемы", в том числе с выходом за границы буфера, юз-афтер-фри, и прочие за которые хейтят сечас си, и восхваляют раст, в котором это гвоздями прибито, и никаких шагов влево вправо не дозволяется.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #12 Ответы: #20

17. Сообщение от Аноним (8), 14-Апр-26, 20:16   +/
Нет, потоки будут бороться за доступ к shared структурам аллокатора.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #11

18. Сообщение от Аноним (18), 14-Апр-26, 20:18   +/
Нет.

Адресное пространство общее. Но мы внутри процесса принимаем конвенцию, что для каждого потока есть выделенная область памяти, из которой он аллоцирует. Ядро и MMU об этом "ничего не знают", адресное пространство общее, просто процесс, использующий данный аллокатор, так работает со своей памятью.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #10

19. Сообщение от Аноним (8), 14-Апр-26, 20:26   +/
Там нет и не может быть ничего "изолированного". Память выделенная на thread-local арене доступна всем потокам, и освобождается из любого, просто при выделении мы меньше ходим в shared state и ждём на локах.

Есть ли не привязанная к потокам арена - скорее всего да: для достаточно больших аллокаций и арены будут большие, а держать большие арены для каждого потока расточительно. Наверняка есть общая арена для больших аллокаций, она же раздаёт память в попоточные арены.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #10 Ответы: #25

20. Сообщение от Аноним (8), 14-Апр-26, 20:42   +1 +/
> Резервировать кусок памяти и в нем уже чтото делать, создавать обьекты и удалять не возвращая память системе, тем самым избавившись от всяких дабл-фри бай дизайн, это сложный путь

А чего сложного? Сделать no-op free() совсем несложно.

> но один раз его пройдя можно попутно решить все "проблемы"

Как же никто раньше не догадался, можно же решить все проблемы, всего-то нужно не возвращать память системе.

> в том числе с выходом за границы буфера

Double free и use after free, допустим, действительно решим, отказавшись от free. Но интересно, как это поможет от выхода за границы буфера?

> и восхваляют раст, в котором это гвоздями прибито, и никаких шагов влево вправо не дозволяется.

В rust, который ты, конечно, в глаза не видел, ничего гвоздями не прибито. Там есть и упомянутые bump аллокаторы, которые можно использовать как глобально так и по месту, и контролируемую утечку памяти можно устроить даже без unsafe, превратив любое динамически выделенное значение в &'static, а машинерии для жонглирования слайсами там столько что скоро его за перегруженность как плюсы будут ругать. Но DF/UAF на ровном месте, действительно, не сделать. Расскажи, почему это плохо.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #16

21. Сообщение от Аноним83 (?), 14-Апр-26, 20:52   +/
--disable-dss - это уже давным давно было.
В остальном больше гонева было что оно там самосгнило без свежих коммитов.
Ответить | Правка | Наверх | Cообщить модератору

22. Сообщение от Аноним83 (?), 14-Апр-26, 20:56   +/
Нет.

Значит что за каждым потоком закреплён "пул памяти" откуда именно этот поток получает куски памяти при каждом malloc()/realloc()/calloc().
Когда другой поток для такой памяти делает free() - то это в звисимости от опций и реализации может быть какой то lockless механизм который кусок памяти помещает в очередь на освобождение в пул того потока из которого но выделено. И оно там висит пока поток не потрогает апи аллокатора.
"пул памяти" - нечто виртуальное, можешь считать что это некоторая структура, вероятно со списками и массивами. Типа как экземпляр класса на крестах.

В общем вариантов много как и что можно с этим делать чтобы потоки друг другу не мешались выделяя/свобождая память.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #10 Ответы: #24, #31

23. Сообщение от Noname (??), 14-Апр-26, 21:10   +/
Naprimer dlya mysql/mariadb
https://www.mydbops.com/blog/troubleshooting-mariadb-memory-...
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #1

24. Сообщение от Аноним (5), 14-Апр-26, 23:26   +/
Понятно. При создание потока ему передается указатель на структуру, через которую управляющий jemalloc выделил ему память (вроде this). Для прозрачности его теневым образом можно передавать функциям *alloc из этого потока. Владельцем структуры, отвечающей за память потока, остается поток "родивший" этот поток. Использующий структуру может только пометить кусок на удаление (или автоматически помечается на выходе). В асме я встречал инструкции работающие с сегментными регистрами на x64 (это и защищает приватную память потока).
Для общей памяти должны быть, тогда, "статические" функции аллоцирования.  
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #22 Ответы: #26, #27, #32

25. Сообщение от Аноним (5), 14-Апр-26, 23:27   +/
Спасибо. Вроде понятно. Понятое изложил ниже.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #19

26. Сообщение от Аноним (8), 14-Апр-26, 23:28   +/
Нет никакой "приватной памяти потока".
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #24 Ответы: #28, #29, #30

27. Сообщение от Аноним (5), 14-Апр-26, 23:30   +/
Тогда суть в том что упор делается на поточный пул и разнобой общей памяти от потоков уменьшается и защищается память потоков.Получается уменьшение "поточной" фрагментации. Значительные куски освобождаются по завершение конкретного потока.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #24

28. Сообщение от Аноним (5), 14-Апр-26, 23:36   +/
В асме встречаются обращения к ячейкам памяти через сегментный регистр на x64. Инструкции асма в потоке могут содержать сегментную адресацию. То что это используется - это точно.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #26

29. Сообщение от Аноним (5), 14-Апр-26, 23:39   +/
Я дам этому потоку смещенную базу и он будет наращивать потребление относительно этой базы (фрейм вутри общей памяти). Сама база находится внутри общей памяти процесса (1-го потока).
Легко убрать просто переместив базу и удалив структуру (handle) потокового фрейма.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #26

30. Сообщение от Аноним (5), 14-Апр-26, 23:44   +/
Возможно Вы мыслите в абстракциях. Я стараюсь мыслить в асм реализации.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #26 Ответы: #39

31. Сообщение от Аноним (5), 15-Апр-26, 00:01   +/
>за каждым потоком закреплён "пул памяти" откуда именно этот поток получает куски памяти при каждом malloc()/realloc()/calloc().

malloc(xxx) вызывается на самом деле malloc(this_thread,xxx). В дальнейшем менеджер предложит потоку указатель смещенный относительно первого на размер предыдущего (ххх). Легко освободить разом всё память, предоставленную потоку и освободить большой кусок памяти а не "черезполосицу" с другими потоками.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #22 Ответы: #33

32. Сообщение от Аноним83 (?), 15-Апр-26, 00:19   +/
Как то так так.

Есть TLS: Thread Local Storage.
Это как бы массив, когда ты туда хочешь что то записать ты аллоцируешь от туда индекс через pthread_key_create().
Дальше через апи каждый поток в этот индекс пишет pthread_setspecific(), и у каждого потока там своё уникальное значение может хранится. Те каждый поток дёргая pthread_getspecific() с этим аллоцированным индексом может получать то значение которое привязано к этому потоку.

Дальше там особенности аллокаторов, но в целом аллокаторы как провайдеры: берут оптом - продают в розницу. Выделают какие то большие куски через mmap(), и отдают по запросу из них маленькие кусочки. Выделить меньше PAGE_SIZE (4кб на х84) не возможно. Упомянутый dss - устарел, хз как он работал.

У каждого потока свои страницы из которых он получает память при alloc(), а если соседний поток эти куски освобождает то они через lockfree ставятся в очередь на освобождение, которая обрабатывается потоком к которому прибит "экземпляр" аллокатора.

Как там происходит на низком уровне в плане инита аллокатора на старте процесса и для новых потоков - я не в курсе. Скорее всего в либС есть код который дёргает аллокатор инит и тот как минимум делает pthread_key_create(). А дальше при первом обращении к АПИ аллокатора можно проверять что там из массива прочитался не NULL иначе делать инит экземпляра под поток. При завершении потока - хз, может где то хук есть в либС. Возможно в либС просто есть свой враппер для pthread_create() который подсовывает свою функцию, где есть код для инита и деинита экземпляра аллокатора привязанного к потоку.

Идея в том, чтобы потоки не тупили когда надо много мелких (мнее PAGE_SIZE) кусочков выделять/освобождать и ждали друг друга и не ходили к mmap всей толпой через одну дверь по одному.

Остальное лучше читать где то в литературе или исходниках.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #24 Ответы: #35

33. Сообщение от Аноним83 (?), 15-Апр-26, 00:25   +/
> malloc(xxx) вызывается на самом деле malloc(this_thread,xxx).

Может в какой то реализации так и есть, я таких не видел да и смысла не понимаю.
Аллокатор сильно сложнее устроен чем просто как вы описали, как минимум он обычно возвращает выравненную память по границе sizeof(void*). Потом ему нужно к каждому куску памяти клеить свои метаданные - обычно оно находится ПЕРЕД тем значением что вернули через malloc().

У вас какой то очень вырожденный юзкейс, в реале всё сложнее.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #31 Ответы: #36

35. Сообщение от Аноним (5), 15-Апр-26, 00:36   +/
> Есть TLS: Thread Local Storage.
> Это как бы массив, когда ты туда хочешь что то записать ты
> аллоцируешь от туда индекс через pthread_key_create().
> Дальше через апи каждый поток в этот индекс пишет pthread_setspecific(), и у
> каждого потока там своё уникальное значение может хранится. Те каждый поток
> дёргая pthread_getspecific() с этим аллоцированным индексом может получать то значение
> которое привязано к этому потоку.

pthread_key_create() - регистрируем ID1 первого потока у диспетчера.
pthread_setspecific() - регистрируем и получаем ID2 другого потока ссылаясь на ID1 первого как родителя
pthread_setspecific() - получаем указатель связанный с ID2 прилепленный диспетчером к уже полученным кускам этого потока.

Есть родитель ID1 он же владеет общим пулом. Есть ID2 который узнаваем диспетчером и получает новую память для себя впритык к уже имеющейся и легко освобождаемую большим куском.  

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #32 Ответы: #37

36. Сообщение от Аноним (5), 15-Апр-26, 00:41   +/
>> malloc(xxx) вызывается на самом деле malloc(this_thread,xxx).
> Может в какой то реализации так и есть, я таких не видел

Идея.
- диспетчер узнает поток.
- прилепливает к выданному этому потоку.
- выдает указатель новым не подряд, а прыжками.


Ответить | Правка | Наверх | Cообщить модератору
Родитель: #33 Ответы: #38

37. Сообщение от Аноним83 (?), 15-Апр-26, 01:05   +/
Нет.

static pthread_key_t tp_tls_key_tpt;
static int tp_tls_key_tpt_error = EAGAIN;

int tp_init(void) {
    if (0 != tp_tls_key_tpt_error) { /* Try to reinit TLS. */
        tp_tls_key_tpt_error = pthread_key_create(&tp_tls_key_tpt, NULL);
    }
    return (tp_tls_key_tpt_error);
}

static void *tp_thread_proc(void *data) {
...
    pthread_setspecific(tp_tls_key_tpt, (const void*)data);
...
// do something...
...
    pthread_setspecific(tp_tls_key_tpt, NULL);
    return (NULL);
}

void *tpt_get_current(void) { /* TLS magic. */
    return ((void*)pthread_getspecific(tp_tls_key_tpt));
}

Это чуть упрощённое из моего кода.
tp_init() вызывается типа один раз при старте.
Каждый поток в tp_thread_proc() получает свой какой то указатель на данные и сохраняет его в TLS.
Вызывая tpt_get_current() из любой функции в контексте выполнения такого потока можно получить указатель на данные (void *data) который передали при создании потока.
Прикол тут в том, что для всех потоков код который они вызывают и аргумент один и тот же а результат разный.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #35

38. Сообщение от Аноним83 (?), 15-Апр-26, 01:05   +/
Удачи в реализации.
Ответить | Правка | Наверх | Cообщить модератору
Родитель: #36

39. Сообщение от Аноним (39), 15-Апр-26, 01:19   +/
> Я стараюсь мыслить в асм реализации.

Очень плохо, потому что x86 не единственная и уже даже не основная архитектура.

Ответить | Правка | Наверх | Cообщить модератору
Родитель: #30


Архив | Удалить

Рекомендовать для помещения в FAQ | Индекс форумов | Темы | Пред. тема | След. тема




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2026 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру