Skip to content

Latest commit

 

History

History
162 lines (115 loc) · 15.8 KB

for.md

File metadata and controls

162 lines (115 loc) · 15.8 KB

for

Бесконечный цикл — это дать сонному человеку треугольное одеяло.

Автор неизвестен

Директива for специфицирует итерационную конструкцию разделения работ, которая определяет область, в которой итерации соотвествующего цикла будут выполняться параллельно. Синтаксис конструкций for следующий:

#pragma omp for [клауза [ клауза]]
    цикл for
  • private (список)
  • firstprivate (список)
  • lastprivate (список)
  • reduction (оператор : список)
  • ordered
  • schedule(вид [, длина_порции])
  • nowait

Директива for накладывает ограничения на структуру соотвествующего цикла. Определенно, соответствующий цикл должен иметь каноническую форму:

for ( выражение ; var логическая_операция b; приращение )

выражение одно из следующих:

    var = lb
    integer-type var = lb

приращение одно из следующих:

    ++var
    var++
    --var
    var--
    var += incr
    var -= incr
    var = var + incr
    var = incr + var
    var = var - incr

var - целое со знаком. Если эта переменная предпологалась быть разделяемой, то она неявно становится приватной в течении выполнения цикла. Переменная не должна модефицироваться внутри тела оператора цикла. Если только переменная не определена как lastprivate, то после выполнения цикла её значение не определено.

логическая_операция одна из следующих:

    <
    <=
    >
    >=

lb, b и incr инвариантные относительно цикла выражения. Вычисления выражения осуществляется без какой-либо синхронизации. Таким образом, любые побочные эффекты при вычислениях приводят к неопределённым результатам.

Следует отметить, каноническая форма позволяет вычислить число итераций цикла еще при входе в цикл. Это вычисление выполняется со значением типа переменной var после целочисленного переобразования типов(integral promontions). В частности, если значение b - lb + incr не может быть представлено в этом типе, то результат не определен. Так же var может быть итератором.

Для доступа к элементам некоторого множества элементов алгоритмы STL используют специальные объекты, называемые итераторами. В контейнерных типах STL они доступны через методы класса (например, begin() в шаблоне класса vector). Функциональные возможности указателей и итераторов близки, так что обычный указатель тоже может использоваться как итератор.

Клауза schedule определяет, каким образом итерации цикла делятся между потоками группы. Правильность программы не должно зависеть от того, какой поток выполняет конкретную итерацию. Значение порции, если оно определено, дожно быть инвариантным относительно цикла положительным целочисленным выражением. Вычисление выражения осуществляется без какой-либо синхронизации.

Вид планирования может быть одним из следующих:

  • static

Eсли определена клауза schedule(static, длина_порции) , то итерации делятся на порции длинной, равной длина_порции. Порции статически назначаются потокам в группе циклическим образом в порядке их номеров. Если размер порции определён, то всё количество итераций делится на порции, которые приблизительно равны между собой, и каждому потоку назначается одна порция.

#pragma omp parallel for schedule (static)
for ( i=0; i<n; i++ )
{
    одинаковый_объём_работы(i);
}

Статическое планирование характеризуется свойством, при котором каждый поток получает примерно одно число итераций, как и любой другой, и поэтому каждый поток независимо определяет число итераций присвоенное этому потоку. Итак, нет необходимости в синхронизации в распределении работы и, при предположении, что на каждую итерацию потребуется одинаковый объём работы, все потоки дожны завершить этот объём работ примерно в одно и тоже время.

Только при данном режиме планирования возможно применение nowait для зависимых по данным циклам.

#pragma omp parallel shared(n,a,b,c,d,sum) private(i) schedule(static)
{
   #pragma omp for nowait
   for (i = 0; i < n; i++)
   a[i] += b[i];

   #pragma omp for nowait
   for (i = 0; i < n; i++)
   c[i] += d[i];

   #pragma omp for nowait reduction(+:sum)
   for (i = 0; i < n; i++)
   sum += a[i] + c[i];
}

В зависимых по данным циклы nowait допустим только c schedule(static). Только в этом способе планирования работ стандарт гарантирует корректную работу c nowait для циклов зависимых по данным. В нашем случае зависимость массивов a[] c[] и последнего цикла.

  • dynamic

Если определена клауза schedule(dynamic, длина_порции) , то порции итераций длиной длина_порции назначаются каждому потоку. Когда поток завершит присвоенную ей порцию итераций, ей динамически назначается другая порция, пока все порции не закончатся. Следует заметить, что последняя назначаемая порция может иметь меньшее число итераций. Если параметр длина_порции не определен, то значение по умолчанию равно 1.

#pragma omp parallel for schedule (dynamic)
for ( i=0; i<n; i++ )
{
    непрогнозируемый_объём_работы(i);
}

Динамический способ планирования характеризуется тем свойством, что нити не ожидают в точке синхронизации дольше, чем другой поток потребовалось бы завершить выполнение её последней итерации. Это требует того, чтобы итерации назначали потокам, как только они становятся доступными с синхронизацией при каждом присвоении. Накладные расходы по синхронизации могут быть сокращены определением минимального размера порции k более чем 1, так что потокам назначается k итераций каждый раз, пока не останется менее kитераций. Это гарантирует, что не будет потоков в точке синхронизации, ожидающи дольше, чем однин из потоков выполнит свою окончательную порцию (не более чем k) итераций.

Динамическое планирование может быть полезным, если потоки получают изменяющийся вычислительный ресурс, который имеет такой же эффект, как изменяющийся объём работ для каждой итерации. Также динамический способ планирования может оказаться полезным, если потоки достигают конструкции for в различные времена, хотя в некоторых случаях способ планирования guided может оказаться предпочтительнее.

  • guided

Если клауза schedule(guided, длина_порции) определена, то итерации назначаются потокам порциями уменьшающихся размеров. Когда поток завершает выполнение назначенной ей порции итераций, то динамически назначается другая порция до тех пор, пока их не останется. Если значение длина_порции равно 1, то длина порции примерна равна частному от деления числа не назначенных итераций на число потоков. Длина порции уменьшается показательным образом до 1. Если длина_порции равна k (k > 1), то размеры порций уменьшаются показательно до к, за исключением того, что последняя порция может иметь меньше чем k итераций. Если параметр длина_порции не определен, то значение по умолчанию равно 1.

#pragma omp parallel for schedule (dynamic)
for ( i=0; i<n; i++ )
{
    инвариантный_объём_работы(i);
}

Способ планирования guided, подходящий для случаев, в котором потоки могут достигать конструкции for в различные времена, и каждая итерация требует примерно одинакового объёма работ. Это может случаться, например, если конструкция for предшествует одна или несколько секций или конструкция for использована с клаузой nowait.

Подобно динамическому планированию, планирование способом guided гарантирует, что в точке синхронизации потоки находятся в сосотоянии ожидания не дольше, чем один из потоков выполнит свою окончательную итерацию, или k итераций, если размер порции определен равным k. Среди этих способов планирование по способу guided характеризуется таким свойством, что оно требует наименьшей синхронизации.

  • auto

Если клауза schedule(auto) определена, то решение по назначении размера порций и планированию итераций делегировано компилятору или определяется во время выполнения. Тем самым программист дает свободу выбора отображения итераций на количество потоков группы. В этой клаузе не указывается параметр, определяющий размер порции.

  • runtime

Если клауза schedule(runtime) определена, то заключение относительно распределения работ откладывается до времени выполнения. Вид распределения и размер порции могут быть выбраны во время выполнения путем установки переменной среды окружения OMP_SCHEDULE. Если переменная среды не установлена, то окончательное планирование зависит от реализации. В этой клаузе не указывается параметр, определяющий размер порции.

Когда выбор оптимального способа планирования не очевиден, то способ runtime удобен для экспериментирования с различными способами планирования и размерами порции без модификаций и перетрасляции программы. Это может быть полезным и в тех случаях, когда оптимальное планирование зависит некоторым предсказуемым способом от входных данных, с которыми программа используется.

Если клауза nowait не указана, то конструкция for неявно завершится барьерной синхронизацией. В конце параллельного цикла происходит неявная барьерная синхронизация параллельно работающих потоков: их дальнейшее выполнение происходит только тогда, когда все они достигнут данной точки; если в подобной задержке нет необходимости, опция nowait позволяет потокам, уже дошедшим до конца цикла, продолжить выполнение без синхронизации с остальными. Пример использования клаузы nowait:

#pragma omp parallel 
{
    #pragma omp for nowait
    for ( i=1; i<n; i++ )
        c[i] = ( a[i] + a[i-1] ) / 2.0;
    #pragma omp for nowait
    for ( i=1; i<n; i++ )
        y[i] = sqrt( z[i] );
}