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

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

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

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

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

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

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

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

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

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

Цитата Сообщение от Skaarj Посмотреть сообщение
Сколько потоков создавать? Так как характер коллизий неизвестен, то наверное оптимально по количеству ядер(тут бы не хотелось бы привязываться к конкретной машине)
можно ориентироваться на std::thread::hardware_concurrency
1
2 / 2 / 4
Регистрация: 28.06.2013
Сообщений: 56
07.10.2015, 22:22  [ТС]
Цитата Сообщение от 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
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
07.10.2015, 23:14
Цитата Сообщение от Skaarj Посмотреть сообщение
Сколько потоков создавать?
по количеству ядер машины.

Цитата Сообщение от Skaarj Посмотреть сообщение
Какой механизм синхронизации лучше будет использовать в моём случае - спинлок, мютекс или что нибудь другое?
std::atomic<float>
1
 Аватар для DiffEreD
1458 / 795 / 257
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
08.10.2015, 09:54
Я бы сделал примерно так:
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
694 / 304 / 99
Регистрация: 04.07.2014
Сообщений: 851
08.10.2015, 10:04
  1. N потоков создают N массивов
  2. По окончании работы складываем массивы в один
  3. Используй OpenMP
  4. Синхронизация не нужна (у каждого свой массив)
  5. Собственный массив для каждого из потоков, позволяет держать их (или их часть) в кеше ядра процессора, что даст ещё дополнительный прирост производительности

Добавлено через 1 минуту
PS: не используй rand в нескольких потоках, это блокирующая функция.
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
08.10.2015, 10:26
Цитата Сообщение от 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
694 / 304 / 99
Регистрация: 04.07.2014
Сообщений: 851
08.10.2015, 11:37
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

Code
1
2
3
4
5
6
$ 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  [ТС]
Всем спасибо за ответы. Сейчас стало более менее понятно в какую сторону думать

Цитата Сообщение от 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
694 / 304 / 99
Регистрация: 04.07.2014
Сообщений: 851
08.10.2015, 22:16
Skaarj, упс опечатка естественно должно было быть +=

Добавлено через 6 минут
Нужно добавить накопленные данные каждого из потоков к целевому
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
08.10.2015, 22:49
Цитата Сообщение от Skaarj Посмотреть сообщение
Сколько потоков создавать? Так как характер коллизий неизвестен, то наверное оптимально по количеству ядер(тут бы не хотелось бы привязываться к конкретной машине)?
Мысль правильная. только кроме того что по количеству конвейеров надо еще гарантировать их выполнение на разных конвейерах. Опять же какой размер массива и каков размер данных которые используются для счета? Если размер большой то все может просто напросто упираться в трансфер по шине и параллелить просто без толку.
Цитата Сообщение от AlexVRud Посмотреть сообщение
N потоков создают N массивов
По окончании работы складываем массивы в один
Используй OpenMP
Синхронизация не нужна (у каждого свой массив)
Собственный массив для каждого из потоков, позволяет держать их (или их часть) в кеше ядра процессора, что даст ещё дополнительный прирост производительности
В GPU в GPU c таким методом. Там и конвейеров ого-го сколько и трансфер ого-го, и то что потоки на разных конвейерах тоже гарантированно. Он именно для такого подхода и создавался.
0
694 / 304 / 99
Регистрация: 04.07.2014
Сообщений: 851
09.10.2015, 09:35
Цитата Сообщение от 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
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
09.10.2015, 10:51
В чем смысл синхронизации? Если массив разбить по потокам ничего же синхронизировать не надо.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
09.10.2015, 16:16
Цитата Сообщение от AlexVRud Посмотреть сообщение
ТС указал достаточно для выбора алгоритма решения задачи, остальное тонкости, до которых ему, на данном этапе, должно быть фиолетово.
Вопрос какой трансфер данных по шине тянет за собой сама функция calculate(); Тоже в принципе может на каких то таблицах считаться.
Цитата Сообщение от AlexVRud Посмотреть сообщение
И как эта задача ляжет на GPU? Только если решить
Цитата Сообщение от AlexVRud Посмотреть сообщение
При большом количестве потоков надо менять алгоритм вычисления.
Алгоритм примерно тот же, только у каждого потока массив разреженный. И периодически по исчерпанию памяти производить суммирование разреженных массивов в результирующий массив.
0
2 / 2 / 4
Регистрация: 28.06.2013
Сообщений: 56
09.10.2015, 23:08  [ТС]
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Мысль правильная. только кроме того что по количеству конвейеров надо еще гарантировать их выполнение на разных конвейерах. Опять же какой размер массива и каков размер данных которые используются для счета? Если размер большой то все может просто напросто упираться в трансфер по шине и параллелить просто без толку.
Объём массива будет задаваться пользователем, порядок ~10^4. Суть задачи - решение методом Монте Карло задачи переноса электронов в веществе. Пример я тут немного утрировал. Исходные данные будут статическими. Во время жизни частицы по случайному закону будут меняться её положение, направление и энергия и ещё несколько вспомогательных параметров, пока она не поглотится/вылетит за пределы области. Поток будет соответствовать трассировке одной частице. Массив будет накапливать полученную энергию.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
09.10.2015, 23:14
Цитата Сообщение от Skaarj Посмотреть сообщение
Поток будет соответствовать трассировке одной частице
Ото дейcтвительно в GPU. Задача из того же класса что и трассировка лучей и построение фотонных карт. И она там именно так и решается -по потоку на луч/фотон.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.10.2015, 23:14
Помогаю со студенческими работами здесь

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

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru