Название: Два потока работают с QQueue Отправлено: Yegor от Октябрь 20, 2014, 16:54 Здравствуйте!
Работаю на Qt4.8.6 У меня есть два параллельных потока. Стоит задача - передавать большое количество данных за единицу времени от одного потока к другому. Данные разбиты на блоки. Одна такой блок - это структура. В структуре - байты. Для этого можно использовать механизм обмена событиями между потоками. Но мне кажется, что это медленное решение, так как сюда привлекаются отправка события, очереди события, прием из очереди и т.д. Я думаю использовать вариант побыстрее. Сделать промежуточный буфер между этими потоками - QQueue. Один буфер общий на два потока. То есть один поток добавляет данные к очереди, а второй извлекает. Подскажите, пожалуйста, какие могут быть подводные камни в таком решении. В доке написано, что QQueue - reentrant. То есть очередь защищена от проблем при использовании многопоточности. Но все, же, что еще нужно учитывать? Нужно ли делать синхронизацию между потоками? Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 20, 2014, 17:04 Нужно ли делать синхронизацию между потоками? Для QQueue нужно обязательно.В доке написано, что QQueue - reentrant. То есть очередь защищена от проблем при использовании многопоточности. Нет, это значит, что методы можно повторно вызывать.Название: Re: Два потока работают с QQueue Отправлено: Yegor от Октябрь 20, 2014, 19:25 А как синхронизировать? Приведите, пожалуйста, кусочки кода. Или сслыку.
Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 20, 2014, 19:35 А как синхронизировать? Приведите, пожалуйста, кусочки кода. Или сслыку. Поиск же по форуму... :)http://www.prog.org.ru/index.php?topic=14426.msg95463#msg95463 Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 20, 2014, 20:25 Для этого можно использовать механизм обмена событиями между потоками. Но мне кажется, что это медленное решение, Все-таки стандартные средства (напр контейнер защищенный QReadWriteLock) позволяют сэкономить немало времени. Может стоит сначала проверить "насколько же они медленны"так как сюда привлекаются отправка события, очереди события, прием из очереди и т.д. Я думаю использовать вариант побыстрее. Стоит задача - передавать большое количество данных за единицу времени от одного потока к другому. Тогда почему не lock-free? (т.е. без всяких контейнеров и мутексов). Данные разбиты на блоки. Одна такой блок - это структура. В структуре - байты. Код Впрочем синхронизация все равно нужна. А главное - это капитально "выносит моск", напр я совсем не уверен что написал правильно. Ну если конечно Вы не боитесь трудностей... :) Название: Re: Два потока работают с QQueue Отправлено: __Heaven__ от Октябрь 20, 2014, 21:53 Как-то вариант, предложенный Old проще для восприятия. Тоже недавно интересовался подобным вопросом.
Не совсем разобрался в эффективности применения QAtomic Название: Re: Два потока работают с QQueue Отправлено: vulko от Октябрь 21, 2014, 08:38 Никакие атомарные операции тут не нужны. В 99.99% случаев вполне достаточно обычного мьютекса или крит. секции.
Передавать данные между потоками ненужно, лучше данные пошарить. Создайте разделяемый ресурс. Например пусть это будет класс. Код: class ShareData { Когда один поток обращается к данным, например для записи, обрамляете этот код вызовами shareData->lock(); // синхронизируемый код shareData->unlock(); Когда другой поток обращается к данным, например для чтения, также обрамляете код вызовами shareData->lock(); // синхронизируемый код shareData->unlock(); Если данных много и они должны обрабатываться постоянно, то можно сделать контейнер для хранения общих данных. Например вектор, а не char*, как в примере выше. Тогда если !vector.isEmpty(), значит можно данные забирать и обрабатывать. Если нет, то ничего делать получающему потоку не нужно. А передающий поток будет просто делать vector.push_back(item); Название: Re: Два потока работают с QQueue Отправлено: __Heaven__ от Октябрь 21, 2014, 08:49 Никакие атомарные операции тут не нужны. В 99.99% случаев вполне достаточно обычного мьютекса или крит. секции. А в каких случаях целесообразно применять атомарные операции?Название: Re: Два потока работают с QQueue Отправлено: vulko от Октябрь 21, 2014, 09:20 Никакие атомарные операции тут не нужны. В 99.99% случаев вполне достаточно обычного мьютекса или крит. секции. А в каких случаях целесообразно применять атомарные операции?Когда хотите изобрести свою синхронизацию. lock/unlock мьютекса это как раз атомарные операции. Название: Re: Два потока работают с QQueue Отправлено: OKTA от Октябрь 21, 2014, 09:37 А можно QSemaphore c фабрикой попробовать. разве нет?
Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 21, 2014, 09:48 А в каких случаях целесообразно применять атомарные операции? В общем случае когда время выполнения "того что надо сделать под локом" достаточно мало. Напр то же добавление/извлечение из контейнера. Если такие операции интенсивны, то прямолинейное использование "честного" мутекса (который останавливает нитку) будет печально по скорости. Можно использовать нечестный (unfair), это простоКод
А как синхронизировать? Приведите, пожалуйста, кусочки кода. Или сслыку. Возможно и "никак", тупо ждем пока откроется мутекс. В этом есть смысл если по задаче мы знаем что время ожидания достаточно мало (ориентир < 0.3 sec)Название: Re: Два потока работают с QQueue Отправлено: OKTA от Октябрь 21, 2014, 09:58 Интересная статья по поводу QAtomic http://woboq.com/blog/introduction-to-lockfree-programming.html
Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 21, 2014, 10:41 Интересная статья по поводу QAtomic http://woboq.com/blog/introduction-to-lockfree-programming.html Да что ж он, гад, делает! Дает код, показывает что в 10 раз быстрее, а потом (так, между прочим, в конце статьи) говорит что там баг, и как фиксить хз. Он же меня запутал!!! :)Да, если интересно почему баг (там не очень ясно почему) - я поясню, это заморочка капитальная. А в общем ну его нафиг, тот lock-free, достигнутая чудесная скорость не окупает мозговых затрат на нее. Название: Re: Два потока работают с QQueue Отправлено: OKTA от Октябрь 21, 2014, 10:58 Зато повод узнать о существовании QAtomic и его возможностях :)
Название: Re: Два потока работают с QQueue Отправлено: vulko от Октябрь 21, 2014, 11:44 А в каких случаях целесообразно применять атомарные операции? В общем случае когда время выполнения "того что надо сделать под локом" достаточно мало. Напр то же добавление/извлечение из контейнера. Если такие операции интенсивны, то прямолинейное использование "честного" мутекса (который останавливает нитку) будет печально по скорости. Можно использовать нечестный (unfair), это простоНе путай людей своей бредятиной. lock() и unlock() это атомарные операции. Применение или не применение их никак не связано с временем выполнения кода, который между ними. Целесообразность определяется не тем сколько времени выполняется код под локом, важна архитектура и потребности. Прямолинейное использование это что такое вообще? О каких честных/нечестных мьютексах вообще речь, когда ты не понимаешь как они работают даже. Обрамлять локом/анлоком мьютекса операции вставки/удаления в контейнер крайне глупо. Во-первых, для таких случаев используется критическая секция, т.к. она работает куда быстрее. Во-вторых, удаление обрамлять локом анлок вообще очень глупо, особенно если нужно пробежаться по контейнеру и поудалять элементы... или удалить все по очереди. Название: Re: Два потока работают с QQueue Отправлено: Yegor от Октябрь 21, 2014, 16:37 А что, если сделать просто:
Код
Нагрузка на методы вставки / извлечения - сотни за секунду. Так будет правильно? Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 21, 2014, 16:54 Нагрузка на методы вставки / извлечения - сотни за секунду. Так будет правильно? Правильно - да, быстро - не очень. Edit: не совсем правильно :) QQueue::dequeue() полагает что контейнер не пустой, надо добавить проверку Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 21, 2014, 17:01 А что, если сделать просто: А как вы видите организацию поллинга для элементов вашей очереди? Как нить получатель будет определять, что в очереди появились данные?Название: Re: Два потока работают с QQueue Отправлено: Yegor от Октябрь 21, 2014, 20:51 Думаю поллинг лучше организовать так:
1. Собрался лимит количества элементов - высылаю сигнал о готовности сразу. Это когда большой поток данных. 2. Если лимит еще не собрался, но что то есть, то высылаю сигнал о готовности по таймауту. Это при непостоянном / медленном обмене данных. Где сигнал о готовности - уведомление о наличии данных в очереди. Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 21, 2014, 21:12 Думаю поллинг лучше организовать так: Накидайте это в коде (псевдокоде) и сравним с тем решением, ссылку на которое я вам привел.Там такой сигнал как раз и посылается специальным объектом синхронизации QWaitCondition. Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 22, 2014, 09:50 Накидайте это в коде (псевдокоде) и сравним с тем решением, ссылку на которое я вам привел. Тут может создаться впечатление что это какое-то "особое" решение. В действительности схема с Там такой сигнал как раз и посылается специальным объектом синхронизации QWaitCondition. (Q)WaitCondition всегда одна и та же, другой просто нет. Конечно можно ее переписать, и она будет работать. А можно еще проще - сделать сигнал с QueuedConnection, и Qt за нас все сделает. Проблема только одна - при достаточно интенсивном обмене скорость будет падать. При сотнях обменов в секунду надо работать синхронно, т.е. тупо крутиться в while проверяя размер контейнера. Да, мы задействуем процессор/ядра вхолостую, но мы уверены что это ненадолго, данные скоро поступят. Код
Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 22, 2014, 09:53 А можно еще проще - сделать сигнал с QueuedConnection, и Qt за нас все сделает. Проще - возможно (как сказал бы Верес "сделайте тупо" :) ), а будет ли это решение сделанное Qt за нас - быстрее? Да и проще тоже сомнительно? :)Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 22, 2014, 09:58 как сказал бы Верес "сделайте тупо" :) Вы бы не будили лиха - пока оно тихо. А то попрет "фонтан мысли" :'( Название: Re: Два потока работают с QQueue Отправлено: xokc от Октябрь 23, 2014, 08:42 Проблема только одна - при достаточно интенсивном обмене скорость будет падать. При сотнях обменов в секунду надо работать синхронно, т.е. тупо крутиться в while проверяя размер контейнера. Да, мы задействуем процессор/ядра вхолостую, но мы уверены что это ненадолго, данные скоро поступят. А есть какие-нибудь объективные метрики относительно того, начиная с какого темпа наполнения очереди имеет смысл не использовать штатные для Qt механизмы и начинать городить велосипедный огород? В моём понимании "сотни обменов в секунду" - это вообще ни о чем для современного железа и тратить ресурсы ядра процессора (при таких скоростях поступления элементов) на постоянные проверки "непустоты" очереди - непозволительная роскошь. Вопрос не праздный - сам маюсь с сервером на Qt с похожей архитектурой. Пока пользуюсь сигнал-слотами, но переживаю по поводу масштабируемости текущего решения.Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 23, 2014, 10:52 А есть какие-нибудь объективные метрики относительно того, начиная с какого темпа наполнения очереди имеет смысл не использовать штатные для Qt механизмы .. Величина 0.3 секунды - это default параметр в OpenMP, типа "время прокрутки". Нитка усыпляется и перестает грузить проц по истечении этого интервала. Время совсем не маленькое ..и начинать городить велосипедный огород? А что тут "велосипедного"? QAtomicInt, QWaitCondition и др. - штатные средства синхронизации. Нет готовых классов поюзать - ну что-то нужно и самому написать (чтоб совсем не облениться :))Предлагаю проверить на тестах, я возьму на себя реализацию с atomic Название: Re: Два потока работают с QQueue Отправлено: vulko от Октябрь 24, 2014, 13:18 А есть какие-нибудь объективные метрики относительно того, начиная с какого темпа наполнения очереди имеет смысл не использовать штатные для Qt механизмы .. Величина 0.3 секунды - это default параметр в OpenMP, типа "время прокрутки". Нитка усыпляется и перестает грузить проц по истечении этого интервала. Время совсем не маленькое ..и начинать городить велосипедный огород? А что тут "велосипедного"? QAtomicInt, QWaitCondition и др. - штатные средства синхронизации. Нет готовых классов поюзать - ну что-то нужно и самому написать (чтоб совсем не облениться :))Предлагаю проверить на тестах, я возьму на себя реализацию с atomic Какое OpenMP? Какие 0.3 секунды?! Никаких классов писать не нужно, все уже давно написано. В 99.9999999999% случаев существующих средств синхронизации более чем достаточно. Есть std, хотя мне не очень оно нравится, есть Qt'шные обертки, есть boost в конце концов. Мьютексы, разные локи, wait-notify средства синхронизации и межпоточного взаимодействия. Нужно просто понимать принцип их работы и уметь ими пользоваться. Название: Re: Два потока работают с QQueue Отправлено: xokc от Октябрь 24, 2014, 19:02 А что тут "велосипедного"? QAtomicInt, QWaitCondition и др. - штатные средства синхронизации. Нет готовых классов поюзать - ну что-то нужно и самому написать (чтоб совсем не облениться :)) Для меня штатной в Qt является система сигнал/слот. Всё остальное и есть велосипедостроение. Впрочем, это вопрос терминологии.Предлагаю проверить на тестах, я возьму на себя реализацию с atomic Проверить-то можно, нужно только договориться о входных параметрах теста (например, темп наполнения очереди, количество потоков-писателей и потоков-читателей), корректных (в смысле одинаковоинтерфейсных для простоты взаимозаменяемости) способах реализации очередей различного типа и, наконец, о том что именно будем измерять. Кроме того, хотелось мерять не "сферического коня", а использовать время "простоя" читателя для выполнения какой-нибудь "полезной" нагрузки. В 99.9999999999% случаев существующих средств синхронизации более чем достаточно. Вы топик вообще читали? В разработке каких ещё "несуществующих" средств синхронизации Вы нас тут пытаетесь обвинить? Мы всего лишь стараемся понять насколько те самые "существующие" средства синхронизации хороши для выполнения совершенно конкретной задачи. А также понять как определить, что твой проект не входит в те самые 99.9999999999% (это число ведь не с потолка взято, за него-то Вы можете ответить?)?Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 24, 2014, 19:16 2xokc Вы бы подробные рассказали про архитектуру вашего сервера. И откуда берётся боспокойство о масштабируемости.
Название: Re: Два потока работают с QQueue Отправлено: xokc от Октябрь 24, 2014, 21:28 Ой, да долго рассказывать. Попробую.
По инфинибенду 10 раз в секунду блоками по 288*54 кБайт независимо друг от друга поступают в отдельных потоках данные с суммарной интенсивностью до 1 ГБайта в сек. Дальше с помощью CUDA оно асинхронно сплитится на 54*4 блока и по кольцу записывается на рейд из 8 SSD дисков, а так же кешируется в кольцевом буфере в оперативке. В этой части Qt практически не используется. Дальше появляются пулы потоков с ридерами (до 1024 шт.), которые читают эти данные с разной скоростью в зависимости от нагрузки на сервер (от 10 р/с до 80 р/с). Дальше эти ридеры производят над считанными данными разные преобразования, результаты которых оправляются в сетку. Так вот кусок с ридерами сделан на Qt и сейчас взаимодействие между пулами потоков ридеров и сетевой подсистемой производится через сигнал-слот. Сейчас оно крутится на 2-х процессорном Windows сервере на базе 12-ти ядерных Xeon. И периодически (как обычно, труднопрогнозируемо) "впадает в кому" в момент отправки сигнала с QByteArray. Не факт, что это связано с узким горлом в Qt сигналах - система только что собрана полностью и до этого места руки ещё не дошли. Проблема также в том, что параллельно всему этому в отдельном процессе работает своя достаточно ресурсоемкая задача (там и CUDA и OpenMP), которая тоже не прибавляет здоровья системе. В итоге "в среднем по больнице" при 256 ридерах вижу общую загрузку процессоров на уровне 30-40%, что пока устраивает. Беспокоит то, что в перспективе ожидается кратное увеличение количества пишущих потоков (причем, возможно, существенное - правда при сохранении общей интенсивности потока данных за счет уменьшения количества блоков). Ну и ридеров может стать побольше. Поэтому и переживания о том, не упремся ли мы потом в производительность сигнал/слотов. Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 24, 2014, 21:45 Я правильно понял, что потоки ридеры после обработки данных, передают их потоку сендеру, который и отправляет их в сеть? И для этой передачи используются сигналы?
Название: Re: Два потока работают с QQueue Отправлено: xokc от Октябрь 24, 2014, 22:10 В целом - да. Но сетевой поток в общем случае тоже не один - там пул потоков (клиентов может быть до 256 шт).
Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 24, 2014, 22:32 В целом - да. Но сетевой поток в общем случае тоже не один - там пул потоков (клиентов может быть до 256 шт). Посылка сигнала между потоками сводится к помещению события в Qt-шную очередь сообщений. Вам без очереди все равно не обойтись, поэтому пока не вижу серьезных причин отказываться от Qt-шной. В будущем, при необходимости, вы всегда сможете сделать свою специальную очередь для этого. Но без дополнительных тестов, показывающих что тормозит именно она, я бы пока не дергался.А вот как раскидываются задачи на отправку между потоками сендерами? Или у вас по потоку на клиента и всем передаются одинаковые данные? И как вы определили, что "в кому" впадает именно при отправке сигнала? /* И выбор Qt для решения этой задачи удивляет. Не очень понятно для чего он здесь. Ну это риторический вопрос. :) */ Название: Re: Два потока работают с QQueue Отправлено: xokc от Октябрь 25, 2014, 11:08 Посылка сигнала между потоками сводится к помещению события в Qt-шную очередь сообщений. Вам без очереди все равно не обойтись, поэтому пока не вижу серьезных причин отказываться от Qt-шной. В будущем, при необходимости, вы всегда сможете сделать свою специальную очередь для этого. Но без дополнительных тестов, показывающих что тормозит именно она, я бы пока не дергался. С этим-то всё ясно, об этом-то сейчас и думаю. В том числе и в этом топике :)А вот как раскидываются задачи на отправку между потоками сендерами? Или у вас по потоку на клиента и всем передаются одинаковые Нет, на клиента не по потоку. Сетевые клиенты собраны в пулы, каждый пул обрабатывает один поток. Количество таких пулов пока задается вручную при старте сервера. Трафик, постоянно и равномерно отправляемый на клиента, составляет: в худшем случае 1 МБ/с, в среднем - 256 кБ/с. данные? И как вы определили, что "в кому" впадает именно при отправке сигнала? Периодически удаётся "попасть" на это место при запуске сервера из-под отладчика. Последнее разумное место в стеке до AV это как раз вызов сигнала. Но, повторюсь, вопрос до конца не исследован, может это не причина, а следствие./* И выбор Qt для решения этой задачи удивляет. Не очень понятно для чего он здесь. Ну это риторический вопрос. :) */ Не менее риторический ответ - по историческим причинам :). Фоново уже думаю о том, чтобы уйти в что-нибудь менее тяжёлое типа libuv или asio. Может ещё ZeroMQ. Но, там много всяких плюшек от Qt используется типа соединения с БД, логирование, сеть, QSettings, плагины, QSingleApplication и т.п. Не хотелось бы всё это заменять на набор разнородных костылей. В любом случае, делать это только ради того, чтобы гордо сказать, что у "меня на сервере нет Qt" не будем :). А вот "со своей специальной очередью" - думаем. Название: Re: Два потока работают с QQueue Отправлено: Igors от Октябрь 25, 2014, 14:45 Развил здесь (http://www.prog.org.ru/index.php?topic=27837.msg202663#msg202663)
Мда, запас прочности Qt намного больше чем я предполагал :) Название: Re: Два потока работают с QQueue Отправлено: Old от Октябрь 25, 2014, 15:37 Нет, на клиента не по потоку. Сетевые клиенты собраны в пулы, каждый пул обрабатывает один поток. Количество таких пулов пока задается вручную при старте сервера. Трафик, постоянно и равномерно отправляемый на клиента, составляет: в худшем случае 1 МБ/с, в среднем - 256 кБ/с. Все равно не очень понятно. Все клиенты получают одинаковые данные. А отправкой данных занимается одна нить на несколько клиентов?Название: Re: Два потока работают с QQueue Отправлено: xokc от Октябрь 25, 2014, 16:15 Все клиенты получают разные данные. Вернее могут получать и одинаковые, но это частный случай. Ридеры читают разные места из кольцевого буфера - клиентам уходят разные данные. И - да, отправкой данных занимается одна нить на несколько клиентов. Считайте, что это большой архив системы видеонаблюдения. Клиент "смотрит" тот участок видео, какой ему понадобится, и с той скоростью, какую может позволить себе сервер или с какой может "справиться" сам клиент.
Название: Re: Два потока работают с QQueue Отправлено: xokc от Ноябрь 29, 2014, 23:14 И как вы определили, что "в кому" впадает именно при отправке сигнала? Кстати, таки определили из-за чего оно валится. У нас в приложении в каждом из потоков-читателей открыт один и тот-же файл, из которого и производится весьма интенсивное чтение блоками по 188 кБайт через QFile::read. Так вот, если QFile открыт без опции QIODevice::Unbuffered, то на уровне Qt производится кеширование, снимающее проблему низкой производительности при чтении из файла мелким порциями. И в нашем случае оно работает криво, т.е. приводит к регулярному AV. Так что проблема решилась добавлением к флагам при открытии файла QIODevice::Unbuffered. Название: Re: Два потока работают с QQueue Отправлено: Old от Ноябрь 29, 2014, 23:19 Так а что значит: в вашем случае работало кривой?
В чем кривизна? Название: Re: Два потока работают с QQueue Отправлено: xokc от Ноябрь 30, 2014, 00:09 Вываливалось в access violation внутри QFile::read. Подебажил исходники - внутри read если не включен QIODevice::Unbuffered производится кеширование через внутренний QByteArray. Дальше разбираться не стал - добавил QIODevice::Unbuffered в QFile::open и всё заработало нормально. В нашей задаче даже кеш ОС отключить стоило-бы, так что лишние "оптимизации" тут со стороны Qt нам ни к чему.
|