Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 60, средняя оценка - 4.77
insite2012
Модератор
Эксперт .NET
4790 / 3749 / 1081
Регистрация: 12.10.2013
Сообщений: 10,986
Записей в блоге: 2
#1

PLINQ и работа с PFX - C#

03.01.2015, 10:36. Просмотров 12751. Ответов 4
Метки нет (Все метки)

Итак, приступим.
Небольшое отступление. На создание этой темы меня подтолкнуло чтение статьи с сайта RSDN (написанной, в свою очередь, на материале из книги Албахари, как я понимаю). В данной статье раскрываются тонкости работы PLINQ и библиотеки PFX (ParallelFramework), предназначенных для повышения производительности создаваемых приложений с учетом возрастания количества ядер процессоров на современных компьютерах. Почитав данные материалы, и поискав по форуму, я заметил, что данное направление пока еще мало раскрыто (именно на форуме), и поэтому, думаю, данный материал (особенно, если его поддержат те, у кого есть намного бОльший опыт в данной области) будет полезен. Сразу скажу, что я сам пока не профи в данной области, и буду благодарен за поддержку моего начинания.

Первым делом рассмотрим самое простое - это запросы обычного LINQ. Штука очень удобная, вне всяких сомнений. Но как улучшить производительность для многоядерных компьютеров?
Для этой цели в платформу .NET (начиная с версии 4.0) добавлено новое API - PLINQ. Предположим, мы имеем большой набор данных, который нам необходимо перебрать через запросы LINQ. Для повышения производительности мы можем использовать параллельные запросы. Вот простой пример кода. Верхняя часть - обычный запрос, нижняя - параллельный.
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
 
namespace ParallelTest {
    class Program {
        static void Main(string[] args) {
            IEnumerable<int> numbers = Enumerable.Range(0, 100000000);
            Stopwatch sw = new Stopwatch();
            sw.Start();
            int[] result_NotParallel = numbers.Where(i => i % 2 == 0).ToArray();
            sw.Stop();
            Console.WriteLine("Result WITHOUT using Parallel execution: {0}", sw.ElapsedMilliseconds);
 
            sw.Reset();
            sw.Start();
            int[] result_Parallel = numbers.AsParallel().WithExecutionMode(ParallelExecutionMode.ForceParallelism)
                .WithDegreeOfParallelism(Environment.ProcessorCount)
                .Where(i => i % 2 == 0).ToArray();
            sw.Stop();
            Console.WriteLine("Result WITH using Parallel execution: {0}", sw.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}
Как видно из кода, для того, чтобы выполнить запрос параллельно, достаточно применить статический метод PLINQ AsParallel(). Необходимо отметить, что не все запросы могут быть выполнены параллельно, но это будет рассмотрено далее.
Несколько важных комментариев по приведенному коду:
1. Использование метода AsParallel() не гарантирует, что запрос ОБЯЗАТЕЛЬНО будет выполнен параллельно. Если PLINQ сделает вывод о том, что для улучшения производительности необходимо использовать обычный запрос, он будет выполнен именно так.
2. Для того, чтобы использовать именно параллельный запрос (даже если это затребует много ресурсов), после метода AsParallel() мы можем применить метод WithExecutionMode(), в котором указать параметр - один из членов перечисления ParallelExecutionMode, который и укажет коду, как следует выполнить запрос. В данном коде использован элемент ForceParallelism, который указывает, что запрос ОБЯЗАТЕЛЬНО должен быть выполнен параллельно, даже если потребуется больше ресурсов, чем для использования обычного запроса.
3. Так же есть возможность указать степень параллельности выполнения запроса - через статический метод WithDegreeOfParallelism(), с параметром типа int, который указывает, какое количество задач будет выполняться одновременно. В моем примере в качестве этого параметра используется число доступных ядер процессора на компьютере.

Кроме данных методов, в PLINQ существуют и другие, и их использование будет постепенно рассмотрено.
И напоследок, два скриншота с тестом для демонстрации преимуществ в скорости. Первый сделан на слабом нетбуке, на котором, собственно, и писался данный код. Как видно, использование параллельного запроса привело к увеличению времени выполнения, поскольку затребовало использования больше ресурсов.
Второй скриншот сделан на достаточно мощном четырехядерном компьютере, и там сразу видны все преимущества параллельности - загрузка всех ядер процессора распределена равномерно, время выполнения параллельного запроса существенно снижено.

Спасибо за внимание. Постепенно, по мере изучения мной возможностей PLINQ и PFX, данная тема будет дополняться новыми постами.

http://www.cyberforum.ru/csharp-net/thread1691036.html

15
Миниатюры
PLINQ и работа с PFX   PLINQ и работа с PFX  
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
03.01.2015, 10:36
Я подобрал для вас темы с готовыми решениями и ответами на вопрос PLINQ и работа с PFX (C#):

Можно ли использовать PLINQ на .NET Framework 2.0
Добрый день! Возможно ли на c# проделать следующую штуку: если у...

Как из pfx файла достать private/public ключ?
Есть pfx файл и из него надо достать приватный ключ и публичный ключ, создаю...

PLINQ: Читать файл, каждую строку преобразовывать и записывать преобразованную строку в другой файл
есть простая задача: читать файл, каждую строку преобразовывать и записывать...

Цифровая подпись, файл .pfx
Привет! Никто не подскажет как подписать проект с++ с помощью .pfx файла(сам...

Многопоточность с TPL и PLINQ
Доброго времени суток! В поисках ответов на свои вопросы, я удачно зашёл на...

4
insite2012
Модератор
Эксперт .NET
4790 / 3749 / 1081
Регистрация: 12.10.2013
Сообщений: 10,986
Записей в блоге: 2
03.01.2015, 11:44  [ТС] #2
Продолжим. В данном посте рассмотрим одну из особенностей PLINQ - порядок следования элементов в выходной последовательности.
Если используется обычный LINQ то порядок элементов в выходной последовательности соответствует их порядку во входной (не будем рассматривать фильтрацию последовательности, поскольку в данном случае, очевидно, не все члены входной последовательности присутствуют в выходной, хотя и в данном случае порядок следования сохранится).
Но при использовании PLINQ данное правило меняется. То есть, не факт, что порядок следования элементов в выходной последовательности будет в точности соответствовать входной. В большинстве случаев это и не важно, однако если порядок следования элементов выходной последовательности должен точно следовать порядку входной, можно явно указать это, используя статический метод AsOrdered(). Это укажет PLINQ отслеживать позицию каждого элемента последовательности, что приведет к замедлению скорости работы.
Если порядок следования последовательности после какого-либо запроса PLINQ уже не имеет значения, можно дать команду PLINQ прекратить отслеживание позиций элементов через статический метод AsUnordered().
Вот код, который это демонстрирует, и скриншот выполненной программы.
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ParallelTest2 {
    class Program {
        static void Main(string[] args) {
            string data = "abcdefgh";
            //Последовательный запрос
            string not_Parallel = new string(data
                .Select(s => char.ToUpper(s))
                .ToArray());
            Console.WriteLine(not_Parallel);
            //Параллельный запрос без сохранения порядка следования элементов
            string with_Parallel_NotOrdered = new string(data
                .AsParallel()
                .Select(s => char.ToUpper(s))
                .ToArray());
            Console.WriteLine(with_Parallel_NotOrdered);
            //Параллельный запрос с сохранением порядка следования элементов
            string with_Parallel_WithOredered = new string(data
                .AsParallel()
                .AsOrdered()
                .Select(s => char.ToUpper(s))
                .ToArray());
            Console.WriteLine(with_Parallel_WithOredered);
            //Параллельный запрос с включением-отключением отслеживания порядка следования элементов
            string final_Result = new string(data.AsParallel()
            .AsOrdered()
            .Select(s => char.ToUpper(s))
            .AsUnordered()
            .Select(s => char.ToLower(s))
            .ToArray());
            Console.WriteLine(final_Result);
 
            Console.ReadLine();
        }
    }
}
4
Миниатюры
PLINQ и работа с PFX  
insite2012
Модератор
Эксперт .NET
4790 / 3749 / 1081
Регистрация: 12.10.2013
Сообщений: 10,986
Записей в блоге: 2
04.01.2015, 15:59  [ТС] #3
Продолжаем тему.
Все мы в процессе использования LINQ часто используем анонимные типы, поскольку это удобно. Однако PLINQ вносит свои коррективы в практику. При использовании параллельных запросов наилучшим выбором будет использование не анонимных типов, а именованных структур. Происходит это из-за того, что структура - тип значения, и хранится в стеке. При параллельных запросах каждому потоку выделяется свой стек. При использовании же анонимных типов (которые, как известно, являются классами) всем потокам приходится обращаться к управляемой куче, что снижает производительность.
Я провел несколько экспериментов, и выяснил такую закономерность (на своем железе): при количестве элементов последовательности менее 1000000 анонимные типы работают быстрее. При большем количестве элементов последовательности зависимость изменяется: работа с использованием именованной структуры резко ускоряется, и чем больше элементов, тем четче видна эта разница.
Ниже пример кода и скриншот, который показывает разницу в скорости (количество элементов последовательности - 1000000), при различных вариантах (структура-анонимный тип).
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.Diagnostics;
 
namespace ConsoleApplication6 {
    class Program {
        static void Main(string[] args) {
            Stopwatch sw = new Stopwatch();
            int[] numbers = Enumerable.Range(0, 10000000).ToArray();
            sw.Start();
            var structResult = numbers
                .AsParallel()
                .Select((i, j) => new ItemStruct { Item = i, Index = j })
                .Where(data=>data.Item%2==0)
                .OrderBy(item => item.Index)
                .ToArray();
            Console.WriteLine("Result using struct: {0} milliseconds", sw.ElapsedMilliseconds);
 
            sw.Restart();
            var classResult = numbers
                .AsParallel()
                .Select((i, j) => new { Item = i, Index = j })
                .Where(data => data.Item % 2 == 0)
                .OrderBy(item => item.Index)
                .ToArray();
            Console.WriteLine("Result using class: {0} milliseconds", sw.ElapsedMilliseconds);
 
            Console.ReadLine();
        }
    }
    struct ItemStruct {
        public int Item;
        public int Index;
    }
}
9
Миниатюры
PLINQ и работа с PFX  
insite2012
Модератор
Эксперт .NET
4790 / 3749 / 1081
Регистрация: 12.10.2013
Сообщений: 10,986
Записей в блоге: 2
10.01.2015, 14:05  [ТС] #4
Продолжу тему. Рассмотрим два вопроса: отмена параллельных операция и оптимизация параллельной работы.
Для отмены параллельной операции необходимо использовать расширяющий метод WithCancellation(), в который передать объект - маркер отмены - свойство Token объекта CancellationTokenSource, созданного перед этим.
После того, как объект отмены создан, вызов его метода Cancel() приведет к передаче в параллельные вычисления объекта CancellationToken, после чего все потоки, выполняющие параллельные вычисления, будут завершены.
Необходимо отметить, что в данном случае необходима обязательная обработка ошибок в параллельном коде, поскольку вызов этого метода привод к генерации OperationCanceledException().
Ниже приведен небольшой пример с комментариями к коду.
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.Threading;
using System.Threading.Tasks;
 
namespace PLINQ_Cancellation {
    class Program {
        //Маркер отмены
        static CancellationTokenSource cancelToken = new CancellationTokenSource();
        static void Main(string[] args) {
            //Входная последовательность
            IEnumerable<int> numbers = Enumerable.Range(0, 100);
            //Параллельная фильтрация последовательности
            var result = numbers
                .AsParallel()
                .WithCancellation(cancelToken.Token)
                .WithMergeOptions(ParallelMergeOptions.NotBuffered)
                .Where(n => {
                    Thread.Sleep(100);
                    return n % 2 == 0;
                });
            //Поток для отмены операции (через 2 сек.)
            new Thread(() => {
                //Задержка 2 сек.
                Thread.Sleep(2000);
                //Вызов метода отмены параллельной операции
                cancelToken.Cancel();
 
            }).Start();
            //Получение выходной последовательности
            //(с обязательной обработкой OperationCanceledException)
            try {
                foreach (var r in result) {
                    Console.WriteLine("Item: {0}", r);
                }
            }
            catch (OperationCanceledException ex) {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }
    }
}
Оптимизация параллельных вычислений (со стороны выходной последовательности) может быть осуществлена следующим образом. Допустим, нам необходимо для каждого элемента выполнить какой-либо метод, при чем последовательность выполнения нам не важна. Тогда все, что следует сделать - вызвать метод ForAll() после вызова AsParallel(), передав в метод ForAll() делегат (прямо или через лямбда выражение). После этого для каждого элемента последовательности будет выполнен метод-цель переданного делегата, с параметром - элементом последовательности.
Ниже приведен простой пример использования данного метода.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.Threading;
using System.Threading.Tasks;
 
namespace PLINQ_Optimisation1 {
    class Program {
        static void Main(string[] args) {
            IEnumerable<int> numbers = Enumerable.Range(0, 10);
 
            numbers.AsParallel()
                   .ForAll(i => Console.WriteLine("Changed item: {0}",i*i));
            Console.ReadLine();
        }
    }
}
6
tezaurismosis
24.08.2015, 11:05     PLINQ и работа с PFX
  #5
 Комментарий модератора 
Если вы нашли неточность или опечатку, хотите что-то добавить к написанному в статье - обсуждение ведётся в отдельной теме: http://www.cyberforum.ru/faq/thread1519086.html
0
24.08.2015, 11:05
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.08.2015, 11:05
Привет! Вот еще темы с ответами:

Можно ли подключить сертификат .pfx CURL?
Могу ли я подключить сертификат .pfx к curl запросу?

нужно изменить пароль в файле .pfx
Есть файл .pfx выданный с налогового комитета т.е. ЭЦП и пароль был установлен...

NullReferenceException при использовании PLINQ
Добрый день, проблема заключается в том,что при парсинге сайта с помощью...

PLINQ : выборка несколько к-ых статистик из массива
Всем привет!!! пытаюсь сделать выборку несколько порядковых статистик(то есть у...


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

Или воспользуйтесь поиском по форуму:
5
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru