Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Быстрый способ передать данные между потоками 
:(
    Опции темы
borisbn
Дата 15.5.2011, 13:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Доброго времени суток !
Есть потребность передавать данные от одного потока другому наиболее быстрым способом.
Идея такая:
Поток, который отдаёт данные (пусть будет DataProducer)
  • выделяет память в "куче" и получает указатель на эту память
  • локирует буфер
  • запихивает указатель в массив
  • разлокирует буфер
Поток, который принимает данные (пусть будет DataReceiver)
  • локирует массив указателей
  • копирует их в свой массив
  • разлокируется
  • складывает последовательно все данные в один выходной буфер
Код написан (приведён ниже), идея работает.
Вопрос: можно ли как-нибудь ускорить этот процесс ?
Спасибо.

Код

#ifndef SPEEDBUFFER_H
#define SPEEDBUFFER_H

#include <vector>
#include <QMutex>
#include <QMutexLocker>

template< class T >
class SpeedBuffer
{
public:
    SpeedBuffer();
    void add( const T * p, unsigned int count ); // вызывается в DataProducer
    bool empty() const; // вызывается в DataReceiver
    const T * take( unsigned int & count ); // вызывается в DataReceiver
private:
    QMutex m_mutex;
typedef std::vector< T > Vect;
typedef std::vector< Vect* > VectVect;
    Vect m_outputBuffer;
    VectVect m_inPtrs;
    VectVect m_outPtrs;
    unsigned int m_size;
};

#endif // SPEEDBUFFER_H

#ifndef SPEEDBUFFER_HPP
#define SPEEDBUFFER_HPP

#include "SpeedBuffer.h"

template< class T >
SpeedBuffer< T >::SpeedBuffer()
    : m_size( 0 )
{
}

template< class T >
void SpeedBuffer< T >::add( const T * p, unsigned int count )
{
    Vect * v = new Vect( p, p + count );
    QMutexLocker locker( &m_mutex );
    m_inPtrs.push_back( v );
    m_size += count;
}

template< class T >
bool SpeedBuffer< T >::empty() const
{
    // Вот ещё вопрос: нужно ли здесь лочить ?
    //QMutexLocker locker( &m_mutex );
    return m_size != 0;
}

template< class T >
const T * SpeedBuffer< T >::take( unsigned int & count )
{
    m_mutex.lock();
    m_outPtrs.assign( m_inPtrs.begin(), m_inPtrs.end() );
    m_inPtrs.clear();
    count = m_size;
    m_size = 0;
    m_mutex.unlock();
    m_outputBuffer.clear();
    for ( typename VectVect::const_iterator it = m_outPtrs.begin(); it != m_outPtrs.end(); ++it )
    {
        const Vect * v = (*it);
        m_outputBuffer.insert( m_outputBuffer.end(), v->begin(), v->end() );
        delete v;
    }
    return &m_outputBuffer[ 0 ];
}

#endif // SPEEDBUFFER_HPP



Это сообщение отредактировал(а) borisbn - 15.5.2011, 13:59


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
boostcoder
Дата 15.5.2011, 14:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(borisbn @  15.5.2011,  13:55 Найти цитируемый пост)
можно ли как-нибудь ускорить этот процесс ?

а что, медленно работает? вроде как нет никаких очевидных тормозов в коде..

PM WWW   Вверх
borisbn
Дата 15.5.2011, 15:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(boostcoder @  15.5.2011,  14:58 Найти цитируемый пост)
а что, медленно работает?

не то чтобы медленно, но поток входных данных очень большой (~4 МБ/сек), плюс к тому таких потоков 48. Остальные места проекта я как-то "вылизал", а за это (немаловажное) переживаю... может есть вариант побыстрее ?

Пробовал перейти к raw-указателям (без STL) - получилось ещё медленнее smile


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
VictorTsaregorodtsev
Дата 15.5.2011, 15:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 274
Регистрация: 28.7.2006

Репутация: 1
Всего: 8



Хм. Зачем лочить указатели? Выделение памяти (через new) давно уже должно быть непроблемным в многопоточной проге (т.е. два одновременно обратившихся за выделением памяти потока не должны получить указатель на одну и ту же область памяти).

Да и разговор о наиболее быстром способе при использовании библиотеки классов не имеет смысла. Создание и последующее неявное автоматическое удаление локальных переменных-экземпляров классов (в Вашем случае имеется в виду строка QMutexLocker locker( &m_mutex ), но при росте размера синхронизирующего кода подобных строк может стать больше) может приводить к большим накладным расходам из-за "тяжелых" конструктора и/или деструктора. Т.е. надо лезть и читать исходники библиотеки классов, чтобы гарантировать отсутствие тормозов по этой причине.
А вот явное использование функций синхронизации потоков и функций межпоточного обмена данными из WinAPI или аналогичных юниксовых примитивов будет гарантированно быстрее (если, конечно, выбрать адекватные задаче низкоуровневые функции и правильно всё реализовать). Нужна многоплатформенная переносимость - так #ifdef'ы и подобные директивы препроцессора никто не отменял.

Ну и ещё по делу - может, подетальнее опишете задачу, чтобы у народа была возможность предложить иной способ реализации (вместо Ваших Продюсера-Ресивера)? Вдруг такая возможность есть?
PM MAIL WWW   Вверх
boostcoder
Дата 15.5.2011, 15:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(borisbn @  15.5.2011,  13:55 Найти цитируемый пост)
// Вот ещё вопрос: нужно ли здесь лочить ?

да. иначе можешь получить среднее состояние.

Цитата(borisbn @  15.5.2011,  15:02 Найти цитируемый пост)
не то чтобы медленно, но поток входных данных очень большой

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

к слову. а что произойдет в твоем коде с потоком потребителем, если данных больше нет?
и еще не понятно следующие моменты:
1. кол-во потоков производителей константно?
2. кол-во потоков потребителей константно?

Добавлено через 3 минуты
Цитата(VictorTsaregorodtsev @  15.5.2011,  15:05 Найти цитируемый пост)
Зачем лочить указатели?

указатели и не лочаться ;)
лочиться участок кода, добавляющий/удаляющий их из/в STL контейнер.

PM WWW   Вверх
borisbn
Дата 15.5.2011, 15:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(VictorTsaregorodtsev @  15.5.2011,  15:05 Найти цитируемый пост)
Зачем лочить указатели?

Я, наверное, неправильно выразился. Я лочу не указатели, а вектор, который их содержит. Вернее операции с этим вектором (push_back в одном потоке, begin, end, clear - в другом).

Цитата(VictorTsaregorodtsev @  15.5.2011,  15:05 Найти цитируемый пост)
 Создание и последующее неявное автоматическое удаление локальных переменных-экземпляров классов (в Вашем случае имеется в виду строка QMutexLocker locker( &m_mutex ), но при росте размера синхронизирующего кода подобных строк может стать больше) может приводить к большим накладным расходам из-за "тяжелых" конструктора и/или деструктора. Т.е. надо лезть и читать исходники библиотеки классов, чтобы гарантировать отсутствие тормозов по этой причине.

Посмотрел QMutexLocker. Как и ожидал - простой до невозможности:
Код

QMutexLocker::QMutexLocker( QMutex * m ) : mutex( m ) {
    mutex->lock();
}
QMutexLocker::~QMutexLocker() : {
    mutex->unlock();
}

а вот QMutex - мдааааа. Пожалуй, и правда, воспользуюсь советом и перейду на низкоуровневые объекты ОС. Спасибо.

Цитата(VictorTsaregorodtsev @  15.5.2011,  15:05 Найти цитируемый пост)
Ну и ещё по делу - может, подетальнее опишете задачу, чтобы у народа была возможность предложить иной способ реализации (вместо Ваших Продюсера-Ресивера)? Вдруг такая возможность есть?

Не проблема. Итак,
имеется 48 устройств, записывающих данные в память PC по DMA (устройств вообще-то 6 по 8 каналов в каждом, но для простоты объяснения пусть будет 48 устройств).
Данные пишутся по кольцу. На каждое устройство создан один поток (я его назвал DataProducer), который получает уведомления о том, что очередной блок данных записан (не всё кольцо, а, допустим, 1/10-я). Уведомления поступают с частотой ~10 мс. Эти данные необходимо обработать, но обработка может занять 1 мс, а может 100, поэтому обрабатывать в этом же потоке нельзя, иначе он может потерять уведомление и данные "перетрутся" новыми. Данные копируются в SpeedBuffer (ф-ция add) и обрабатывающему потоку сообщается, что данные можно забирать.
Для обработки создано ещё 48 потоков (я их назвал DataReceiver), которые ожидают события от своего DataProducer'а, забирают данные (ф-ция take у SpeedBuffer'а) и обрабатывают.

Добавлено через 1 минуту и 27 секунд
boostcoder, походу лочить надо форум, когда набираешь текст smile  smile

Добавлено через 12 минут и 12 секунд
Цитата(boostcoder @  15.5.2011,  15:23 Найти цитируемый пост)

// Вот ещё вопрос: нужно ли здесь лочить ?
да. иначе можешь получить среднее состояние.

вот не уверен. если ты про
Цитата
m_size += count;

то ничего страшного, если за один проход потока-ресивера я получу 0. На следующем будет то, что нужно. А главное, я получаю не сам m_size, а всего лишь равен он нулю или нет.
а если про это
Цитата
m_size = 0;

то это вообще выполняется в том же потоке, где и empty()


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
boostcoder
Дата 15.5.2011, 15:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(borisbn @  15.5.2011,  15:29 Найти цитируемый пост)
я его назвал DataProducer

его все так называют smile

Цитата(borisbn @  15.5.2011,  15:29 Найти цитируемый пост)
ataProducer), который получает уведомления

каким образом это происходит?

Добавлено через 3 минуты и 30 секунд
Цитата(borisbn @  15.5.2011,  15:29 Найти цитируемый пост)
вот не уверен

ну как знаешь..
PM WWW   Вверх
borisbn
Дата 15.5.2011, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(boostcoder @  15.5.2011,  15:45 Найти цитируемый пост)
каким образом это происходит?

по прерыванию от драйвера взводится событие. Он (DataProducer) "висит" на этом собитии.


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
boostcoder
Дата 15.5.2011, 15:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(borisbn @  15.5.2011,  15:48 Найти цитируемый пост)
Он (DataProducer) "висит" на этом собитии

это-то меня и интересовало. каким образом он висит на нем?
PM WWW   Вверх
borisbn
Дата 15.5.2011, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(boostcoder @  15.5.2011,  15:50 Найти цитируемый пост)
каким образом он висит на нем?

WaitForSingleObject


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
boostcoder
Дата 15.5.2011, 15:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(borisbn @  15.5.2011,  15:54 Найти цитируемый пост)
WaitForSingleObject

оно одно для всех 48ми каналов? или на каждый канал свое?
PM WWW   Вверх
borisbn
Дата 15.5.2011, 16:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(boostcoder @  15.5.2011,  15:57 Найти цитируемый пост)
оно одно для всех 48ми каналов? или на каждый канал свое?

Jedem das Seine smile
48 устройств - 48 WaitForSingleObject'ов


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
boostcoder
Дата 15.5.2011, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



Цитата(borisbn @  15.5.2011,  16:01 Найти цитируемый пост)
48 устройств - 48 WaitForSingleObject'ов

ну тогда все можно сильно упростить smile
чуть позже опишу..
PM WWW   Вверх
borisbn
Дата 15.5.2011, 16:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(borisbn @  15.5.2011,  16:01 Найти цитируемый пост)
48 устройств - 48 WaitForSingleObject'ов

на самом деле всё немного по-другому: устройств 6, и Wait'ов тоже шесть. У каждого устройства 8 каналов, которые записываются в один массив. При получении уведомления каждый из шести потоков делает демультиплексирование этих данных на 8 каналов, и последовательно вызывает add у 8-ми обработчиков.


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
xvr
Дата 16.5.2011, 13:58 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Можно слегка ускорить вычитывание данных если сделать накопление данных не в виде массива векторов, а в виде одного массива (как сделано при чтении). А потом при чтении просто делать swap массивов - аккумулятора и выходного массива (выходной перед этим понулить - позвать clear() )

PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0629 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.