Предположим, есть два потока, которые взаимодействуют посредством асинхронной отправки сообщений данных друг другу. У каждого потока есть какая-то очередь сообщений.
У меня очень низкий уровень вопроса: какой самый эффективный способ управления памятью? Я могу придумать несколько решений:
- Отправитель создает объект через
new
. Приемник звонковdelete
. - Пул памяти (для передачи памяти обратно отправителю)
- Сборка мусора (например, Boehm GC)
- (если объекты достаточно малы) копируйте по значению, чтобы полностью избежать выделения кучи
1) является наиболее очевидным решением, поэтому я буду использовать его для прототипа. Скорее всего, это уже достаточно хорошо. Но независимо от моей конкретной проблемы, мне интересно, какая техника наиболее перспективна, если вы оптимизируете производительность.
Я ожидаю, что объединение будет теоретически лучшим, особенно потому, что вы можете использовать дополнительные знания о потоке информации между потоками. Тем не менее, я боюсь, что это также сложнее всего получить права. Много тюнинга ... :-(
Сборка мусора должна быть довольно простой после добавления (после решения 1), и я ожидаю, что она будет работать очень хорошо. Итак, я думаю, что это наиболее практичное решение, если 1) окажется слишком неэффективным.
Если объекты маленькие и простые, копирование по значению может быть самым быстрым. Однако я опасаюсь, что это накладывает ненужные ограничения на реализацию поддерживаемых сообщений, поэтому я хочу этого избежать.
источник
unique_ptr
, я думаю, ты имеешь в видуshared_ptr
. Но хотя нет сомнений в том, что использование умного указателя хорошо для управления ресурсами, это не меняет того факта, что вы используете какую-то форму выделения и освобождения памяти. Я думаю, что этот вопрос более низкого уровня.Наибольшее снижение производительности при передаче объекта из одного потока в другой - это накладные расходы на захват блокировки. Это порядка нескольких микросекунд, что значительно больше, чем среднее время, которое пара
new
/delete
занимает (порядка ста наносекунд). Разумныеnew
реализации стараются избегать блокировок практически любой ценой, чтобы избежать снижения производительности.Тем не менее, вы хотите убедиться, что вам не нужно захватывать блокировки при передаче объектов из одного потока в другой. Я знаю два основных метода для достижения этой цели. Оба работают только однонаправленно между одним отправителем и одним получателем:
Используйте кольцевой буфер. Оба процесса контролируют один указатель в этот буфер, один - указатель чтения, другой - указатель записи.
Отправитель сначала проверяет, есть ли место для добавления элемента, сравнивая указатели, затем добавляет элемент, а затем увеличивает указатель записи.
Получатель проверяет, есть ли элемент для чтения, сравнивая указатели, затем читает элемент, а затем увеличивает указатель чтения.
Указатели должны быть атомарными, поскольку они разделяются между потоками. Однако каждый указатель изменяется только одним потоком, другому нужен только доступ для чтения к указателю. Элементы в буфере могут быть самими указателями, что позволяет вам легко изменить размер вашего кольцевого буфера до размера, который не будет блокировать отправителя.
Используйте связанный список, который всегда содержит хотя бы один элемент. Получатель имеет указатель на первый элемент, отправитель имеет указатель на последний элемент. Эти указатели не являются общими.
Отправитель создает новый узел для связанного списка, устанавливая его
next
указатель наnullptr
. Затем он обновляетnext
указатель последнего элемента, чтобы он указывал на новый элемент. Наконец, он сохраняет новый элемент в своем собственном указателе.Получатель следит за
next
указателем первого элемента, чтобы увидеть, есть ли новые доступные данные. Если это так, он удаляет старый первый элемент, продвигает свой собственный указатель для указания на текущий элемент и начинает его обработку.В этой настройке
next
указатели должны быть атомарными, и отправитель должен быть уверен, что не разыменовывает второй последний элемент после того, как он установил свойnext
указатель. Преимущество, конечно, в том, что отправителю никогда не придется блокировать.Оба подхода намного быстрее, чем любой подход, основанный на блокировках, но для правильной реализации они требуют тщательной реализации. И, конечно же, они требуют собственной аппаратной атомарности записи / загрузки указателя; если ваша
atomic<>
реализация использует внутреннюю блокировку, вы в значительной степени обречены.Аналогичным образом, если у вас есть несколько читателей и / или писателей, вы в значительной степени обречены: вы можете попытаться придумать схему без блокировки, но в лучшем случае ее будет сложно реализовать. С этими ситуациями гораздо легче справиться с помощью замка. Однако, как только вы захватить замок, вы можете перестать беспокоиться о
new
/delete
производительности.источник