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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Проблемы с QMutex  (Прочитано 14256 раз)
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   Вверх
  Печать  
 
Перейти в:  


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