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

Parallel.For: Зачастую t1 не равно t2 и соответственно не равны Sum1 и Sum2

02.12.2013, 07:07. Показов 727. Ответов 13
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Небольшой вопрос по использованию Parallel.for.
Задача проста...

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   int Min = 0,
       Max = 1000,
       t1 = 0, 
       t2 = 0;
 
   double Sum1 = 0,
          Sum2 = 0;
 
   for (int i = Max + 1; --i > Min - 1; )
   {
       for (int j = Max + 1; --j > Min - 1; t1++)
           Sum1 += Math.Atan(i * j) / (i + 1)
                 * Math.Atan(i * j) / (j + 1);
                    
   }
 
   Parallel.For(Min, Max + 1, i =>
   {
        for (int j = Max + 1; --j > Min - 1; t2++)
            Sum2 += Math.Atan(i * j) / (i + 1)
                  * Math.Atan(i * j) / (j + 1);
   });
но почему то зачастую t1 не равно t2 и соответственно не равны Sum1 и Sum2. Собственно вопрос: почему? и как этого избежать? Заранее спасибо)
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
02.12.2013, 07:07
Ответы с готовыми решениями:

Построить новый массив с элементами, значения которых соответственно равны
Дан массив z1, z2, z3,…, z3n. Построить новый массив с элементами, значения которых соответственно...

Построить новый массив с элементами, значения которых соответственно равны a1, an+1, a2, an+2,…, an, a2n
Дан массив a1, a2, a3,…, a2n. Построить новый массив с элементами, значения которых соответственно...

Построить вектор, элементы которого соответственно равны произведениям элементов строк
Дана матрица: Написать программы построения вектора b1, b2, ..., bm, элементы которого...

Построение вектора, элементы которого соответственно равны произведениям элементов строк матрицы
Дана матрица,заполненная с помощью функции случайных чисел написать программы построения вектора...

13
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
02.12.2013, 10:14 2
У вас гонка за Sum2.
Чтобы избежать этого, вам нужно атомарно увеличивать Sum2. Например, это можно сделать заменив обычное сложение на Interlocked.Add
1
0 / 0 / 1
Регистрация: 04.05.2012
Сообщений: 23
02.12.2013, 14:23  [ТС] 3
Понял проблему, в общих чертах. Но предложенное решение ( Interlocked.Add ) не работает с типом double. Есть какое то решение? В гугле нагуглить не смог.
0
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
02.12.2013, 14:50 4
Ох, вы правы. Тогда можно воспользоваться Interlocked.CompareExchange:
C#
1
2
3
4
5
6
7
     double nextResult = Math.Atan(i * j) / (i + 1)
                       * Math.Atan(i * j) / (j + 1);
     double initialValue, computedValue;
     do {
         initialValue = Sum2;
         computedValue = initialValue + nextResult;
     } while (initialValue != Interlocked.CompareExchange(ref Sum2, computedValue, initialValue));
0
0 / 0 / 1
Регистрация: 04.05.2012
Сообщений: 23
02.12.2013, 17:15  [ТС] 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
//предложенный вариант
Parallel.For(Min, Max + 1, i =>
                {
                    for (int j = Max + 1; --j > Min - 1; t3++)
                    {
                        double nextResult = Math.Atan(i * j) / (i + 1)
                      * Math.Atan(i * j) / (j + 1);
                        double initialValue, computedValue;
                        do
                        {
                            initialValue = Sum3;
                            computedValue = initialValue + nextResult;
                        } while (initialValue != Interlocked.CompareExchange(ref Sum3, computedValue, initialValue));
                    }
                });
 
//модифицированный мной вариант
Parallel.For(Min, Max + 1, i =>
                {
                    double nextResult = 0;
 
                    for (int j = Max + 1; --j > Min - 1; t2++)
                    {
                        nextResult += Math.Atan(i * j) / (i + 1) * Math.Atan(i * j) / (j + 1);
                    }
 
                    double initialValue,
                           computedValue;
                    do
                    {
                        initialValue = Sum2;
                        computedValue = initialValue + nextResult;
                    } while (initialValue != Interlocked.CompareExchange(ref Sum2, computedValue, initialValue));
                });
0
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
02.12.2013, 19:18 6
Цитата Сообщение от Simoniys Посмотреть сообщение
Немного поэкспериментировав я получил более стабильный вариант, но в нем так же случаются отклонения.
Странно, у меня ваш вариант стабильно дает один и тот же результат.
Как вы проверяете отклонения?

Возможно проблема в том, что вы не учитываете особенности операций с плавающей запятой.
Строго говоря для типа double в общем НЕ выполняется (a+b)+c = a+(b+c). Порядок операндов при сложении имеет значение.
0
0 / 0 / 1
Регистрация: 04.05.2012
Сообщений: 23
02.12.2013, 20:25  [ТС] 7
Действительно странно. У меня есть TextBox при нажатии на который у меня вызывается функция:
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
private void textBox1_MouseClick(object sender, MouseEventArgs e)
        {
            int Min = 0,
                Max = 125,
                t1 = 0,
                t2 = 0;
 
            double Sum1 = 0,
                   Sum2 = 0;
 
            for (int i = Max + 1; --i > Min - 1; )
            {
                for (int j = Max + 1; --j > Min - 1; t1++)
                    Sum1 += Math.Atan(i * j) / (i + 1) * Math.Atan(i * j) / (j + 1);
 
            }
 
            Parallel.For(Min, Max + 1, i =>
            {
                double nextResult = 0;
 
                for (int j = Max + 1; --j > Min - 1; t2++)
                {
                    nextResult += Math.Atan(i * j) / (i + 1) * Math.Atan(i * j) / (j + 1);
                }
 
                double initialValue = 0,
                       computedValue = 0;
                do
                {
                    initialValue = Sum2;
                    computedValue = initialValue + nextResult;
                } while (initialValue != Interlocked.CompareExchange(ref Sum2, computedValue, initialValue));
            });
 
            t1 = t2 = 0;//здесь ставлю точку останова
        }
Ну и собственно трассирую через F5 и сравниваю значения в окне контрольных. Возможно где то здесь я что то не обнуляю, но что именно? Дело в том что ошибка может проявиться после 10 нажатий.
0
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
02.12.2013, 21:39 8
Ну так и что, намного различаются значения?
0
0 / 0 / 1
Регистрация: 04.05.2012
Сообщений: 23
02.12.2013, 23:18  [ТС] 9
Различия в данном случае достаточно не значительны: Последние три значащие цифры далеко после запятой.
Sum1 = 45.587510489502144 (double)
Sum2 = 45.587510489502208 (double)
Проблемка в том, что в дальнейшем, распараллеливание я буду применять для вычисления "Инвариантных моментом Лежандра", а вот там эти три циферки могут изрядно попортить мне точность, чего я собственно хотел бы избежать. Неужели нет возможности параллельных вычислений без потерь? Для меня это, честно говоря, новость.
0
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
03.12.2013, 09:28 10
Цитата Сообщение от Simoniys Посмотреть сообщение
Неужели нет возможности параллельных вычислений без потерь?
параллельность тут ни при чём. Скорее всего Sum1 тоже не самый точный результат.
Чтобы увеличить точность при работе с дробными значенями, обычно рекомендуют использовать тип decimal, вместо double.
1
0 / 0 / 1
Регистрация: 04.05.2012
Сообщений: 23
03.12.2013, 12:42  [ТС] 11
Да согласен. Я не совсем верно выразился. Нужна стабильность Что бы одни и тебе вычисления давали один результат. Почему при использовании параллелизма возникают различные значения? Из-за того что (a+b)+c ! = a+(b+c) ? возможно ли этого избежать? Если я например буду использовать массив в который буду передавать соответствующие значения параллельных потоков, а затем сложу их в прямом порядке? это даст мне выигрыш во времени, без потери "относительной точности"?
0
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
03.12.2013, 13:08 12
Цитата Сообщение от Simoniys Посмотреть сообщение
Почему при использовании параллелизма возникают различные значения? Из-за того что (a+b)+c ! = a+(b+c) ?
Скорее всего, да.
Цитата Сообщение от Simoniys Посмотреть сообщение
возможно ли этого избежать? Если я например буду использовать массив в который буду передавать соответствующие значения параллельных потоков, а затем сложу их в прямом порядке? это даст мне выигрыш во времени, без потери "относительной точности"?
Да, ваш вариант приемлем. Даст ли он прирост - это надо проверять.
Еще, как я говорил - можно использовать более точные типы данных.
1
0 / 0 / 1
Регистрация: 04.05.2012
Сообщений: 23
03.12.2013, 15:55  [ТС] 13
Итог:
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
 private void textBox1_MouseClick(object sender, MouseEventArgs e)
        {
            int Min = 0,
                Max = 10000,
                t1 = 0,
                t2 = 0;
 
            long Time1,
                 Time2;
            
            Stopwatch sWatch = new Stopwatch();
 
            double[] S;
            
            double Sum1 = 0,
                   Sum2 = 0;
            
            sWatch.Start();
            for (int i = Max + 1; --i > Min - 1; )
            {
                for (int j = Max + 1; --j > Min - 1; t1++)
                    Sum1 += Math.Atan(i * j) / (i + 1) * Math.Atan(i * j) / (j + 1);
 
            }
            Time1 = sWatch.ElapsedMilliseconds;
 
            sWatch.Restart();
            S = new double[Max - Min + 1];
 
            Parallel.For(Min, Max + 1, i =>
            {
                for (int j = Max + 1; --j > Min - 1; t2++)
                {
                    S[i-Min] += Math.Atan(i * j) / (i + 1) * Math.Atan(i * j) / (j + 1);
                }
                
            });
 
            for (int i = S.Length; --i > -1; )
                Sum2 += S[i];
            Time2 = sWatch.ElapsedMilliseconds;
 
            t1 = t2 = 0;
        }
    }
Sum1 187.93178248450576 double
Sum2 187.93178248451187 double
t1 100020001 int
t2 84945418 int
Time1 12892 long
Time2 4711 long

Как видно при 10 000 итераций прирост скорости хороший. Значение немного отличается, но при этом оно стабильно. Насчет decimal еще думаю. Немного смущает тот факт что t1 и t2 имеют различное значение. Или тут тоже имеет место быть гонка и они не отображают реального количества итераций (конкретно t2)?
0
Эксперт Java
4092 / 3826 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
03.12.2013, 17:23 14
Цитата Сообщение от Simoniys Посмотреть сообщение
Немного смущает тот факт что t1 и t2 имеют различное значение. Или тут тоже имеет место быть гонка и они не отображают реального количества итераций (конкретно t2)?
Да, конечно. Тоже гонка.

Добавлено через 28 минут
PS. Вот на википедии нашел про проблему с порядком операция над числами с плавающей запятой - http://en.wikipedia.org/wiki/F... y_problems
1
03.12.2013, 17:23
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
03.12.2013, 17:23
Помогаю со студенческими работами здесь

Вывести max и min, которые равны соответственно наибольшему и наименьшему членам последовательности
Помогите найти ошибку пожалуйста. Уловие : Определить процедуру p(n, max, min), n - натуральное...

Построение вектора b1, b2, bm, элементы которого соответственно равны наименьшим элементам строк матрицы
Дана матрица, заполненная с помощью функции случайных чисел в динамической области памяти,...

Написать программу построения нового массива с элементами, значения которых соответственно равны
Дан массив а0, а1, а2,…, а2n-1. Написать программу построения нового массива с элементами, значения...

Построить вектор, элементы которого соответственно равны сумме элементов каждой строки матрицы
Помогите, пожалуйста. Дана квадратная вещественная матрица размерности n. Построить матрицу...

Написать программы построения вектора, элементы которого соответственно равны суммам элементов строк
Дана матрица m x n. Написать программы построения вектора b1, b2, ..., bm, элементы которого...

Написать программу построения вектора b, элементы которого соответственно равны наименьшим элементам строк
Решите задачу, заранее большое спасибо. Дана матрица, заполненная с помощью функции случайных...


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

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