Какова производительность распределителя C/C++ в многопоточном контексте?

Когда память выделяется с помощью new или malloc, распределителю может потребоваться защитить себя от повторного входа. Я вижу два способа сделать это:

  • Большой мьютекс. Это решение простое, но имеет низкую производительность
  • Пул памяти зарезервирован для каждого потока. Производительность высокая, но оценить размер пула может быть сложно.

Я думаю, что большинство аллокаторов используют второй метод, но я не могу найти доказательства этого.

Знаете ли вы, какой распределитель использует какой метод? Есть ли какой-то стандарт по этому поводу?


person Jérôme Pouiller    schedule 26.06.2013    source источник
comment
Большинство компетентных аллокаторов сегодня используют комбинацию обоих.   -  person Mysticial    schedule 26.06.2013


Ответы (3)


Инструменты производительности Google предоставляют распределитель с именем TCMalloc. Этот распределитель использует пул памяти для каждого потока (= "система кэширования потоков"). В документации показаны измерения повышения производительности по сравнению с glibc 2.3.

Glibc использует пул памяти для каждого потока, начиная с версии 2.16.

Таким образом, больше никаких различий в производительности:

Fedora [мы] некоторое время использовали tcmalloc для QEMU. Затем мы снова проверили производительность и обнаружили, что дельта по сравнению с родным malloc glibc практически исчезла.

Также обратите внимание, что оператор C++ new вызывает функцию malloc, предоставляемую libc (в большинстве случаев = glibc malloc).

So:

  1. Нет, это поведение не стандартизировано
  2. Он использует пул только для каждого потока (и только если) вы используете glibc >= 2.16, иначе вы можете попытаться скомпилировать с помощью TCMalloc.
person Jérôme Pouiller    schedule 06.08.2018
comment
Пути Hoard и TBB Allocator аналогичны TCMalloc. Они используют распределители пула потоков в сочетании с механизмами блокировки (на определенных путях кода) для устранения межпотокового освобождения. Оба используют несколько пулов распределения на поток, которые распределяют объекты фиксированного размера разных размеров, чтобы ограничить фрагментацию в наихудшем случае до некоторого фиксированного процентного значения. В целом, у них очень высокая производительность, однако я не совсем уверен, что вы имеете в виду под размером пула, может быть трудно оценить - если это ссылка на определение общего объема памяти, выделенной из ОС, это легко отследить. - person Lux; 08.03.2020

С++ 17 начинает определять поведение распределителей в многопоточных приложениях:

  • std::pmr::unsynchronized_pool_resource не является потокобезопасным и не может доступ из нескольких потоков одновременно
  • std::pmr::synchronized_pool_resource может быть доступен из нескольких потоков без внешней синхронизации , и могут иметь пулы для конкретных потоков для снижения затрат на синхронизацию. Если доступ к ресурсу памяти осуществляется только из одного потока, unsynchronized_pool_resource более эффективен.
person Jérôme Pouiller    schedule 01.12.2016
comment
В C, начиная с версии 2.16, glibc предоставляется пул памяти для каждого потока: patchwork.sourceware.org /патч/19042 - person Jérôme Pouiller; 19.07.2017

Весь многопоточный программный анализ, который я сделал с помощью intel parallel studio (под окнами), всегда показывает событие блокировки и время ядра из-за распределения. Это означает, что C++ новый компилятор VS'08 в основном использует мьютекс для сохранения когерентности памяти.

Каждый раз, когда это становится проблемой в программном обеспечении, которое я разрабатываю, я пытаюсь использовать идиому RIA и удалять динамическую/общую память или использовать распределитель TLS, если память должна использоваться только самим потоком.

person alexbuisson    schedule 26.06.2013
comment
Что вы называете идиомой РИА? - person Jérôme Pouiller; 27.06.2013
comment
Извините, я сделал опечатку, я бы сказал RAII (Ressource Acquisition Is InItialization). но это ваш случай ..., это своего рода ... когда вы создаете экземпляр объекта или поток выделяет и инициализирует всю память и var, которые вы используете после. или уменьшить минимальное использование динамической памяти. - person alexbuisson; 27.06.2013