Опубликован: 15.10.2009 | Доступ: свободный | Студентов: 896 / 248 | Оценка: 4.42 / 4.20 | Длительность: 08:22:00
Специальности: Программист
Лекция 7:

Введение в PLINQ

< Лекция 6 || Лекция 7: 12 || Лекция 8 >
Аннотация: В лекции рассматривается параллельный интегрированный язык запросов и его отличия от его классической реализации.

PLINQ (Parallel Language-Integrated Query) - параллельный интегрированный язык запросов - является параллельной реализацией LINQ. Отличие PLINQ от LINQ состоит в том, что запросы выполняются параллельно, используя, обычно, все доступные ядра/процессоры.

7.1 Использование PLINQ

PLINQ предназначен для параллельного выполнения LINQ-запросов и потому его реализация встроена в библиотеку System.Linq. PLINQ полностью поддерживает все операторы запросов, имеющиеся в .NET, и имеет минимальное влияние на существующую модель использования и исполнения LINQ-операторов.

Рассмотрим простой пример использования LINQ. Предположим, что мы имеем метод, который проверяет делимость целого числа на 5. Добавим в него некоторую длительную обработку, чтобы этот метод можно было использовать в параллельных программах:

private static bool IsDivisibleBy5 ( int p )
{
      //   Моделирование длительной обработки
      for ( int i = 0; i < 10000000; i++ ) {
     i++; i--;
      }
      return p % 5 == 0;
}
Пример 7.1.

Следующий фрагмент программы с использование LINQ-операторов позволяет подсчитать количество чисел в интервале от 1 до 1000, которые делятся на 5:

IEnumerable<int> arr = Enumerable.Range ( 1, 1000 );
var q =
     from n in arr          
         where ISDivisibleBy5 ( n ) 
      select n;
List<int> list = q.ToList();
Console.WriteLine ( list.Count.ToString() );
Пример 7.2.

Чтобы распараллелить этот запрос средствами PLINQ, достаточно применить к источнику данных (в данном случае, это перечислимый массив arr) extension-метод AsParallel():

IEnumerable<int> arr = Enumerable.Range ( 1, 1000 );
var q =
     from n in arr.AsParallel()          
         where ISDivisibleBy5 ( n ) 
      select n;
List<int> list = q.ToList();
Console.WriteLine ( list.Count.ToString() );
Пример 7.3.

В действительности, LINQ-запросы, записанные в SQL-подобном синтаксисе, как в Примере 2, переводятся в серию вызовов extension-методов классов, содержащихся в System.Linq.Enumerable. Другими словами, запрос из Примера 2 может быть переписан в следующей эквивалентной форме:

IEnumerable<int> q = 
         Enumerable.Select<int,int> (
     Enumerable.Where<int> (          
         arr, delegate (int n) { return 
      IsDivisibleBy5 (n); }
         ),
         delegate (int n) { return n; }
);
Пример 7.4.

Наряду с Enumerable, библиотека System.Linq содержит эквивалентный класс ParallelEnumerable, который содержит тот же набор методов, что и класс Enumerable, но предназначенных для параллельного исполнения.

Тогда параллельная реализация Примера 4 запишется следующим образом:

IParallelEnumerable<int> q = 
         ParallelEnumerable.Select<int,int> (
     ParallelEnumerable.Where<int> (          
         ParallelQuery.AsParallel<int> (arr),
         delegate (int n) { return 
                         IsDivisibleBy5 (n); }
         ),
         delegate (int n) { return n; }
);
Пример 7.5.

Рассмотрим еще один (схематичный) запрос, записанный в синтаксисе LINQ:

IEnumerable<T> data = …;
var q =
     from x in data          
         where p ( x ) 
     orderby k ( x )
      select f ( x );
foreach ( var e in q )
       a ( e );
Пример 7.6.

Распараллеливание исполнения этого запроса снова достигается применением extension-метода AsParallel():

IEnumerable<T> data = …;
var q =
     from x in data.AsParallel()          
         where p ( x ) 
     orderby k ( x )
      select f ( x );
foreach ( var e in q )
       a ( e );
Пример 7.7.

И снова запрос из Примера 6 может быть эквивалентно переписан с использованием Enumerable:

IEnumerable<T> data = …;
var q = Enumerable.Select(
      Enumerable.OrderBy(
      Enumerable.Where(data, x => p(x)),
      x => k(x)),
          x => f(x));
foreach (var e in q)
     a(e);
Пример 7.8.

Распараллелить запрос из Примера 8 легко:

IEnumerable<T> data = …;
var q = ParallelEnumerable.Select(
      ParallelEnumerable.OrderBy(
      ParallelEnumerable.Where(data.AsParallel(), x => p(x)),
      x => k(x)),
          x => f(x));
foreach (var e in q)
     a(e);
Пример 7.9.

Запрос из Примера 8 можно переписать еще одним эквивалентным способом, который отличается тем, что в нем опущены явные указания типа Enumerable, что сокращает запрос в записи, но вызывает процедуру (неявного) вывода типов на этапе компиляции программы:

IEnumerable<T> data = …;
var q = data.Where(x => p(x)).Orderby(x => k(x)).Select(x => f(x));
foreach (var e in q) a(e);
Пример 7.10.

Распараллеливание запроса из Примера 10 снова происходит применением метода AsParallel() к источнику данных:

IEnumerable<T> data = …;
var q = data.AsParallel().
          Where(x => p(x)).Orderby(x => k(x)).Select(x => f(x));
foreach (var e in q) a(e);
Пример 7.11.
< Лекция 6 || Лекция 7: 12 || Лекция 8 >
Максим Полищук
Максим Полищук
"...Изучение и анализ примеров.
В и приведены описания и приложены исходные коды параллельных программ..."
Непонятно что такое - "В и приведены описания" и где именно приведены и приложены исходные коды.