В общем, пример 1 является лучшим, поскольку он распараллеливает самый внешний цикл, что сводит к минимуму накладные расходы на ветку / соединение потока.Хотя многие реализации OpenMP предварительно выделяют пул потоков, все еще существуют накладные расходы на отправку логических задач рабочим потокам (так называемая команда потоков) и их присоединение.Также обратите внимание, что когда вы используете динамическое планирование (например, schedule(dynamic, 1)
), тогда это накладные расходы на диспетчеризацию задачи будут проблематичными.
Так, пример 2 может повлечь за собой значительные параллельные издержки, особенно когда счетчик поездок for-i
большой (хотя 100 - это нормально), а объем рабочей нагрузки for-j
небольшой. Маленький может быть неоднозначным термином и зависит от многих переменных.Но менее 1 миллисекунды было бы определенно расточительно для использования OpenMP.
Однако, в случае, когда for-i
не распараллеливается и только for-j
распараллеливается, тогда Example2 является единственным вариантом.В этом случае вы должны тщательно продумать, может ли величина параллельной рабочей нагрузки компенсировать параллельные накладные расходы.
Example3 полностью допустим, если for-i
и for-j
безопасно распараллеливаемы (т. Е. Нет потоковых зависимостей, переносимых циклом)в каждые две петли соответственно).Пример 3 называется вложенный параллелизм .Вы можете посмотреть эту статью .Вложенный параллелизм следует использовать с осторожностью.Во многих реализациях OpenMP вам нужно вручную включить вложенный параллелизм, вызвав omp_set_nested
.Однако, поскольку вложенный параллелизм может порождать огромное количество потоков, его преимущество может быть значительно уменьшено.