D. Предложение schedule
Параллельный регион имеет по крайней мере один барьер, в конце и может иметь дополнительные барьеры внутри него. На каждом барьере остальные члены команды должны ждать последнего потока. Чтобы свести к минимуму это время ожидания, необходимо распределить общую работу таким образом, чтобы все потоки прибыли на барьер примерно в то же время. Если некоторые из этих общих работ содержатся в for
конструкциях, schedule
предложение можно использовать для этой цели.
При наличии повторяющихся ссылок на одни и те же объекты выбор расписания для for
конструкции может определяться главным образом характеристиками системы памяти, например наличием и размером кэшей, а также временем доступа к памяти или несоединенной. Такие рекомендации могут привести к тому, что каждый поток последовательно ссылается на один набор элементов массива в ряде циклов, даже если некоторые потоки назначаются относительно меньше работы в некоторых циклах. Эту настройку static
можно выполнить с помощью расписания с одинаковыми границами для всех циклов. В следующем примере нулевое значение используется в качестве нижней границы во втором цикле, хотя k
было бы более естественным, если расписание не было бы важным.
#pragma omp parallel
{
#pragma omp for schedule(static)
for(i=0; i<n; i++)
a[i] = work1(i);
#pragma omp for schedule(static)
for(i=0; i<n; i++)
if(i>=k) a[i] += work2(i);
}
В остальных примерах предполагается, что доступ к памяти не является доминирующим фактором. Если не указано иное, предполагается, что все потоки получают сопоставимые вычислительные ресурсы. В этих случаях выбор расписания для for
конструкции зависит от всех общих работ, которые должны выполняться между ближайшим предыдущим барьером и подразумеваемым закрывающим барьером или ближайшим барьером, если есть nowait
предложение. Для каждого типа расписания короткий пример показывает, как этот тип расписания, скорее всего, будет лучшим выбором. Краткое обсуждение следует каждому примеру.
Расписание static
также подходит для простейшей ситуации, параллельной области, содержащей одну for
конструкцию, с каждой итерацией, требующей одинакового объема работы.
#pragma omp parallel for schedule(static)
for(i=0; i<n; i++) {
invariant_amount_of_work(i);
}
Расписание static
характеризуется свойствами, которые каждый поток получает примерно то же количество итерации, что и любой другой поток, и каждый поток может независимо определять итерации, назначенные ему. Таким образом, синхронизация не требуется для распределения работы, и при предположении, что каждая итерация требует одинакового объема работы, все потоки должны завершиться примерно одновременно.
Для команды потоков p пусть потолок (n/p) будет целым числом q, которое удовлетворяет n = p*q - r с 0 <= r .< Одна реализация static
расписания для этого примера будет назначать итерации q первым потокам p-1 и итерации q-r последнему потоку. Другая допустимая реализация присваивает итерации q первым потокам p-r и итерации q-1 остальным потокам r . В этом примере показано, почему программа не должна полагаться на детали конкретной реализации.
Расписание dynamic
подходит для случая for
конструкции с итерациями, требующими различных или даже непредсказуемых объемов работы.
#pragma omp parallel for schedule(dynamic)
for(i=0; i<n; i++) {
unpredictable_amount_of_work(i);
}
Расписание dynamic
характеризуется свойством, которое поток не ожидает в барьере дольше, чем требуется другому потоку для выполнения окончательной итерации. Это требование означает, что при каждом назначении необходимо назначать итерации по одному потоку по мере их доступности с синхронизацией для каждого назначения. Затраты на синхронизацию можно уменьшить путем указания минимального размера блока, превышающего 1, чтобы потоки были назначены k за раз до тех пор, пока не останется меньше k. Это гарантирует, что поток не ожидает на барьере дольше, чем требуется другой поток для выполнения окончательного фрагмента (в большинстве) итерации k .
Расписание dynamic
может быть полезно, если потоки получают различные вычислительные ресурсы, что имеет тот же эффект, что и различные объемы работы для каждой итерации. Аналогичным образом динамический график также может быть полезным, если потоки приходят в for
конструкцию в разное время, хотя в некоторых из этих случаев guided
расписание может быть предпочтительнее.
Расписание guided
подходит для случая, когда потоки могут поступать в различные периоды в for
конструкции с каждой итерацией, требующей примерно одного объема работы. Эта ситуация может произойти, если, например, конструкция for
предшествует одному или нескольким разделам или for
конструкциям с nowait
предложениями.
#pragma omp parallel
{
#pragma omp sections nowait
{
// ...
}
#pragma omp for schedule(guided)
for(i=0; i<n; i++) {
invariant_amount_of_work(i);
}
}
Например dynamic
, расписание гарантирует, guided
что поток не ожидает на барьере дольше, чем требуется другому потоку для выполнения окончательной итерации или окончательных итераций k , если указан размер блока k . Среди таких расписаний расписание характеризуется свойством, guided
которое требует наименьших синхронизаций. Для размера блока k типичная реализация будет назначать итерации q = ceiling(n/p) первому доступному потоку, задать n больше n-q и p*k, а затем повторять до тех пор, пока не будут назначены все итерации.
Если выбор оптимального расписания не так четко, как и в этих примерах, runtime
расписание удобно для экспериментирования с различными расписаниями и размерами блоков, не изменяя и перекомпилируя программу. Это также может быть полезно, если оптимальное расписание зависит (в некотором прогнозируемом способе) от входных данных, к которым применяется программа.
Чтобы просмотреть пример компромиссов между различными расписаниями, рассмотрите возможность совместного использования 1000 итераций между восемью потоками. Предположим, что в каждой итерации есть инвариантное количество работы и используйте это в качестве единицы времени.
Если все потоки начинаются одновременно, static
расписание приведет к выполнению конструкции в 125 единиц без синхронизации. Но предположим, что один поток составляет 100 единиц в конце прибытия. Затем оставшиеся семь потоков ожидают 100 единиц в барьере, а время выполнения для всей конструкции увеличивается до 225.
dynamic
Так как оба плана guided
и расписания не должны ожидать более одной единицы на барьере, задержка потока приводит к увеличению времени выполнения конструкции только до 138 единиц, возможно, увеличением задержки синхронизации. Если такие задержки не являются незначительными, важно, что число синхронизаций равно 1000, dynamic
но только 41 для guided
, если размер блока по умолчанию равен одному. С размером блока 25, dynamic
и guided
оба заканчиваются в 150 единицах, а также любые задержки от необходимых синхронизаций, которые теперь число только 40 и 20 соответственно.