Українська
Редукція, сортування та продуктивність
Паралельна редукція та префіксна сума
Редукція (reduction) – згортання набору даних в одне значення асоціативною операцією
Рис. 6.5. Дерево паралельної редукції
Загальна робота (work, кількість операцій) залишається localInit/localFinally і Aggregate.
Префіксна сума (prefix sum, scan) обчислює всі проміжні результати: для s[i] = s[i - 1] + x[i] має залежність між ітераціями, проте задачу можна розпаралелити алгоритмом Блеллока (Blelloch scan) для масиву довжиною
- Підйом (up-sweep) – це дерево редукції, побудоване на місці: на рівні з кроком
для кожної пари виконуєтьсяa[i + s - 1] += a[i + s/2 - 1]. Після підйому останній елемент містить суму всього масиву. - Спуск (down-sweep): останній елемент замінюють нейтральним елементом (0), далі на рівнях з кроком
для кожної пари лівий елемент отримує значення правого (батька), а правий – суму батька і старого значення лівого.
Після спуску масив містить виключну префіксну суму. Робота алгоритму – близько
Паралельні алгоритми сортування
Сортування – класичний приклад, у якому паралельність поєднує паралелізм даних і задач.
Сортування злиттям
Сортування злиттям (merge sort) ділить масив навпіл, сортує половини й зливає їх. Половини незалежні, тому їх сортують паралельно, наприклад через Parallel.Invoke (рис. 6.6). Дві важливі деталі:
- поріг (cutoff): фрагменти, менші за поріг (тисячі–десятки тисяч елементів), сортують послідовно, бо створення задач для малих фрагментів дорожче за саму роботу;
- обмеження глибини: нові задачі створюють лише на верхніх
рівнях рекурсії, далі рекурсія послідовна.
Рис. 6.6. Паралельне сортування злиттям
Останнє злиття виконується одним потоком і переглядає весь масив, тому за законом Амдала (тема 1) прискорення обмежене: у прикладі «Паралельне сортування злиттям» на 16 логічних процесорах воно становить лише 4–6. Для більшого прискорення зливають також паралельно: середній елемент однієї половини шукають бінарним пошуком у другій, і дві частини зливають незалежно.
Швидке сортування (quicksort) розпаралелюють так само: розділення опорним елементом послідовне, а дві частини сортуються паралельно задачами з тим самим порогом і обмеженням глибини. Частини можуть бути дуже нерівними, тому масштабованість гірша.
Парно-непарне сортування перестановками
Парно-непарне сортування перестановками (odd–even transposition sort) – паралельна версія сортування бульбашкою. Масив з
Sample sort
Sample sort масштабується найкраще на великій кількості процесорів і в кластерах (тема 12): з масиву вибирають і сортують випадкову вибірку; з неї беруть
Інші шаблони паралелізму даних
- Паралельний пошук.
Parallel.ForзіStop()знаходить будь-який елемент, зBreak()– перший; у PLINQ –Any,First(зAsOrdered). Якщо шуканий елемент на початку, послідовний пошук може виявитися швидшим. - Гістограма. Локальна гістограма для кожного розділу (
localInit) і злиття вlocalFinally; спільний масив ізInterlocked.Incrementна кожному елементі працює повільно: потоки постійно змагаються за ті самі лічильники й кеш-рядки (тема 4). - Фільтр зображення. Рядки обробляються незалежно, результат пишуть у новий масив (не на місці): фільтр читає сусідні пікселі, які інший потік міг уже змінити.
- Метод Монте-Карло. Екземпляр
Randomне є потокобезпечним: одночасні виклики з кількох потоків можуть зіпсувати його стан, і він почне повертати нулі.Random.Sharedпотокобезпечний, але результат не відтворюється. Для відтворюваних результатів задачу ділять на фіксовану кількість блоків, і блокkстворює власнийnew Random(seed + k): результат не залежить від кількості потоків (лабораторна робота, приклад 2). - Вкладені цикли. Зазвичай розпаралелюють зовнішній цикл: робота на ітерацію більша, а накладні витрати менші.
Аналіз продуктивності
Для паралельної програми вимірюють час
Сильна масштабованість (strong scaling): розмір задачі сталий, а
Знімок екрана
Windows Terminal: dotnet run -c Release -- 1,2,4,8,16 in the merge sort project; the aligned table n, p, time, S, E and the line with speedup.csv
Рис. 6.7. Таблиця прискорення та ефективності сортування злиттям
Файл CSV відкривають в Excel (Data → From Text/CSV) або LibreOffice Calc (роздільник – кома, десятковий роздільник – крапка) і будують точкову діаграму
- послідовна частина (закон Амдала): зчитування даних, останнє злиття, виведення;
- накладні витрати на задачі й розбиття, якщо робота на ітерацію мала (зернистість);
- обмеження пам’яті: коли обчислення впираються в пропускну здатність пам’яті, а не в ядра, прискорення може припинитися вже на кількох потоках;
- SMT/Hyper-Threading: на i9-11900KF 16 логічних процесорів, але лише 8 фізичних ядер, тому перехід від 8 до 16 потоків дає значно менший приріст;
- збирання сміття: код, що виділяє багато об’єктів (рядки, кортежі), чекає на збирач сміття. Серверний режим збирача (
<ServerGarbageCollection>true</ServerGarbageCollection>у файлі проєкту) у прикладі «Аналіз тексту» пришвидшив варіант зGroupByмайже вдвічі; - хибне розділення кешу (тема 4) та нерівномірне навантаження розділів.
Щоб побачити, де потоки простоюють, використовують профілювальник. У JetBrains Rider режим Timeline профілювальника dotTrace показує роботу кожного потоку на часовій шкалі https://www.jetbrains.com/help/rider/Profiling_Applications.html: оберіть Run → Switch Profiling Configuration → Timeline, потім Run → Profile … (рис. 6.8). Суцільні смуги робочих потоків означають обчислення, проміжки – очікування чи блокування.
Знімок екрана
Rider: Run → Switch Profiling Configuration → Timeline; profile the merge sort example with program argument 16; Get Snapshot; thread lanes of .NET ThreadPool workers, filter by method ParallelMergeSort.Sort
Рис. 6.8. Робота потоків паралельного сортування в dotTrace (Timeline)