Паралельне програмування для багатоядерних систем за допомогою OpenMP








Історія


Самим значущим подією 2005 року стала поява багатоядерних процесорів. До того часу класичні одноядерні системи вичерпали свій потенціал, оскільки не давали потрібної продуктивності за рахунок підвищення частот, мало того розробники архітектур зіткнулися з проблемою нестабільності і підвищеного тепловиділення. Все це волею-неволею сприяло появі революційних багатоядерних процесорів. Ідея цих процесорів гранично проста: в одному процесорі інтегровано більше одного ядра. Такий підхід сприяє появі більш продуктивних процесорів без підвищення частот.

OpenMP


Тим не менш, на сьогоднішній день не так вже й багато додатків (особливо ігор), які повноцінно використовують багатопроцесорні системи. Якщо ваша заявка не використовує кілька ядер, то відповідно і приросту швидкодії не буде. Саме тут з'являється на сцені OpenMP, технологія, за допомогою якої можна легко і швидко створювати мультіпоточние програми на C + + і відповідно істотно підвищити продуктивності. Дана технологія була розроблена у 1997 році і спочатку базувалася на мові Fortran. Сьогодні вона включена в мову C + + і доступна в Visual Studio 2005 (Версія 2.0), а також на платформі XBOX 360.
Перш ніж приступати до коду, потрібно активізувати реалізовані в компіляторі кошти OpenMP. Для цього у властивостях проекту C / C + + -> Language -> OpenMP ставимо Yes (/ openmp). Зустрівши параметр / openmp компілятор визначає символ _OPENMP, за допомогою якого ми зможемо визначити чи використовується дана технологія в нашому застосуванні чи ні. Так само, щоб уникнути помилок, до проекту слід підключити хедер omp.h, який містить потрібні нам функції з vcomp.dll (або vcompd.dll в перевіреній версії, яка містить додаткові повідомлення про помилки), маніфести і т.п.

“Helloworld”


OpenMP досить простий у використанні, він складається з набору прагм і функцій з omp.h. Прагма – це покажчики компілятору розбивати код на блоки, які будуть виконуватися паралельно. Якщо openmp вимкнений, компілятор проігнорує Прагма, а код залишиться цілком працездатним. Все це дає можливість писати універсальний, стерпний код для реалізації програм на різних архітектурах та системах. Специфікація OpenMP підтримується такими постачальниками як Sun, Intel і IBM.
Всі директиви починаються з # pragma omp. Найважливіша і поширена директива – parallel. Вона створює паралельний регіон для наступного за нею блоку. В якості прикладу розглянемо класичний "Hello world”:
Код:
int main()
{
 #pragma omp parallel
{
 printf(“Hello world
“);
}
return 0;
}
При виконанні на двоядерному системі ми отримаємо результат:

Код:
Hello world
Hello world

Розглянемо більш складний приклад, в якому використовуємо конструкцію # pragma omp for, яка повідомляє, що при виконанні циклу for в паралельному регіоні ітерації циклу повинні бути розподілені між потоками групи:
Код:
#pragma omp parallel
{
#pragma omp for
 for(int i < 0; i < num_Particles; i++) {
        Action(vec[i]);
}
}
Відзначимо дуже важливе зауваження: якщо прибрати з конструкції # pragma omp for, то кожен потік повністю виконає цикл, таким чином буде виконано багато непотрібної роботи. У OpenMP також є можливість більш короткої записи прагм # pragma omp parallel і # pragma omp for:
 
Код:
# Pragma omp parallel for for (int i <0; i <num_Particles; i + +) {Action (vec [i]);
}

Бар'єри та nowait


Бар'єри є одним з видів синхронізації. При виявленні бар'єру потік зупиняється і чекає поки всі інші потоки не опиняться на цьому ж бар'єрі. Наприклад, при виконанні циклу вище ми зіткнулися з неявній бар'єрної синхронізацією, самі не помітивши того. В кінці циклу стоїть бар'єр, який призупиняє виконання звільнилися потоків до тих пір, поки не звільняться інші потоки, обробні ітерації цього ж циклу. У деяких випадках виникає потреба відключати синхронізацію, для цього існує оператор nowait, наведемо приклад, де це може бути вигідно:
Код:
#pragma omp parallel
{
#pragma omp for nowait
            for(int i = 0; i < 5000; i++) {
                        // …
            }
#pragma omp for
            for(int j = 0; j < 1000; j++) {
                        // …
            }
#pragma omp barrier
             some_func();
} У цьому прикладі потоки, які звільнилися після обробки першого циклу переходять до обробки другого циклу без очікування інших потоків. У деяких випадках це може підвищити продуктивність, оскільки зменшується час простою потоків. Можлива і зворотна ситуація, коли нам потрібно організувати бар'єр. В наведеному вище прикладі функція some_func () не виконається до тих пір, поки всі потоки не виконають попередні завдання (це потрібно, наприклад, при оновленні буфера кадрів перед виведенням його вмісту на екран).

Обмеження на цикли


У OpenMP існує ряд обмежень на цикли, які потрібно распараллеліть:
1.Переменная циклу повинна мати тип integer. Беззнакові цілі числа, такі як DWORD, працювати не будуть.
2.Цікл повинен бути базовим блоком і не може використовувати goto і break (за винятком оператора exit, який завершує всі додаток).
3.Інкрементная частина циклу for повинно бути або цілочисловим складанням, або цілочисловим вирахуванням і повинно практично збігатися із значенням інваріанта циклу.
4.Якщо використовується операція порівняння <або <=, мінлива циклу повинна збільшуватися при кожній ітерації, а при використанні операції> або> = мінлива циклу повинна зменшуватися.

Критичні розділи


За допомогою критичних розділів можна запобігти одночасний доступ до одного сегмента коду з декількох потоків. Один потік отримує доступ тільки тоді, коли інші не обробляють даний код. Конструкція має наступний вигляд:
Код:
#pragma omp critical
{
     …
} Такі критичні розділи називаються неіменованим. Існують також іменовані, які сприяють підвищенню продуктивності додатка, оскільки блокування буде проводитися тільки тими потоками, яким це дійсно необхідно. Виглядає таким чином:

Код:
#pragma omp critical(some_value)
{
      …
}

Конструкція Sections


OpenMP можна застосувати не тільки для циклів, але і для функцій. Конструкція sections використовується коли необхідно виконувати блоки коду в окремих потоках. Розглянемо найпростіший приклад (сортування):
Код:
#pragma omp parallel sections
{
            #pragma omp section
            sort(array, …);
            #pragma omp section
            sort(array, …);
}
У даному прикладі перший директива # pragma створює паралельний регіон секцій, а наступні секції визначаються директивою # pragma omp section. Кожній секції в паралельному регіоні ставиться у відповідність один потік з групи потоків, і всі секції виконуються одночасно. Зауважимо, що кожна з функцій sort виконується повністю незалежно один від одного, це дуже важливо, оскільки призводить до тривіальним помилок, які дуже важко відловити. Розглянемо приклад неправильний код:
Код:
int n, m, q;
#pragma omp parallel sections
{
            #pragma omp section
            n = partition(array, lo, hi);
            #pragma omp section
m = partition (array, lo, n-1); / / n можливо не визначено!!
            #pragma omp section
q = partition (array, n +1, hi); / / n можливо не визначено!!
} При компіляції програми без параметра / openmp буде згенерована коректна послідовна версія. Одна з переваг OpenMP в тому, що ця технологія сумісна з компіляторами, що не підтримують OpenMP.
Висновки
OpenMP швидка, проста і легка технологія з допомогою якої можна використовувати потенціал багатоядерних систем. Всього за кілька хвилин звичайний код можна переробити в оптимізований, при цьому залишається сумісність з компіляторами, які не підтримують дану технологію. Можливе застосування в ігрових движках: particle system, collision detection, fractals, пошук шляхів і т. д. На жаль, в даній статті ми коротко розглянули лише малу частину можливостей даної технології, але більш докладну специфікацію ви можете знайти на сайті http://www.openmp.org.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*