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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Помогите с распараллеливанием потоков.  (Прочитано 5690 раз)
Alexu007
Гость
« : Сентябрь 09, 2014, 07:47 »

Вещь для меня новая, прошу помощи более опытных.

Есть вложенный цикл, например решето Эрастофена:

Код
C++ (Qt)
   const uint N = 1000000000;     //миллиард
 
   QBitArray bbuf(N, false);
 
   bbuf[0] = 1;
   bbuf[1] = 1;
 
   for(uint s = 2; s < N; s++)
   {
       if(bbuf.at(s) == false)
       {
          for(uint j = s * 2; j < N; j += s) bbuf[j] = true;
       }
   }

Ну где-то так. На моём компе работает  6 минут, хотелось бы ускорить, и заодно потоки выучить. Поэтому идея: допустим имеем 2 потока, каждый из которых может вычислять внутренний цикл. Внешний цикл проверяет, свободен ли поток, и если свободен - запускает его на выполнение. При этом ставится флаг занятости потока. После окончания вычислений поток сбрасывает флаг занятости. И так далее.

Вопросы: реализуемо ли это в принципе, и будет ли выигрышь во времени, т.е. будут ли при этом работать 2 ядра процессора?
Записан
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #1 : Сентябрь 09, 2014, 07:57 »

Посмотрите в сторону OpenMP Улыбающийся
Записан

m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Сентябрь 09, 2014, 10:44 »

Цитировать
Ну где-то так. На моём компе работает  6 минут, хотелось бы ускорить, и заодно потоки выучить.
У вас алгоритм не оптимально реализован.
И от распараллеливания здесь врят ли будет существенный выйгрыш..
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vizir.vs
Гость
« Ответ #3 : Сентябрь 09, 2014, 11:58 »

Решето Эратосфена не самый лучшей способ поиска простых чисел. Этот метод можно ускорить
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Сентябрь 09, 2014, 12:16 »

[off]
выигрышь/выйгрыш - вспоминается классика
Цитировать
...на котором красовалась надпись: ВАСТОК-2. Я подошел и исправил обе ошибки: ВОСТОГ-2
[/off] Улыбающийся

Что касается распараллеливания - то надо сначала для него переделать алгоритм (так бывает часто). Здесь хорошо на atomic. Главная нитка ищет след простое число, а в это время др нитки занимаются "вычеркиванием". Надо только не пускать главную дальше "уже вычеркнутого"

Внешний цикл проверяет, свободен ли поток, и если свободен ...
На эти грабли наступают абсолютно все - и много раз  Улыбающийся
Записан
Alexu007
Гость
« Ответ #5 : Сентябрь 09, 2014, 20:48 »

Цитировать
Ну где-то так. На моём компе работает  6 минут, хотелось бы ускорить, и заодно потоки выучить.
У вас алгоритм не оптимально реализован.
И от распараллеливания здесь врят ли будет существенный выйгрыш..
Дело не в алгоритме. Я не пишу никакую программу и эти простые числа мне не нужны. Просто, когда я запускаю эту прогу, комп показывает загрузку 25% - это одно ядро процессора. Интересно было бы попробовать загрузить 2 ядра или 4.

Ну так, к слову, эрастофена я оптимизировал. Во первых, внешний цикл достаточно тянуть до кв. корня. Во вторых, я выкинул чётные числа - в 2 раза расход памяти уменьшился и в 2 раза шустрей работать стал:

Код
C++ (Qt)
   const uint N = 1000000000/2;
 
   QBitArray bbuf(N, false);
 
   QTime t1 = QTime::currentTime();
 
   uint k;
 
   for(uint i = 0; i < sqrt(N)+1; i++)
   {
       if(!bbuf.at(i))
       {
           k = 2*i+3;
           for(uint j = k*(i+1)+i; j < N; j += k) bbuf[j] = true;
       }
   }
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #6 : Сентябрь 09, 2014, 22:42 »

Цитировать
Дело не в алгоритме. Я не пишу никакую программу и эти простые числа мне не нужны. Просто, когда я запускаю эту прогу, комп показывает загрузку 25% - это одно ядро процессора. Интересно было бы попробовать загрузить 2 ядра или 4.
Так я о том и говорю.. Вы выбрали пример, где распараллеливание не даст заметного выйгрыша, поскольку операция итерирования примерно совпадает по порядку с операцией во внутреннем цикле:
Код
C++ (Qt)
bbuf[j] = true;
 
+ Надо ещё учитывать затраты на создание потоков и т.п.. Короче, ваша задача едва ли раскроет тот потенциал, который можно извлечь от распараллеливания..
Я бы не стал именно здесь на этом замарачиваться, имхо.. Но, как уже здесь упоминалось, вы можете это легко проверить с помощью OpenMP)

Но есть, например, другие задачи, когда это существенно может ускорить время вычислений. Сам сейчас этим занимаюсь (особенно после того, как прикупил себе AMD FX 8350))
В частности, мне часто приходиться использовать распараллеленную версию std алгоритма transform. О реализации можно почитать, например здесь: http://www.prog.org.ru/topic_25963_15.html  Улыбающийся

 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Сентябрь 10, 2014, 08:42 »

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

Код
C++ (Qt)
#pragma omp parallel for
for (int thread = 0; thread < num_thread; ++thread)
for (uint i = 0; i < N; ++i)
   if (!bbuf.at(i))
 
Вот здесь надо каким-то образом проверить было ли значение в буфере уже "пройдено" всеми остальными нитками или еще нет.

По ходу дела
   for(uint i = 0; i < sqrt(N)+1; i++)
Вероятно компилятор это оптимизирует, но все равно грамотно будет вычислить счетчик самому до цикла

« Последнее редактирование: Сентябрь 10, 2014, 13:32 от Igors » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Сентябрь 10, 2014, 11:25 »

Да, есть прозаичный, хотя  и громоздкий, способ:

- находим все простые числа напр в диапазоне от 0 до 100
- запускаем "вычеркивание" всеми нитками для полного диапазона
- находим простые в след диапазоне - напр от 100 до 1000
и.т.д

Можно и по-другому, с LRU или даже с std::set ...
Записан
Bepec
Гость
« Ответ #9 : Сентябрь 10, 2014, 11:33 »

А разве openmp не забирает на себя полностью эти проблемы? Насколько мне объясняли, openmp просто бьёт цикл на N частей и каждый отдаёт потоку. Собственно этим и достигается распараллеливание.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Сентябрь 10, 2014, 13:38 »

А разве openmp не забирает на себя полностью эти проблемы? Насколько мне объясняли, openmp просто бьёт цикл на N частей и каждый отдаёт потоку. Собственно этим и достигается распараллеливание.
Он может бить всяко-разно и диспатчить, напр
Код
C++ (Qt)
#pragma omp parallel for schedule(dynamic, 3)
Но он не отвечает за зависимость одного вычисления (тела цикла) от другого, а здесь она есть. Откройте в вике "решето Эрастофена"
Записан
Alexu007
Гость
« Ответ #11 : Сентябрь 10, 2014, 14:00 »

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


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