Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Ноябрь 04, 2009, 04:38



Название: Проблемы с QMutex
Отправлено: Igors от Ноябрь 04, 2009, 04:38
Здравствуйте

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

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

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

Спасибо


Название: Re: Проблемы с QMutex
Отправлено: spectre71 от Ноябрь 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; читать всегда безопасно.


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 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 ниткой: да, мутекс что-то съел но вполне в рамках приличий.

Другие мнения?


Название: Re: Проблемы с QMutex
Отправлено: kuzulis от Ноябрь 04, 2009, 13:40
А если попробовать QReadWriteLock и т.п.?


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 04, 2009, 14:28
А если попробовать QReadWriteLock и т.п.?
Твк все "и т.п." сделано на тех же мутексах  :)  А также мне надо лочить не только обращения к swap файлу, но и не допускать работы с данными которые могут быть выгружены. Не вижу др. кандидата кроме мутекса


Название: Re: Проблемы с QMutex
Отправлено: kuzulis от Ноябрь 04, 2009, 14:32
А QSharedPointer ?


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 04, 2009, 16:00
А QSharedPointer ?
Открываем исходник QSharedPointer.cpp ....
Хммм.... все пестрит вызовами QMutexLocker lock(&kd->mutex);   :)


Название: Re: Проблемы с QMutex
Отправлено: SASA от Ноябрь 04, 2009, 19:57
2 Igors. Мутексы - это дорого, полюбому ;)
2 Spectre. А как работает QAtomicInt? За счёт чего достигается атомарность?


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 04, 2009, 20:07
2 Igors. Мутексы - это дорого, полюбому ;)
Очень элегантный ответ :)  Если нетрудно, укажите теоретический источник. А если есть собственный опыт (любой) - расскажите обязательно


Название: Re: Проблемы с QMutex
Отправлено: shadone от Ноябрь 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


Название: Re: Проблемы с QMutex
Отправлено: shadone от Ноябрь 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
}


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 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
}
Флаги и.т.п. могут использоваться, например, для завершения нитки - но не более.


Название: Re: Проблемы с QMutex
Отправлено: kuzulis от Ноябрь 05, 2009, 08:33
а это если прикрутить?

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

:)


Название: Re: Проблемы с QMutex
Отправлено: spectre71 от Ноябрь 05, 2009, 10:37
2 Spectre. А как работает QAtomicInt? За счёт чего достигается атомарность?
Как конкретно реализован QAtomicInt в QT не смотрел.
Но по идее  должны использоваться нативные вызовы конкретной операционки. Например для Windows - Interlocked функции.
С другой стороны атомарные операции должны поддерживаться на уровне определенных команд процессоров.


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 05, 2009, 12:34
а это если прикрутить?

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

:)
Хмм... пригодится или нет - не знаю. Но интересно, спасибо за наводку


Название: Re: Проблемы с QMutex
Отправлено: Igors от Ноябрь 05, 2009, 17:39
Вот неплохая статейка о том что делает atomic

http://labs.trolltech.com/blogs/category/c/ (http://labs.trolltech.com/blogs/category/c/)


Название: Re: Проблемы с QMutex
Отправлено: Akon от Октябрь 01, 2010, 10:52
2 Igors. Мутексы - это дорого, полюбому

Например под виндой мьютекс реализован через Event - это объект ядра, т.е. при выполнении кода блокировки/разблокировки мьютекса происходит переход в режим ядра. На платформе x86 это очень накладно (в районе тыщи машинных инструкций).

В винде есть не связанный с ядром примитив синхронизации CriticalSection (значительно быстрее Event'a). Для блокировки в пределах одного процесса это самое оно.

Если бы под виндой мьютекс был реализован через CriticalSection было бы значительно лучше. Зато мьютекс нельзя было бы ожидать в QWaitCondition.