Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.76/21: Рейтинг темы: голосов - 21, средняя оценка - 4.76
2 / 2 / 4
Регистрация: 28.06.2013
Сообщений: 56
1

Многопоточность и вычисления: выбор оптимальной стратегии

07.10.2015, 19:03. Показов 3880. Ответов 15
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Есть некоторый массив
C++
1
float acc[SIZE]
Нужно произвести модификацию элементов этого массива, путём многократного прибавления(M>>SIZE) некоторых значений. Доступ к элементу массива можно сказать случаен, прибавляемое значение никак не зависит от содержимого элемента массива, то есть они по сути аккумулируют значения. Так как прибавляемое значение и расчёты "попадания" в нужный элемент массива занимают много времени, но при этом не зависят от содержимого массива, то хотелось бы раскидать их по потокам. С потоками к сожалению дела почти не имел. Сколько потоков создавать? Так как характер коллизий неизвестен, то наверное оптимально по количеству ядер(тут бы не хотелось бы привязываться к конкретной машине)? Какой механизм синхронизации лучше будет использовать в моём случае - спинлок, мютекс или что нибудь другое?

Если есть какой нибудь похожий пример - с удовольствием почитал бы.

зы что использовать буду std::thread или boost::thread пока не решил. Знаю я их одинаково плохо
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
07.10.2015, 19:03
Ответы с готовыми решениями:

Найти счёт при оптимальной стратегии двух игроков
взялся тут решать задачку с олимпиады, и честно говоря уже час потратил за зря...Никак не могу...

Выбор оптимальной структуры данных
Здравствуйте! Задача состоит в следующем. Есть большой файл (~68 mb) с текстом. Нужно посчитать...

Выбор оптимальной последовательности. Конечный алгоритм
Дана квадратная матрица размером NxN, например: Нужно выбрать j-е число из i-ой строки,...

Нахождение оптимальной стратегии игры
Я совсем не догоняю в задачах, если не трудно решите мне саму задачу :sorry: ;) Нахождение...

15
594 / 532 / 76
Регистрация: 22.03.2011
Сообщений: 1,585
07.10.2015, 20:20 2
Слабо понял задачу, как это будет происходить...

Цитата Сообщение от Skaarj Посмотреть сообщение
Сколько потоков создавать? Так как характер коллизий неизвестен, то наверное оптимально по количеству ядер(тут бы не хотелось бы привязываться к конкретной машине)
можно ориентироваться на std::thread::hardware_concurrency
1
2 / 2 / 4
Регистрация: 28.06.2013
Сообщений: 56
07.10.2015, 22:22  [ТС] 3
Цитата Сообщение от OstapBender Посмотреть сообщение
можно ориентироваться на std::thread::hardware_concurrency
Похоже это то что надо.

Возможно я несколько сумбурно описал задачу.
У меня есть достаточно тяжеловесные вычисления для одной операции. И есть массив достаточно большого размера ~10^4-10^5. К его элементам в случайном порядке прибавляется некоторая величина(либо вообще не прибавляется), рассчитанная при каждой операции. Нужно произвести допустим 10^6-10^7 таких операций. Если считать в несколько потоков, то надо как то синхронизировать доступ к его элементам, иначе как я понял - неопределенное состояние. Оно будет редким, но всё же будет.

Вот псевдокод.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    
#define SIZE 10000
    float calculate()
    {
        // Ресурсоемкие вычисления
    }
 
    void main()
    {
        float acc[SIZE];
        float temp = 0;
 
        srand(time(NULL));
        size_t index = 0;
    
        //Вот эту часть распараллелить по потокам
        for (size_t i = 0; i < 10000000; i++)
        {
            index = rand() % SIZE;        // Здесь на самом деле тоже долго высчитывать. 
            temp = calculate();
            acc[index] += temp;     // Вот здесь нужно синхронизировать
        }
    }
Я так понимаю, что делать потоков больше чем ядер нету смысла. Осталось разобраться какой механизм синхронизации больше всего подойдёт, вроде как простой спинлок должен подойти. И ещё возник вопрос по поводу srand. По идее для каждого потока нужно делать своё зерно иначе он будет генерить для каждого потока одинаковые числа? Может есть какое нибудь стандартное решение?
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
07.10.2015, 23:14 4
Цитата Сообщение от Skaarj Посмотреть сообщение
Сколько потоков создавать?
по количеству ядер машины.

Цитата Сообщение от Skaarj Посмотреть сообщение
Какой механизм синхронизации лучше будет использовать в моём случае - спинлок, мютекс или что нибудь другое?
std::atomic<float>
1
1458 / 795 / 257
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
08.10.2015, 09:54 5
Я бы сделал примерно так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <vector>
#include <utility>
#include <thread>
#include <future>
#include <ctime>
#include <random>
#include <array>
 
using data_t = float;
using res_t = std::pair<data_t, size_t>;
constexpr std::size_t SIZE = 10;
std::array<data_t, SIZE> data;
std::mutex m;
 
res_t calculate()
{
   static std::mt19937 gen(static_cast<unsigned>(std::time(nullptr)));
   std::uniform_real_distribution<data_t> data_dis(-10, 10);
   std::uniform_int_distribution<std::size_t> index_dis(0, SIZE-1);
 
   std::this_thread::sleep_for(std::chrono::milliseconds(index_dis(gen)));
   return {data_dis(gen), index_dis(gen)};
}
 
template <typename Cont>
void modify(Cont &cont, const typename Cont::value_type& val, std::size_t idx)
{
   std::lock_guard<std::mutex> lock(m);
   cont[idx] += val;
}
 
int main()
{
   std::vector<std::future<res_t>> results;
   int count = 10000;
   while (count--)
      results.emplace_back(std::async(calculate));
 
   for (auto &f : results)
   {
      res_t res  = f.get();
      modify(data, res.first, res.second);
   }
 
   for (data_t i : data) std::cout << i << "\n";
}
1
693 / 303 / 99
Регистрация: 04.07.2014
Сообщений: 846
08.10.2015, 10:04 6
  1. N потоков создают N массивов
  2. По окончании работы складываем массивы в один
  3. Используй OpenMP
  4. Синхронизация не нужна (у каждого свой массив)
  5. Собственный массив для каждого из потоков, позволяет держать их (или их часть) в кеше ядра процессора, что даст ещё дополнительный прирост производительности

Добавлено через 1 минуту
PS: не используй rand в нескольких потоках, это блокирующая функция.
1
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
08.10.2015, 10:26 7
Цитата Сообщение от DiffEreD Посмотреть сообщение
template <typename Cont>
void modify(Cont &cont, const typename Cont::value_type& val, std::size_t idx)
{
* *std::lock_guard<std::mutex> lock(m);
* *cont[idx] += val;
}
жжем производительность напалмом.
0
693 / 303 / 99
Регистрация: 04.07.2014
Сообщений: 846
08.10.2015, 11:37 8
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <vector>
#include <random>
#include <time.h>
#include <math.h>
#include <omp.h>
 
#define SIZE 10000
double calculate(double x)
{
  return sin(x)*cos(x);
}
 
int main()
{
    // Итоговый вектор, данные храним на куче
    std::vector<double> acc(SIZE);
 
    // Начало параллельной секции
    #pragma omp parallel
    {
        // Промежуточный вектор
        std::vector<double> acc_private(SIZE);
        // Каждый поток использует свой генератор со своим зерном
        std::mt19937 gen(time(0)+1000*omp_get_thread_num());
        std::uniform_int_distribution<std::size_t> index_dis(0, SIZE-1);
 
        // Распределяем иттерации по потокам
        #pragma omp for
        for (size_t i=0; i<10000000; ++i) {
            size_t index = index_dis(gen);
            acc_private[index] += calculate((double)i/1000.0);
        }
        // Критическая секция со своей синхронизацией
        #pragma omp critical
        {
            for(size_t i=0; i<SIZE; ++i) {
                acc[i] = acc_private[i];
            }
        }
    }
    return 0;
}
При компиляции не забываем включить OpenMP

Код
$ g++ -Wall -std=c++11 -fopenmp openmp_reduction_array.cpp 
$ time ./a.out 

real    0m0.404s
user    0m1.556s
sys     0m0.004s
1
2 / 2 / 4
Регистрация: 28.06.2013
Сообщений: 56
08.10.2015, 16:53  [ТС] 9
Всем спасибо за ответы. Сейчас стало более менее понятно в какую сторону думать

Цитата Сообщение от hoggy Посмотреть сообщение
std::atomic<float>
Для float, кстати, как я понял метод atomic::fetch_add() не объявлен. Работает только с целочисленными типами. Ну здесь наверное можно попробовать int или в случае чего long long.

Не совсем понятен этот кусок
C++
1
2
3
4
5
        #pragma omp critical
        {
            for(size_t i=0; i<SIZE; ++i) {
                acc[i] = acc_private[i];
            }
Почему мы просто присваиваем наш вектор к acc_private? Ведь acc_private как я понимаю вектор соответствующий каждому потоку по идее нужно сложить накопленные результаты в каждом потоке и скопировать в наш вектор?
0
693 / 303 / 99
Регистрация: 04.07.2014
Сообщений: 846
08.10.2015, 22:16 10
Skaarj, упс опечатка естественно должно было быть +=

Добавлено через 6 минут
Нужно добавить накопленные данные каждого из потоков к целевому
0
2063 / 1542 / 168
Регистрация: 14.12.2014
Сообщений: 13,402
08.10.2015, 22:49 11
Цитата Сообщение от Skaarj Посмотреть сообщение
Сколько потоков создавать? Так как характер коллизий неизвестен, то наверное оптимально по количеству ядер(тут бы не хотелось бы привязываться к конкретной машине)?
Мысль правильная. только кроме того что по количеству конвейеров надо еще гарантировать их выполнение на разных конвейерах. Опять же какой размер массива и каков размер данных которые используются для счета? Если размер большой то все может просто напросто упираться в трансфер по шине и параллелить просто без толку.
Цитата Сообщение от AlexVRud Посмотреть сообщение
N потоков создают N массивов
По окончании работы складываем массивы в один
Используй OpenMP
Синхронизация не нужна (у каждого свой массив)
Собственный массив для каждого из потоков, позволяет держать их (или их часть) в кеше ядра процессора, что даст ещё дополнительный прирост производительности
В GPU в GPU c таким методом. Там и конвейеров ого-го сколько и трансфер ого-го, и то что потоки на разных конвейерах тоже гарантированно. Он именно для такого подхода и создавался.
0
693 / 303 / 99
Регистрация: 04.07.2014
Сообщений: 846
09.10.2015, 09:35 12
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
В GPU в GPU c таким методом.
И как эта задача ляжет на GPU? Только если решить
C++
1
2
3
 for (size_t i = 0; i < 10000000; i++)
        {
            index = rand() % SIZE;
понять, что для index=0 надо перебрать i из 123, 542, 643, .... для index=1 надо перебрать i = 14,42, 52355, .... и т.д.
и уже распределять их вычисление по index.

Рабочий код я представил. Тест на 2-х процессорной машинке:
Threads 1 2 5 10 11 12 19 20
Real Time 3,276 1,641 0,660 0,3330,3040,2800,1800,187
CPU time 3,265 3,2663,2653,2703,2683,2713,2873,350
Соотношение 0,997 1,990 4,947 9,820 10,750 11,682 18,261 17,914
Регресс только в последнем случае, но там вмешались другие потоки, его можно решить через schedule и nowait

Предложенное мной решение как раз и рассматривается для нескольких потоков (~10).

При большом количестве потоков надо менять алгоритм вычисления.

Опять же какой размер массива и каков размер данных которые используются для счета?
ТС указал достаточно для выбора алгоритма решения задачи, остальное тонкости, до которых ему, на данном этапе, должно быть фиолетово.
0
5231 / 3204 / 362
Регистрация: 12.12.2009
Сообщений: 8,116
Записей в блоге: 2
09.10.2015, 10:51 13
В чем смысл синхронизации? Если массив разбить по потокам ничего же синхронизировать не надо.
0
2063 / 1542 / 168
Регистрация: 14.12.2014
Сообщений: 13,402
09.10.2015, 16:16 14
Цитата Сообщение от AlexVRud Посмотреть сообщение
ТС указал достаточно для выбора алгоритма решения задачи, остальное тонкости, до которых ему, на данном этапе, должно быть фиолетово.
Вопрос какой трансфер данных по шине тянет за собой сама функция calculate(); Тоже в принципе может на каких то таблицах считаться.
Цитата Сообщение от AlexVRud Посмотреть сообщение
И как эта задача ляжет на GPU? Только если решить
Цитата Сообщение от AlexVRud Посмотреть сообщение
При большом количестве потоков надо менять алгоритм вычисления.
Алгоритм примерно тот же, только у каждого потока массив разреженный. И периодически по исчерпанию памяти производить суммирование разреженных массивов в результирующий массив.
0
2 / 2 / 4
Регистрация: 28.06.2013
Сообщений: 56
09.10.2015, 23:08  [ТС] 15
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Мысль правильная. только кроме того что по количеству конвейеров надо еще гарантировать их выполнение на разных конвейерах. Опять же какой размер массива и каков размер данных которые используются для счета? Если размер большой то все может просто напросто упираться в трансфер по шине и параллелить просто без толку.
Объём массива будет задаваться пользователем, порядок ~10^4. Суть задачи - решение методом Монте Карло задачи переноса электронов в веществе. Пример я тут немного утрировал. Исходные данные будут статическими. Во время жизни частицы по случайному закону будут меняться её положение, направление и энергия и ещё несколько вспомогательных параметров, пока она не поглотится/вылетит за пределы области. Поток будет соответствовать трассировке одной частице. Массив будет накапливать полученную энергию.
0
2063 / 1542 / 168
Регистрация: 14.12.2014
Сообщений: 13,402
09.10.2015, 23:14 16
Цитата Сообщение от Skaarj Посмотреть сообщение
Поток будет соответствовать трассировке одной частице
Ото дейcтвительно в GPU. Задача из того же класса что и трассировка лучей и построение фотонных карт. И она там именно так и решается -по потоку на луч/фотон.
1
09.10.2015, 23:14
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.10.2015, 23:14
Помогаю со студенческими работами здесь

Выбор оптимальной видеокарты
Система: Процессор - INTEL core i3-2100 3.1GHz Мать - ASrock h61m\u3s3 ОЗУ - DDR3 1600 8GB Блок...

Выбор оптимальной замены
Здравствуйте! Прошу помочь мне с выбором хорошей видеокарты (желательно GeForce, т.к....

Выбор оптимальной БД / СУБД
В рамках дипломного проекта разрабатываю систему &quot;умный дом&quot;. Реализация сводится к...

Выбор оптимальной видеокарты
Здравствуйте. На днях полетела видюха, решил её сменить. Остановился на geforce gtx 750 ti. Отсюда...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru