linux высокопроизводительная передача сообщений между потоками в c ++ - PullRequest
1 голос
/ 20 декабря 2011

скажем, у меня есть процесс, который порождает 2 потока

первый поток прослушивает в узком цикле события пакетов на порте UDP. второй поток должен получить байты, содержащиеся в этом UDP-пакете, и проанализировать его / сделать вещи.

Кто-нибудь может порекомендовать более быстрый метод, чем использование очереди сообщений linux? я думаю, что они медленные, потому что они копируют байты при записи в очередь и копируют их снова при чтении из очереди

Я знаю о библиотеке zeromq, но есть ли удобный способ сделать это без этих накладных расходов? я понимаю, что мог бы просто использовать tcp / ip-сокет между двумя потоками для простой связи в очереди, но есть ли более быстрый способ?

Я думаю, что может быть кольцевой буфер в памяти, который разделяется между потоками и мьютексом, используемым для управления указателем на последний обновленный элемент?

Кто-нибудь мысли здесь?

Ответы [ 4 ]

5 голосов
/ 20 декабря 2011

Самый эффективный метод, который я могу придумать, - это использовать один связанный список, один мьютекс и одну условную переменную:

Thread A:
   1. Allocate a udp-packet-buffer object (the object should contain 'previous' and 'next' pointers, and an array of bytes to store UDP data in)
   2. recv() UDP data into the object's byte-array
   3. lock the mutex
   4. append the udp-packet-buffer object to tail of the linked-list
   5. unlock the mutex
   6. signal the condition variable
   7. goto 1

Thread B:
   1. wait on the condition variable (until the condition variable is signaled)
   2. lock the mutex
   3. pop the next udp-packet-buffer off the head of the linked-list
   4. unlock the mutex
   5. parse/handle the UDP data in the udp-packet-buffer object
   6. delete the udp-packet-buffer object
   7. goto 1

Это дает вам механизм связи с нулевой копией.Время, затрачиваемое на удержание мьютекса, минимально, поэтому конкуренция также должна быть низкой.

Если вы хотите еще немного оптимизировать процесс, вы можете сделать так, чтобы поток B вместо этого сразу отбирал все элементы из связанного списка.просто выталкивать по одному - то, что можно сделать за O (1) раз со связанным списком.И затем иметь поток A, сигнализирующий переменную условия, только если связанный список был пуст перед тем, как добавить свой последний объект udp-packet-buffer.Это уменьшит количество блокировок / разблокировок / сигналов потоков при большой нагрузке.

1 голос
/ 30 июня 2015

Этот очень хороший ответ был тем, что я ожидал, когда зашел на эту страницу https://stackoverflow.com/a/8567548/2893944

Небольшое замечание, которое может помочь: избавление от динамического выделения памяти - это также точка, которая может повысить производительность и избежать фрагментации памяти (например: использовать статический массив, использовать пул памяти).

(это должен был быть комментарий, но недостаточно репутации для этого)

0 голосов
/ 20 декабря 2011

Для максимальной производительности вы хотите фиксированный размер, блокировку, очередь с одним записывающим устройством, очередь с одним читателем.

Эта статья о проекте кода , появившаяся в быстром гугле, кажется разумной и объясняет некоторые входы и выходы.

Я понятия не имею, каков ваш вариант использования - но если предпринята такая длительная мера - вашему программному обеспечению (и среде) необходимо подумать, как справиться с ситуацией, когда полная очередь, вызванная медленным потребителем, вызоветdataloss.

0 голосов
/ 20 декабря 2011

checkout http://www.boost.org/libs/circular_buffer/ Я использую это заполнение boost :: array <1500> в качестве точки связи между двумя потоками в одном проекте, и я не вижу, что это медленно в любом случае.где первый поток принимает пакеты UDP и быстро проверяет некоторые биты, должен ли он поместить его в буфер.он копирует и выводит, но даже не показывает, когда я профилирую.

РЕДАКТИРОВАТЬ: пример BoundedBuffer - интересный бит http://www.boost.org/libs/circular_buffer/doc/circular_buffer.html#boundedbuffer

...