Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.62/58: Рейтинг темы: голосов - 58, средняя оценка - 4.62
1 / 1 / 0
Регистрация: 28.05.2013
Сообщений: 50

Многопоточное сложение элементов массива

01.06.2013, 20:46. Показов 11414. Ответов 26
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте.

Задание - сложить многопоточно элементы одномерного массива.
На вход подаётся кол-во элементов и кол-во потоков.

Как это реализовать?

Мне понятно как создать нужное кол-во потоков. Т.е. это приблизительно выглядит так:


C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; ++i)
            {
                Thread thread = new Thread(ThreadFunction);
                thread.Start();
                Console.WriteLine("Поток "+i);
            }
            Console.Read();
        }
 
        static void ThreadFunction()
        {
            Console.WriteLine("");
        }
    }
Такая программа создаст соответственно 10 потоков, выведет в консоль их номера.

А вот как обрабатывать сложение элементов массива?
Для каждого потока писать отдельную функцию? Но на вход подаётся случайное кол-во потоков, как тогда нужное кол-во функций создать?

По сути, результат сложения будет в виде бинарного дерева, как я понимаю. (Извиняюсь, как картинку вставить я так и не понял, поэтому ссылка)

Т.е. на первом уровне происходит попарное суммирование элементов, на втором - суммирование получившихся сумм и так далее до ответа.

Подскажите пожалуйста, как вообще правильно разбивать элементы массива по разным потокам и передавать результат вычисления дальше, в эти же потоки?
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
01.06.2013, 20:46
Ответы с готовыми решениями:

Сложение элементов массива
У нас есть массив с известным количеством элементов, например, . Нужно сделать новый массив, элементами которого будут суммы элементов...

Сложение всех элементов одномерного массива
int z = new int; Console.WriteLine(&quot;Massivtin kosindisin esepteu!!!&quot;); for (int i = 0; i &lt; 5; i++) ...

Сложение элементов массива, индексы которых вводятся с клавиатуры
Есть массив целых чисел. С клавиатуры вводится два числа порядковые номера элементов массива, которые необходимо суммировать(вводится...

26
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
02.06.2013, 01:57
XlorD59, все зависит от того, что вам надо. И да, создавать больше 4-8 потоков (в зависимости от процессора) не просто бесполезно, а получится еще медленнее, чем однопоточно.
0
177 / 94 / 10
Регистрация: 27.05.2013
Сообщений: 290
02.06.2013, 06:57
Такие вещи вообще на SSE писать надо.
0
1 / 1 / 0
Регистрация: 28.05.2013
Сообщений: 50
02.06.2013, 13:35  [ТС]
Psilon, задание как раз и состоит в том, чтобы показать как распараллелится сложение элементов массива в зависимости от количества процессоров, т.е. потоков. О скорости тут речь не идёт.
А нужно - чтобы исходный массив разбивался на пары элементов, эти пары распределялись между потоками. И соответственно каждые два элемента складывались между собой параллельно в разных потоках.
Затем уже результаты этих сложений разбивались на пары элементов, они распределялись между потоками и т.д.
И так до того момента, пока не получится результат - сумма всех элементов массива.
0
177 / 94 / 10
Регистрация: 27.05.2013
Сообщений: 290
02.06.2013, 16:44
быстрее будет потокам раздать равные куски массива, а потом сложить их результаты. Почитай на досуге про класс Parallel.For
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
02.06.2013, 17:03
Вот тупой вариант:
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace ConsoleApplication16
{
    public class Program
    {
        private static void Main(string[] args)
        {
            int[] a = Enumerable.Range(1, 1000).ToArray();
            int i = GetSum(a);
            Console.WriteLine(i);
            Console.ReadKey();
        }
 
        private static int GetSum(int[] a)
        {
            return GetSum(a, 0, a.Length - 1).Result;
        }
 
        private static async Task<int> GetSum(int[] a, int i, int j)
        {
            if (i == j)
                return a[i];
            if (j - i == 1)
                return a[i] + a[j];
            var task = new Task<int>(() => GetSum(a, i, (i+j)/2).Result);
            var task2 = new Task<int>(() => GetSum(a, (i + j) / 2 + 1, j).Result);
            task.Start();
            task2.Start();
            return await task + await task2;
        }
    }
}
вот поумнее:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace ConsoleApplication16
{
    public class Program
    {
        private static void Main(string[] args)
        {
            int[] a = Enumerable.Range(1, 1000).ToArray();
            int sum = 0;
            Parallel.ForEach(a, i => Interlocked.Add(ref sum, i));
            Console.WriteLine(sum);
            Console.ReadKey();
        }
    }
}
1
Каратель
Эксперт С++
6610 / 4029 / 401
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
02.06.2013, 17:06
Цитата Сообщение от Psilon Посмотреть сообщение
вот поумнее:
http://stackoverflow.com/quest... ve#tab-top
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
02.06.2013, 17:08
Jupiter, http://msdn.microsoft.com/ru-r... ocked.aspx
0
Каратель
Эксперт С++
6610 / 4029 / 401
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
02.06.2013, 17:12
Psilon, что ты хочешь этим сказать?
0
1 / 1 / 0
Регистрация: 28.05.2013
Сообщений: 50
02.06.2013, 17:14  [ТС]
Psilon, спасибо, только можно разъяснить что где происходит?
Особенно во втором варианте.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
02.06.2013, 17:16
Jupiter, а ты?

XlorD59, все очень просто, система сама разбивает на потоки с учетом числа процессоров, ядер и т.д., а результат суммы записывается в переменную sum
0
Каратель
Эксперт С++
6610 / 4029 / 401
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
02.06.2013, 17:19
Psilon, я о более естественном подходе к суммированию элементов
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
02.06.2013, 17:29
Jupiter, существует специальный метод, позволяющий пройти параллельно по всем элементам, есть специальный класс, который позволяет атомарно осуществить сумму. Зачем что-то еще изобретать, особенно если учесть, что этот метод еще и быстрее?

Добавлено через 4 минуты
Хотя не, interlocked тормознутее... Тогда нафиг он нужен такой...

Добавлено через 4 минуты
от двух до 10 раз медленнее. Я в шоке.
0
02.06.2013, 17:31

Не по теме:

Цитата Сообщение от Psilon Посмотреть сообщение
существует специальный метод, позволяющий пройти параллельно по всем элементам
я думал AsParallel делает то же самое, беру свои слова назад

0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
02.06.2013, 17:31
Результаты:
Interlocked = 16000 ticks
Lock(this) = 1500 ticks
AsParallel linq = 24000 ticks
0
177 / 94 / 10
Регистрация: 27.05.2013
Сообщений: 290
02.06.2013, 17:56
Parallel в задачах массивов работает быстрее. new Thread() надо использовать лишь в ситуациях ограниченного распараллеливания (2-3 потока максимум) или асинхронной подкачки блоков данных(например параллельная выгрузка и индексация текстовых страниц), когда поток никогда не завершает свою работу, а временно уходит в Sleep. Ну тут короче надо пробовать разные варианты.
0
Higher
 Аватар для diagon
1953 / 1219 / 120
Регистрация: 02.05.2010
Сообщений: 2,925
Записей в блоге: 2
05.06.2013, 19:33
Провел небольшой бенчмарк
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace sum
{
    class Program
    {
        static void Main(string[] args)
        {
            DoBenchmark();
        }
 
        static void DoBenchmark()
        {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
            DisableGC();
 
            var arr = Enumerable.Repeat(6, 1005000 * 6).ToArray();
            int correctAnswer = 6 * arr.Length;
 
            var methods = new Func<int[], int>[] { ForSum, ForeachSum, ForEachSum, ParallelForEachSum, LinqSum, ParallelLinqSum, CppSum, CppOmpSum, CppParallelSum };
            var names = new[] { "ForSum", "ForeachSum", "ForEachSum", "ParallelForEachSum", "LinqSum", "ParallelLinqSum", "CppSum", "CppOmpSum", "CppParallelSum" };
 
            for (int index = 0; index < methods.Length; ++index)
            {
                var method = methods[index];
 
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
 
                long minResult = long.MaxValue;
 
                for (int i = 0; i < 100; ++i)
                {
                    minResult = Math.Min(minResult, TestMethod(method, arr, correctAnswer));
                }
 
                Console.WriteLine("{0}: {1} ticks", names[index], minResult);
            }
        }
 
        static void DisableGC()
        {
            GCLatencyMode oldMode = GCSettings.LatencyMode;
 
            // Make sure we can always go to the catch block, 
            // so we can set the latency mode back to `oldMode`
            RuntimeHelpers.PrepareConstrainedRegions();
 
            GCSettings.LatencyMode = GCLatencyMode.LowLatency;
        }
 
        static long TestMethod(Func<int[], int> foo, int[] arr, int correctAnswer)
        {
            var watch = Stopwatch.StartNew();
 
            if (foo(arr) != correctAnswer)
            {
                return -1;
            }
 
            watch.Stop();
            return watch.ElapsedTicks;
        }
 
        static int ForSum(int[] arr)
        {
            int res = 0;
 
            for (int i = 0; i < arr.Length; ++i)
            {
                res += arr[i];
            }
 
            return res;
        }
 
        static int ForeachSum(int[] arr)
        {
            int res = 0;
 
            foreach (var x in arr)
            {
                res += x;
            }
 
            return res;
        }
 
        static int ForEachSum(int[] arr)
        {
            int res = 0;
 
            Array.ForEach(arr, x => res += x);
 
            return res;
        }
 
        static int ParallelForEachSum(int[] arr)
        {
            int res = 0;
 
            Parallel.ForEach(arr, x => Interlocked.Add(ref res, x));
 
            return res;
        }
 
        static int LinqSum(int[] arr)
        {
            return arr.Sum();
        }
 
        static int ParallelLinqSum(int[] arr)
        {
            return arr.AsParallel().Sum();
        }
 
        static unsafe int CppSum(int[] arr)
        {
            fixed (int* ptr = &arr[0])
            {
                return NativeSum(ptr, arr.Length);
            }
        }
 
        static unsafe int CppOmpSum(int[] arr)
        {
            fixed (int* ptr = &arr[0])
            {
                return NativeOmpSum(ptr, arr.Length);
            }
        }
 
        static unsafe int CppParallelSum(int[] arr)
        {
            fixed (int* ptr = &arr[0])
            {
                return NativeParallelSum(ptr, arr.Length);
            }
        }
 
        [DllImport("libcpp.dll", EntryPoint = "NativeSum")]
        private static extern unsafe int NativeSum(int* arr, int size);
 
        [DllImport("libcpp.dll", EntryPoint = "NativeOmpSum")]
        private static extern unsafe int NativeOmpSum(int* arr, int size);
 
        [DllImport("libcpp.dll", EntryPoint = "NativeParallelSum")]
        private static extern unsafe int NativeParallelSum(int* arr, int size);
    }
}
Плюсовые варианты - троллинга хохмы ради.
Реализация функций на с++
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
48
49
50
51
52
53
54
55
56
#include <algorithm>
#include <future>
#include <vector>
#include <thread>
 
extern "C" __declspec(dllexport) int NativeSum(int *arr, int size)
{
    int res = 0;
    
    for (int i = 0; i < size; ++i)
    {
        res += arr[i];
    }
    
    return res;
}
 
extern "C" __declspec(dllexport) int NativeOmpSum(int *arr, int size) //не идеально, т.к. не сильно разбираюсь в omp
{
    int res = 0;
    
    #pragma omp parallel for num_threads(8)
    for (int i = 0; i < size; ++i)
    {
        #pragma omp atomic
        res += arr[i];
    }
    
    return res;
}
 
extern "C" __declspec(dllexport) int NativeParallelSum(int *arr, int size) //не идеально, набросано на скорую руку
{
    const int threads_count = std::thread::hardware_concurrency();
    const int block_size = size / threads_count;
    
    std::vector< std::future<int> > results;
    results.reserve(threads_count);
    for (int block_begin = 0, block_end = block_size; block_begin < size; block_begin += block_size, block_end += block_size)
    {
            if (block_end > size)
            {
                    block_end = size;
            }
            
            results.emplace_back( std::async(std::launch::async, NativeSum, arr, block_end - block_begin) );
    }
    
    int res = 0;
    for (auto &x : results)
    {
            res += x.get();
    }
    
    return res;
}
Компилятор - g++ 4.8.1
Ключи компиляции -
Bash
1
2
D:\coding\c++test>g++ main.cpp -std=c++11 -shared -static -march=corei7-avx -mtu
ne=corei7-avx -Ofast -flto -fwhole-program -o libcpp.dll


Мои результаты:
Code
1
2
3
4
5
6
7
8
9
ForSum: 8368 ticks
ForeachSum: 8367 ticks
ForEachSum: 37555 ticks
ParallelForEachSum: 319413 ticks
LinqSum: 96028 ticks
ParallelLinqSum: 16649 ticks
CppSum: 3204 ticks
CppOmpSum: 3200 ticks
CppParallelSum: 1025 ticks
Мои выводы:
1) LINQ дико тормозит
2) LINQ дико параллелится (ускорение почти в 6 раз, с учетом того, что у меня 4 ядра + гипертрединг - превосходно).
3) for == foreach в данном случае (я ожидал того, что в for'e будет проверятся выход за границы).
4) JIT плохо инлайнит (ForEach сильно тормозит, видимо, из-за постоянных вызовов функций)
5) JIT не умеет векторизацию
6) тормоза с Parallel.ForEach наводят на мысли, что шарповский тредпул - не самая эффективная штука (omp вроде тоже через тредпул и атомики параллелит, разница видна невооруженным глазом)
2
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
05.06.2013, 20:28
diagon, сделай массив байтов длинной int.MaxValue/3 (чтобы не вылетело OutOfMemory) и затести на нем.
На ночь поставлю (на сотню прогонов каждый способ), а пока так:


debug Any CPU


release x64


release x32
0
 Аватар для m0nax
1274 / 975 / 113
Регистрация: 12.01.2010
Сообщений: 1,971
05.06.2013, 20:57
обязательно надо было считерить на с++ варианте?)

тогда уже надо добавить такой вариант
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
 static int ManualParallelSum(int[] arr)
        {
            int blockSize = arr.Length / Environment.ProcessorCount;
 
            int blockCount = arr.Length / blockSize + arr.Length % blockSize;
 
            var wHandlers = new ManualResetEvent[blockCount];
             
            int[] tempResults = new int[blockCount];
 
            for (int i = 0; i < blockCount; i++)
            {
                ManualResetEvent handler = (wHandlers[i] = new ManualResetEvent(false));
 
                ThreadPool.UnsafeQueueUserWorkItem(param =>
                    {
                        int subResult = 0;
                        int blockIndex = (int)param;
                        int endBlock = Math.Min(arr.Length, blockSize * blockIndex + blockSize);
                        for (int j = blockIndex * blockSize; j < endBlock; j++)
                        {
                            subResult += arr[j];
                        }
                        tempResults[blockIndex] = subResult;
 
                        handler.Set();
                    }, i);
            }
 
            int res = 0;
 
            for (int block = 0; block < blockCount; ++block)
            {
                wHandlers[block].WaitOne();
                res += tempResults[block];
            }
 
            return res;
        }
Добавлено через 15 минут
на anycpu release есть ускорение от без-поточного варианта (у меня аж на 3000 тиков, логических ядер всего 2)
1
 Аватар для m0nax
1274 / 975 / 113
Регистрация: 12.01.2010
Сообщений: 1,971
05.06.2013, 21:07
вот примерно так
Миниатюры
Многопоточное сложение элементов массива  
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
05.06.2013, 21:07
Помогаю со студенческими работами здесь

массив string сложение элементов массива в разной последовательности, все возможные варианты
Подскажите как проще всего реализовать, задача следующая, есть массив, допустим: string mas = new string { &quot;A&quot;,...

Сложение двух элементов массива
Добрый день! Подскажите пожалуйста... есть массив: int Arr = { { 1, 2, 3 }, { 4, 5, 6

Сложение элементов одномерного массива
Приветствую всех) У меня возникли некие затруднения в ходе сложения элементов приведенного ниже одномерного массива Трабл состоит в...

Сложение элементов массива
Здравствуйте! Помогите найти ошибку, при запуске ехе выдает сообщение &quot;Переполнение деления&quot; ;Написать и отладить программу на...

Сложение элементов массива
На взвешивание в птицефабрику стоит гигантская очередь машин. Все машины пронумерованы от 1 до K. Директору интересно знать, какой...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru