Опубликован: 15.10.2009 | Уровень: специалист | Доступ: платный
Лекция 4:

Конструкция Parallel.Invoke

< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Аннотация: В лекции рассматривается еще один способ распараллеливания процессов - Parallel.Invoke, а также описаны проблемы при реализации параллельной обработки бинарных деревьев.

В "Лекции 2" были рассмотрены параллельные реализации циклов For и ForEach. Еще один способ распараллеливания, поддерживаемый классом Parallel - это метод Parallel.Invoke.

Статический метод Invoke позволяет распараллелить исполнение блоков операторов. Часто в приложениях существуют такие последовательности операторов, для которых не имеет значения порядок выполнения операторов внутри них. В таких случаях вместо последовательного выполнения операторов одного за другим, возможно их параллельное выполнение, позволяющее сократить время решения задачи.

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

class Tree<T>
{
public T Data;
public Tree<T> Left, Right;
…
}

На C# обход дерева в последовательной реализации может выглядеть следующим образом:

static void WalkTree<T>(Tree<T> tree, Action <T> func)
{
  if (tree == null) return;
  WalkTree(tree.Left, func);
  WalkTree(tree.Right, func);
  func(tree.Data);
}

Заметим, что в последовательной реализации, имеется группа операторов, выполняющих обход обоих ветвей дерева и работающая с текущим узлом. Если наша цель - выполнить это действие для каждого узла в дереве и если порядок, в котором эти узлы будут обслужены неважен, то мы можем распараллелить исполнение группы таких операторов, используя Parallel.Invoke. Параллельная реализация выглядит следующим образом:

static void WalkTree<T>(Tree<T> tree, Action <T> func)
{
  if (tree = null) return;
Parallel.Invoke(
    () => WalkTree(tree.Left, func);
    () => WalkTree(tree.Right, func);
    () => func(tree.Data));
}

Только что показанный способ распараллеливания применим и к другим алгоритмам типа "разделяй и властвуй". Рассмотрим последовательную реализацию алгоритма быстрой сортировки:

static void SeqQuickSort<T>(T[] domain, int left, int right)
where T : IComparable<T>
{
  if (right - left + 1 <= INSERTION_TRESHOLD)
{
  InsertionSort(domain, left, right);
}
else
{
  int pivot = Partition(domain, left, right);
  SeqQuickSort(domain, left, pivot - 1);
SeqQuickSort(domain, pivot + 1, right);
}
}

Также как в предыдущем примере, распараллеливание может быть выполнено посредством метода Parallel.Invoke:

static void ParQuickSort<T> (T[] domain, int left, int right)
where T : IComparable<T>
{
  if (right - left + 1 <= SEQUENTIAL_TRESHOLD)
{
  SeqQuickSort (domain, left, right);
}
else
{
  int pivot = Partition(domain, left, right);
Parallel.Invoke(
    () => SeqQuickSort(domain, left, pivot - 1);
() => SeqQuickSort(domain, pivot + 1, right));
}
}

Заметим, что в последовательной реализации SeqQuickSort, если размер сортируемого сегмента массива достаточно мал, то алгоритм вырождается в алгоритм сортировки вставкой ( InsertionSort ). Для очень больших массивов алгоритм быстрой сортировки значительно эффективнее, чем простые алгоритмы сортировки (сортировка вставкой, метод пузырька, сортировка выборкой) . Будем использовать эту идею и при параллельной реализации. Массив очень большого размера, поступающий на вход, разделяется на сегменты, которые обрабатываются параллельно. Однако для массива небольшого размера дополнительные издержки на обслуживание потоков могут привести к потере производительности. Итак, если для массивов небольшого размера SeqQuickSort вырождается в InsertionSort, то в параллельном варианте ParQuickSort выраждается в SeqQuickSort. Аналогичным способом можно организовать только что рассмотренный обход бинарного дерева, чтобы уменьшить потери производительности:

static void WalkTree<T> (Tree<T> tree, Action<T> func, int depth)
{
  if (tree = null) return;
else if (depth > SEQUENTIAL_TRESHOLD)
{
    WalkTree(tree.Left, func, depth + 1);
    WalkTree(tree.Right, func, depth + 1);
    func(tree.Data);
}
else
{
Parallel.Invoke(
      () => WalkTree(tree.Left, func, depth + 1);
      () => WalkTree(tree.Right, func, depth + 1);
      () => func(tree.Data));
}
}
< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Максим Полищук
Максим Полищук
"...Изучение и анализ примеров.
В и приведены описания и приложены исходные коды параллельных программ..."
Непонятно что такое - "В и приведены описания" и где именно приведены и приложены исходные коды.
Дмитрий Молокоедов
Дмитрий Молокоедов
Россия
Паулус Шеетекела
Паулус Шеетекела
Россия

( ! ) Warning: include_once(./includes/unicode.entities.inc) [<a href='function.include-once'>function.include-once</a>]: failed to open stream: No such file or directory in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8307102088712watchdog( )../bootstrap.inc:0
23.8310102091184module_invoke( )../bootstrap.inc:967
33.8310102093040call_user_func_array ( )../module.inc:462
43.8310102093376devel_watchdog( )../module.inc:462
53.8311102094224decode_entities( )../devel.module:382
63.8311102096144drupal_error_handler( )../devel.module:340
73.8311102099752watchdog( )../common.inc:663
83.8312102101824module_invoke( )../bootstrap.inc:967
93.8312102103680call_user_func_array ( )../module.inc:462
103.8312102104016devel_watchdog( )../module.inc:462
113.8312102104712decode_entities( )../devel.module:382

( ! ) Warning: include_once() [<a href='function.include'>function.include</a>]: Failed opening './includes/unicode.entities.inc' for inclusion (include_path='.:/usr/local/zend/var/libraries/Zend_Framework_1/default/library:/usr/local/zend/share/pear') in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8307102088712watchdog( )../bootstrap.inc:0
23.8310102091184module_invoke( )../bootstrap.inc:967
33.8310102093040call_user_func_array ( )../module.inc:462
43.8310102093376devel_watchdog( )../module.inc:462
53.8311102094224decode_entities( )../devel.module:382
63.8311102096144drupal_error_handler( )../devel.module:340
73.8311102099752watchdog( )../common.inc:663
83.8312102101824module_invoke( )../bootstrap.inc:967
93.8312102103680call_user_func_array ( )../module.inc:462
103.8312102104016devel_watchdog( )../module.inc:462
113.8312102104712decode_entities( )../devel.module:382

( ! ) Warning: include_once(./includes/unicode.entities.inc) [<a href='function.include-once'>function.include-once</a>]: failed to open stream: No such file or directory in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8307102088712watchdog( )../bootstrap.inc:0
23.8310102091184module_invoke( )../bootstrap.inc:967
33.8310102093040call_user_func_array ( )../module.inc:462
43.8310102093376devel_watchdog( )../module.inc:462
53.8311102094224decode_entities( )../devel.module:382
63.8318102096336drupal_error_handler( )../devel.module:340
73.8318102100024watchdog( )../common.inc:663
83.8318102102096module_invoke( )../bootstrap.inc:967
93.8319102103952call_user_func_array ( )../module.inc:462
103.8319102104288devel_watchdog( )../module.inc:462
113.8319102105064decode_entities( )../devel.module:382

( ! ) Warning: include_once() [<a href='function.include'>function.include</a>]: Failed opening './includes/unicode.entities.inc' for inclusion (include_path='.:/usr/local/zend/var/libraries/Zend_Framework_1/default/library:/usr/local/zend/share/pear') in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8307102088712watchdog( )../bootstrap.inc:0
23.8310102091184module_invoke( )../bootstrap.inc:967
33.8310102093040call_user_func_array ( )../module.inc:462
43.8310102093376devel_watchdog( )../module.inc:462
53.8311102094224decode_entities( )../devel.module:382
63.8318102096336drupal_error_handler( )../devel.module:340
73.8318102100024watchdog( )../common.inc:663
83.8318102102096module_invoke( )../bootstrap.inc:967
93.8319102103952call_user_func_array ( )../module.inc:462
103.8319102104288devel_watchdog( )../module.inc:462
113.8319102105064decode_entities( )../devel.module:382

( ! ) Warning: include_once(./includes/unicode.entities.inc) [<a href='function.include-once'>function.include-once</a>]: failed to open stream: No such file or directory in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8325102089256watchdog( )../bootstrap.inc:0
23.8325102091328module_invoke( )../bootstrap.inc:967
33.8325102093184call_user_func_array ( )../module.inc:462
43.8325102093520devel_watchdog( )../module.inc:462
53.8326102094224decode_entities( )../devel.module:382
63.8326102096144drupal_error_handler( )../devel.module:340
73.8326102099752watchdog( )../common.inc:663
83.8326102101824module_invoke( )../bootstrap.inc:967
93.8326102103680call_user_func_array ( )../module.inc:462
103.8326102104016devel_watchdog( )../module.inc:462
113.8327102104712decode_entities( )../devel.module:382

( ! ) Warning: include_once() [<a href='function.include'>function.include</a>]: Failed opening './includes/unicode.entities.inc' for inclusion (include_path='.:/usr/local/zend/var/libraries/Zend_Framework_1/default/library:/usr/local/zend/share/pear') in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8325102089256watchdog( )../bootstrap.inc:0
23.8325102091328module_invoke( )../bootstrap.inc:967
33.8325102093184call_user_func_array ( )../module.inc:462
43.8325102093520devel_watchdog( )../module.inc:462
53.8326102094224decode_entities( )../devel.module:382
63.8326102096144drupal_error_handler( )../devel.module:340
73.8326102099752watchdog( )../common.inc:663
83.8326102101824module_invoke( )../bootstrap.inc:967
93.8326102103680call_user_func_array ( )../module.inc:462
103.8326102104016devel_watchdog( )../module.inc:462
113.8327102104712decode_entities( )../devel.module:382

( ! ) Warning: include_once(./includes/unicode.entities.inc) [<a href='function.include-once'>function.include-once</a>]: failed to open stream: No such file or directory in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8325102089256watchdog( )../bootstrap.inc:0
23.8325102091328module_invoke( )../bootstrap.inc:967
33.8325102093184call_user_func_array ( )../module.inc:462
43.8325102093520devel_watchdog( )../module.inc:462
53.8326102094224decode_entities( )../devel.module:382
63.8331102096336drupal_error_handler( )../devel.module:340
73.8332102100024watchdog( )../common.inc:663
83.8332102102096module_invoke( )../bootstrap.inc:967
93.8332102103952call_user_func_array ( )../module.inc:462
103.8332102104288devel_watchdog( )../module.inc:462
113.8332102105064decode_entities( )../devel.module:382

( ! ) Warning: include_once() [<a href='function.include'>function.include</a>]: Failed opening './includes/unicode.entities.inc' for inclusion (include_path='.:/usr/local/zend/var/libraries/Zend_Framework_1/default/library:/usr/local/zend/share/pear') in /.2/var_www_new.intuit.ru/htdocs/includes/unicode.inc on line 340
Call Stack
#TimeMemoryFunctionLocation
13.8325102089256watchdog( )../bootstrap.inc:0
23.8325102091328module_invoke( )../bootstrap.inc:967
33.8325102093184call_user_func_array ( )../module.inc:462
43.8325102093520devel_watchdog( )../module.inc:462
53.8326102094224decode_entities( )../devel.module:382
63.8331102096336drupal_error_handler( )../devel.module:340
73.8332102100024watchdog( )../common.inc:663
83.8332102102096module_invoke( )../bootstrap.inc:967
93.8332102103952call_user_func_array ( )../module.inc:462
103.8332102104288devel_watchdog( )../module.inc:462
113.8332102105064decode_entities( )../devel.module:382