crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

 
Ответить в данную темуНачать новую тему
> QtConcurrent нужен совет по использованию
SABROG
  опции профиля:
сообщение 27.12.2009, 19:49
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Есть код брутфорса, который делает 4294967295 (int32) операций. По диспетчеру задач вижу, что используется только одно ядро процессора. Поэтому решил посмотреть на возможности класса QtConcurrent. В принципе задача следующая, распределить нагрузку на все ядра. 4294967295 / QThread::idealThreadCount(). Если брать конкретно ситуацию с двумя ядрами, то одно ядро должно перебирать (4294967295 / 2 = 2147483647,5) диапозон 0-2147483647, второе 2147483647-4294967295. Если один из потоков находит нужную последовательность, то я хочу иметь возможность вернуть результат и прервать все остальные потоки (сколько бы ядер не было). Также я планирую выводить QProgressDialog и если пользователь жмет "Cancel", то завершать все вычисления. Тут я планирую использовать QFutureWatcher::cancel(), однако тут тоже не понятно. Если внутри функции будет бесконечный или очень долгий цикл, сработает ли cancel()?

Каша в голове из-за обилия методов передачи указателя на мою функции или метод класса. Для начала мне нужно передать некий список с задачами, причем в задаче должен быть не один параметр. Значит придется использовать QList<QPair <int, int> > для передачи диапозонов в метод вычисления. Методы типа QtConcurrent::run(), QtConcurrent::*Reduced(), QtConcurrent::*filtered() исключаются. Первый из-за невозможности прерывания, второй из-за того, что запускается только один поток, третий из-за удаления элементов из списка при возвращении true функцией (этот функционал мне не нужен). Значит остаются QtConcurrent::map и QtConcurrent::mapped(). Из-за скудного описания я не понимаю как они работают. Эти методы блокирующие или нет?

Далее насчет прогресса. Я так понял метод типа QFutureWatcher::progressValue () может мне возвратить только 2 значения: 50% и 100%, т.к. ядра 2 и потоков тоже 2. Меня это не устраивает, мне нужен реальный ход прогресса, который бы вычислялся на основе текущих значений переменной i во всех потоках. Т.е. если один поток на стадии цикла 5 из диапозона 0-10, а второй поток на стадии выполнения 2 из диапозона 11-20, то 2+5=8 - общий ход выполнения. Таким образом мне нужен механизм для влияния на QProgressDialog. Отсюда всплывают сигналы и слоты. Значит мои потоки должны уметь испускать сигналы. Стало быть список для QtConcurrent::map должен быть какой-то такой QList<QObject *>, а раз ядра 2, то и объектов в списке будет 2. Придется завести сигнал типа step(), который подключить к QProgressDialog, таким образом шквал этих сигналов от разных потоков заставит полосочку бежать вперед. Затем нужно будет завести сигнал типа finish(), чтобы мои объекты могли останавливать ход просчета на середине, если один из потоков нашел искомую комбинацию. И эту комбинацию (результат) еще надо будет как-то вернуть обратно в основной поток.

В общем всё довольно сложно, может будут какие-то идеи?
---
Похоже я начал вникать потихоньку. В QtCreator'e везде используется QtConcurrent::run(), причем внутри QObject'ов, в метод передается указатель this и ссылка на метод этого класса. И мне похоже надо будет создать каркас класса унаследовав QObject и создать количество экземпляров типа Task равное количеству процессоров, после чего в итерации запустить на каждом метод типа QtConcurrent::run().
---
Разработчики QtCreator'а злыдни. Используют не документированный класс QFutureInterface<> для установки прогресса и результата потока. А простые пользователи библиотеки значит должны голову ломать...

Сообщение отредактировал SABROG - 28.12.2009, 0:16
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
azure
  опции профиля:
сообщение 29.12.2009, 14:51
Сообщение #2


Студент
*

Группа: Участник
Сообщений: 60
Регистрация: 24.12.2009
Пользователь №: 1332

Спасибо сказали: 5 раз(а)




Репутация:   0  


QThread мб? я сам не пробовал, но из документации понял что это должно выглядеть так:
из основного треда (в котором гуй с прогресбаром) запускаеются треды. каждый из них по мере вычислений испускает сигналы, мол, я завершил свою работу на 10%, например. гуи эти сигналы ловит и, зная кол-во тредов и прогресс каждого из них вычисляет реальный прогресс.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 29.12.2009, 23:11
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


В цикле я создал несколько потоков через QtConcurrent::run() и благодаря тому, что у этой функции аж 42 варианта параметров я передал указатель на каждый класс на базе QObject'a и метод этого класса с параметрами. Межпоточные сигналы и слоты работают. Единственные проблемы с которыми я столкнулся были: прерывание потока и распределение нагрузки по ядрам. Первую проблему я решил через флаг типа m_canceled и сигнал, который посылал потоку, чтобы он сам его выставлял. Вторую проблему решил через функцию, которую пришлось написать, чтобы равномерно распределить задачи по доступным ядрам. Если количество задач не делиться без остатка, то при варианте 11/2 (11 задач и 2 ядра), нагрузка распределяется как 6+5, т.е. метод раскидывает остаток по задачам, пока остаток не станет равным нулю. Прогресс выполнения всей задачи определяю следующим образом. Передаю в каждый поток цифру типа total, общую сумму. Определяю сколько процентов будет от этой общей суммы и сколько процентов сделало текущее ядро, в главный поток посылаю сигнал типа step(), который равняется 1%. Т.е. QProgressBar увеличивается сам по этому сигналу из всех потоков. Тут оказался важным момент округления для нахождения процента от задачи. Если не округлить по правилу математики, то количество сигналов step() может быть больше на 1 из-за того, что при делении целочисленного числа по умолчанию округление идет в меньшую сторону. С другой стороны, если насильно прибавить +1, то числа, которые делятся ровно без остатка посылают меньше сигналов и прогресс висит на 99% и автоматически не закрывается. Из всего этого я делаю сейчас вывод, что QtConcurrent::run() в каком-то смысле зло по сравнению с QtConcurrent::map/mapped, т.к. последний сам всё распределяет по ядрам и более того позволяет видеть прогресс выполненной задачи и отменять задачу (я писал выше, что мне пришлось полностью реализовывать механизм отмены задачи саму, также как и процесс распределения задач по процессорам и отчет о прогрессе через сигналы). Я не буду переписывать свою программу с QtConcurrent::run() на QtConcurrent::map из-за того, что моё количество задач огромно и не влазит в контейнер и оперативную память соответственно, а делать 816 вызовов QtConcurrent::map при расходе ОЗУ в 20Мб я не намерен, т.к. считаю это оверхедом и раз уж я уже написал всё сам, чтобы его избежать, то так пусть оно и остается. Но советовать другим людям я буду именно QtConcurrent::map()/mapped().
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


3 чел. читают эту тему (гостей: 3, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 3.1.2025, 4:30