Опубликован: 23.04.2013 | Доступ: свободный | Студентов: 854 / 184 | Длительность: 12:54:00
Лекция 8:

Распараллеливание циклов. Класс Parallel

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >

Вычисление интеграла и Parallel.For

Проведем еще один заключительный эксперимент эффективности применения Parallel.For в сравнении с другими методами распараллеливания. Для этого вернемся к уже хорошо знакомой задаче вычисления определенного интеграла, рассмотренной в предыдущих главах. В "Параллельные алгоритмы" мы начали проектировать класс NewIntegral, добавляя в него в последующих главах различные методы, позволяющие вычислить интеграл. Добавим еще один метод, в котором для распараллеливания циклов используется вызов Parallel.For:

public void ParallelIntegralWithParallelFor()
        {
            Parallel.For(0, p, EvalIntegral);
            result = 0;
            for (int i = 0; i < p; i++)
            {
                result += results[i];
            }
        }

Это самое простое и самое понятное решение. Цикл состоит из одной строчки. В цикле вызывается метод EvalIntegral, которому передается индекс итерации цикла. В методе EvalIntegral формируются границы интервала интегрирования, и вызывается последовательный метод, осуществляющий интегрирование на заданном отрезке. Вот код этого метода:

void EvalIntegral(int i)
        {
            double dx = (b - a) / p;            
            double start, finish;
            start = a + i * dx;
            finish = start + dx;
            DefiniteIntegral(start, finish, out results[i]);
        }

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

Вычисление интеграла разными методами

увеличить изображение
Рис. 7.16. Вычисление интеграла разными методами

В этом эксперименте все параллельные методы в 4-5 раз эффективнее последовательных методов. Лучший результат в это раз показал метод, использующий потоки. Заметьте, при сравнительно большом числе потоков этот метод работает хорошо при длинных итерациях, как в данном случае. Тем не менее, для распараллеливания циклов следует применять метод Parallel.For как наиболее простой, интуитивно понятный метод, соответствующий привычному для нас оператору for.

Цикл while и Parallel.For

До сих пор, говоря о распараллеливании циклов, мы рассматривали исключительно цикл типа for. Более общей формой цикла является форма с циклом while:

while (B) { body }

Как распараллелить такой цикл, когда заголовок цикла не определяет число итераций, требуемых для завершения цикла? Пример на эту тему у нас уже встречался, когда мы рассматривали числа-градины. Там же, по существу, дано и решение возникающей проблемы. Решение основано на возможности использования оператора break в параллельно выполняемых итерациях цикла. Условие выхода (B) проверяется в ходе выполнения итерации и при его истинности осуществляется прерывание выполнения исполняемых итераций. При этом обеспечивается возможность выяснения наименьшего индекса итерации, для которого выполняется условие выхода. Подробная семантика процесса прерывания уже описана в этой главе. Давайте рассмотрим схему замены цикла while параллельным циклом Parallel.For. Она выглядит следующим образом:

ParallelLoopResult res;
  //Параллельный запуск итераций
   res = Parallel.For(0, N, body);
  //минимальный индекс итерации, на которой выполняется условие завершения          
  int index = res.LowestBreakIteration;
  if (res.IsCompleted)
  //выход по достижению максимума итераций
  else 
  //выход по условию цикла while

Тело цикла оформляется как метод, которому передаются два параметра - индекс текущей итерации и параметр класса ParallelLoopState:

void body(int i, ParallelLoopState pls)
{
  //начальная часть тела цикла
  …
  //проверка условия выхода
  if (B)
    pls.Break();
  //завершающая часть тела цикла
  …
}

Остается вопрос, требующий решения, - как задать параметр N. Чаще всего, известно максимально возможное число итераций. Например, в классической задаче поиска по образцу, использующей цикл while, число итераций не может превышать числа элементов массива. В других задачах это число выбирается из каких-то внешних предположений, как например в задаче о числах-градинах, нас интересовал ответ для некоторого фиксированного интервала чисел. Например, для плохо сходящихся процессов разумно задавать некоторое максимальное число итераций, при достижении которого процесс прекращается. Так что число N в этой схеме - это число, ограничивающее максимальное число итераций. Наша схема всегда позволяет выяснить, как закончился цикл - либо по достижению максимума итераций, либо по достижению истинности условия B на итерации. В последнем случае известен минимальный индекс итерации, на котором это условие стало истинным.

Подводя итог, можно сказать, что циклы while также могут быть достаточно просто распараллелены. Как всегда, главная проблема состоит в обеспечении независимости итераций цикла. Ответственность за решение этой проблемы несет программист.

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >
Алексей Рыжков
Алексей Рыжков

не хватает одного параметра:

static void Main(string[] args)
        {
            x = new int[n];
            Print(Sample1,"original");
            Print(Sample1P, "paralel");
            Console.Read();
        }

Никита Белов
Никита Белов

Выставил оценки курса и заданий, начал писать замечания. После нажатия кнопки "Enter" окно отзыва пропало, открыть его снова не могу. Кнопка "Удалить комментарий" в разделе "Мнения" не работает. Как мне отредактировать недописанный отзыв?