Russian Qt Forum
Ноябрь 27, 2024, 08:41 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Проблемы с QMutex  (Прочитано 13848 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Ноябрь 04, 2009, 04:38 »

Здравствуйте

Приаттачен простой пример 100 строк. Запускается NUM_THREAD ниток, они делают какую-то работу и завершаются. Все работает "как доктор прописал": больше ниток - быстрее считаем (при наличии процессоров). Проблема: скорость резко падает (во несколько раз) если я вставляю в расчет блокировку QMutexLocker 

Все то же самое в нативном коде (без Qt). Возможно это возникает только на моей платформе, или я что-то упустил. Если несложно проверить - буду благодарен. Порядок тестирования:

- запустить как есть, через несколько секунд на консоли напечатается время
- поставить #define CRAZY_TEST  1 и опять запустить. У меня время намного больше. На 4 нитках трудно дождаться
- если железо слабенькое можно уменьшить NUM_CALC

Спасибо
Записан
spectre71
Гость
« Ответ #1 : Ноябрь 04, 2009, 08:07 »

Igors, если делаешь тест, то делай компилябильный проект.
=====

1)

Код
C++ (Qt)
void MyThread::run( void )
{
float f;
while (true) {
QMutexLocker blk(&mutexJob);
if (!mList->size()) return;
f = mList->last();
mList->pop_back();
++mNumCalc;
blk.unlock();
 
Calc(f, NUM_CALC);
}
}
 

В данном случае лучше не использовать QMutexLocker ! Каждую итерацию создается и уничтожается объект QMutexLocker, что накладно!
Лучше так

Код
C++ (Qt)
void MyThread::run( void )
{
float f;
while (true) {
mutexJob.lock();
if (!mList->size()) {
mutexJob.unlock();
return;
}
f = mList->last();
mList->pop_back();
mutexJob.unlock();
 
++mNumCalc;
Calc(f, NUM_CALC);
}
}
 

2)

Код
C++ (Qt)
void Calc( float f, int num )
{
float sum = 0.0f;
for (int i = 0; i < num; ++i) {
sum += log(atan2(f, f * 0.34));
sum += cos(f) * sin(f);
 
#if CRAZY_TEST
QMutexLocker test(&mutexTmp);
++sum;
#endif
}
}

- Опять же, каждую итерацию цикла создается и уничтожается объект QMutexLocker, что очень очень накладно (здесь твои тормоза)
- Не вижу ни какого смысла в QMutexLocker test(&mutexTmp); в данном случае
- И вообще, данная функция ничего не возвращает

Могу предположить что это написано только ради теста.
Теперь советы по оптимизации:

- Не используй QMutexLocker для часто вызываемого участка кода, используй непосредственно QMutex (lock/unlock), поскольку создание и уничтожение объекта дорго.
- Желательно выносить(не блокировать) участки которые этого не требуют, например: ++mNumCalc;
- Во определенных случаях, если есть глобалы типа int, то для работы с ними не обязателено использовать QMutex. Можно использовать атомарные операции. Пример:

Предположим что int sumVal глобально накапливает некоторую сумму по всем потокам:

Код
C++ (Qt)
QAtomicInt sumVal = 0;
void Calc( float f, int num )
{
float sum = 0.0f;
int addVal;
for (int i = 0; i < num; ++i) {
sum += log(atan2(f, f * 0.34));
sum += cos(f) * sin(f);
++sum;
 
addVal = (int)log(atan2(f, f * 0.34));
sumVal.fetchAndAddAcquire(addVal);
}
}  

Это гораздо дешевле чем:

Код
C++ (Qt)
int sumVal = 0;
void Calc( float f, int num )
{
float sum = 0.0f;
int addVal;
for (int i = 0; i < num; ++i) {
sum += log(atan2(f, f * 0.34));
sum += cos(f) * sin(f);
++sum;
 
addVal = (int)log(atan2(f, f * 0.34));
mutexJob.lock();
sumVal += addVal;
mutexJob.unlock();
}
}

И кстати из QAtomicInt sumVal; читать всегда безопасно.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Ноябрь 04, 2009, 13:24 »

Здравствуйте, Spectre

1)
Igors, если делаешь тест, то делай компилябильный проект.
Что-то не так с файлом, приходится возиться чтобы откомпилить? Скажите что - исправлюсь. Разумеется, все вычисления не имеют никакого смысла - незачем здесь вмешивать большую предметную часть.

2) Возникают ли у Вас тормоза и на какой платформе? Судя по Вашему ответу да, но лучше спросить  Улыбающийся

3) Обертка QMutexLocker ест немного, но все равно уберем для чистоты. Вообще специфика Qt здесь ни при чем,  та же ситуация с родным pmutex_thread_lock

4) Использование QAtomicInt мне не подходит (см. топик "GetPixel thread-safe"  в этом же разделе).

5) Мое мнение: блокировка мутексом (в Qt и OS) может быть медленной ЕСЛИ 2 или более ниток ждут. В примере ниже можно регулировать частоту блокировок макросом NUM_PORTION. Дело не столько в том что становится меньше блокировок, а в том что нитки меньше "пересекаются". Это же подтверждает тест с 1 ниткой: да, мутекс что-то съел но вполне в рамках приличий.

Другие мнения?
« Последнее редактирование: Ноябрь 04, 2009, 13:26 от Igors » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #3 : Ноябрь 04, 2009, 13:40 »

А если попробовать QReadWriteLock и т.п.?
Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Ноябрь 04, 2009, 14:28 »

А если попробовать QReadWriteLock и т.п.?
Твк все "и т.п." сделано на тех же мутексах  Улыбающийся  А также мне надо лочить не только обращения к swap файлу, но и не допускать работы с данными которые могут быть выгружены. Не вижу др. кандидата кроме мутекса
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #5 : Ноябрь 04, 2009, 14:32 »

А QSharedPointer ?
Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Ноябрь 04, 2009, 16:00 »

А QSharedPointer ?
Открываем исходник QSharedPointer.cpp ....
Хммм.... все пестрит вызовами QMutexLocker lock(&kd->mutex);   Улыбающийся
Записан
SASA
Гость
« Ответ #7 : Ноябрь 04, 2009, 19:57 »

2 Igors. Мутексы - это дорого, полюбому Подмигивающий
2 Spectre. А как работает QAtomicInt? За счёт чего достигается атомарность?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Ноябрь 04, 2009, 20:07 »

2 Igors. Мутексы - это дорого, полюбому Подмигивающий
Очень элегантный ответ Улыбающийся  Если нетрудно, укажите теоретический источник. А если есть собственный опыт (любой) - расскажите обязательно
Записан
shadone
Гость
« Ответ #9 : Ноябрь 04, 2009, 21:13 »

2 Igors. Мутексы - это дорого, полюбому Подмигивающий
Очень элегантный ответ Улыбающийся  Если нетрудно, укажите теоретический источник. А если есть собственный опыт (любой) - расскажите обязательно

в двух словах потому что требует синхронизации кэша между процессорами.

http://en.wikipedia.org/wiki/Mutual_exclusion
http://en.wikipedia.org/wiki/Critical_section
http://en.wikipedia.org/wiki/Memory_barrier
http://en.wikipedia.org/wiki/ABA_problem
« Последнее редактирование: Ноябрь 04, 2009, 21:19 от ddenis » Записан
shadone
Гость
« Ответ #10 : Ноябрь 04, 2009, 21:15 »

- Опять же, каждую итерацию цикла создается и уничтожается объект QMutexLocker, что очень очень накладно (здесь твои тормоза)
все правильно. за одним дополнением - стоимостью создания/удаления объекта QMutexLocker можно пренебречь - это на порядки дешевле чем сама операция блокировки мьютекса.


еще одно дополнение - чтение int без блокировки безопасная операция, если вас не пугают проблему кэширования данных.
int val = 0; // global value accessible by several threads
...
if (val == 0) {
  int foo = val; // might be non-0
}
« Последнее редактирование: Ноябрь 04, 2009, 21:18 от ddenis » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Ноябрь 04, 2009, 21:50 »

Здравствуйте, Денис

Спасибо за подборочку по теории.

стоимостью создания/удаления объекта QMutexLocker можно пренебречь - это на порядки дешевле чем сама операция блокировки мьютекса.
Насчет стоимость QMutexLocker полностью согласен. Насчет стоимости блокировки - не так просто/очевидно. Пример: простой цикл из 1 миллиона несложных вычислений. Вставьте в тело цикла QMutexLocker - замедления не наблюдается.

еще одно дополнение - чтение int без блокировки безопасная операция, если вас не пугают проблему кэширования данных.
Опасная. Дело совсем не в том что "переменная может храниться в регистре" - проблемы будут и без этого

Код:
int val = 0; // global value accessible by several threads
...
if (val == 0) {
                      <- here other thread(s) could change val
  int foo = val; // might be non-0
}
Флаги и.т.п. могут использоваться, например, для завершения нитки - но не более.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #12 : Ноябрь 05, 2009, 08:33 »

а это если прикрутить?

http://www.linux.org.ru/view-message.jsp?msgid=4199760&lastmod=1257363282391

Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
spectre71
Гость
« Ответ #13 : Ноябрь 05, 2009, 10:37 »

2 Spectre. А как работает QAtomicInt? За счёт чего достигается атомарность?
Как конкретно реализован QAtomicInt в QT не смотрел.
Но по идее  должны использоваться нативные вызовы конкретной операционки. Например для Windows - Interlocked функции.
С другой стороны атомарные операции должны поддерживаться на уровне определенных команд процессоров.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Ноябрь 05, 2009, 12:34 »

Хмм... пригодится или нет - не знаю. Но интересно, спасибо за наводку
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.134 секунд. Запросов: 23.