Идентификация подпрограммы Parallelizable

Эта глава обеспечивает очень основное введение в то, как разработать программу так, чтобы ее можно было реализовать как ядро OpenCL и вызвать от узла OpenCL. Для специфических особенностей о шаблонах разработки, которые могут положительно или негативно влиять на производительность приложения, посмотрите Настраивающуюся Производительность На GPU и Улучшающуюся Производительность На CPU.

Первый шаг в использовании OpenCL для улучшения производительности приложения должен идентифицировать, какие части приложения являются подходящими для распараллеливания. Принимая во внимание, что в общем применении можно породить отдельный поток для задачи, пока функции в потоке повторно используемы, и Вы осторожны относительно того, как Вы синхронизируете доступ к данным, для достижения уровня параллелизма, для которого OpenCL идеален, для единиц работы намного более важно быть независимым друг от друга. Несмотря на то, что единицы работы в той же рабочей группе могут совместно использовать локальные данные, они выполняются синхронно и таким образом, вычисления никакой единицы работы не зависят от результата другой единицы работы. Распараллеливание работает только, когда задачи, которые Вы выполняете параллельно, не зависят друг от друга.

Например, предположите запись простого приложения, отслеживающего классы для студентов в классе. Это приложение состоит из двух основных задач:

  1. Вычислите итоговую отметку для каждого студента, предположив, что итоговая отметка для каждого студента является средним числом всех оценок, которые получил тот студент.

  2. Получите среднее число класса путем усреднения итоговых отметок всех студентов.

Вы не можете выполнить эти две задачи параллельно, потому что они весьма зависимы друг из друга: для вычисления среднего числа класса необходимо сначала вычислить итоговую отметку для каждого студента.

Несмотря на то, что Вы не можете выполнить задачу 1 и задачу 2 одновременно, существует все еще возможность для распараллеливания здесь.

Псевдокод в Перечислении 4-1 продолжается через каждого студента в классе, один за другим, вычисляя среднее число классов каждого студента и кэшируя его в студенческом объекте. Несмотря на то, что этот пример вычисляет каждый средний балл по одному, нет никакой причины, что средние баллы для всех студентов не могли быть вычислены одновременно. Поскольку классы одного студента не влияют на классы другого, можно вычислить итоговую отметку для каждого студента независимо и одновременно вместо цикличного выполнения через тот же набор инструкций для каждого студента по одному. Это - идея позади параллелизма данных.

  Псевдокод перечисления 4-1, вычисляющий итоговую отметку для каждого студента

 
// Assume 'class' is a collection of 'student' objects.
foreach(student in class)
{
    // Assume getGrades() returns a collection of integer grades.
    grades = student.getGrades();
 
    sum = 0;
    count = 0;
 
    // Iterate through each grade, adding it to sum.
    foreach(grade in grades)
    {
        sum += grade;
        count++;
    }
 
    // Cache the average grade in the student object.
    student.averageGrade = sum / count;
}

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

Выражать это программно: сначала разделите свою задачу (вычисление итоговой отметки студента) от Ваших данных (список студентов в классе). Перечисление 4-2 показывает, как можно изолировать градуирующую финал задачу.

Перечисление 4-2  изолированная задача среднего балла

task calculateAverageGradeForStudent( student )
{
    // Assume getGrades() returns a collection of integer grades.
    grades = student.getGrades();
 
    sum = 0;
    count = 0;
 
    // Iterate through each grade, adding it to sum.
    foreach(grade in grades)
    {
        sum += grade;
        count++;
    }
 
    // Store the average grade in the student object.
    student.averageGrade = sum / count;
}

Теперь, когда Вам изолировали задачу, необходимо применить ее к отдельным студентам в классе параллельно. Поскольку OpenCL имеет собственную поддержку параллельных вычислений, можно переписать задачу, показанную в Перечислении 4-2 как функция ядра OpenCL. Используя платформу OpenCL API, можно ставить в очередь это ядро для работы устройства, где каждый вычисляет модуль на устройстве, может применить экземпляр ядра (т.е. единица работы) к различному набору данных.

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