Короткий огляд Parallel Extensions для. NET Framework, Різне, Інтернет-технології, статті

Зараз нікого не здивуєш наявністю декількох ядер в процесорі, будь то робоча станція, ноутбук, нетбук – неважливо. Далі – більше: “двоголові” процесори скоро облаштуються і в мобільних телефонах. Ось чому саме зараз тема розпаралелювання програм перестає бути академічної і набуває цілком практичний інтерес. Зрозуміло, при створенні такого ПО є ряд підводних каменів. Крім того, не всі і не завжди можна і потрібно распараллелівать – але це тема окремої розмови. Мета цієї статті – зробити екскурс в область інструментів від компанії Microsoft для створення распараллеленних керованих додатків.


Отже, Parallel Extensions, як можна здогадатися з назви, це як раз і є той самий набір інструментів, покликаний підвищити ефективність застосування нових апаратних засобів. Вперше в загальному доступі бібліотека з’явилася в листопаді 2007 року і мала статус CTP. У червні 2008 року з’явилася нова preview-версія. Обидві вони використали. NET 3.5 для своєї роботи, і поставлялися у вигляді окремої збірки. Однак восени минулого року з виходом Visual Studio 2010 CTP цей інструментарій переїхав в збірку mscorlib і став використовувати. NET 4. З тих пір нічого не змінилося, і нових версій для. NET 3.5 не випускалося. Протягом усього цього часу змінювався склад бібліотеки, її API. Найбільш актуальна версія не так давно випущена у складі. NET 4 beta 2. Ви можете скачати Visual Studio 2010 beta 2 і спробувати бібліотеку своїми руками.


Схематично склад Parallel Extensions можна зобразити таким чином:



. NET 4 несе вагоме кількість змін. Вони торкнулися в тому числі і принципу роботи пулу потоків. Його оптимізували для управління великим числом робочих потоків, і поміняли реалізацію черги завдання для кожного з них. Тепер крім глобальної черги завдань кожен потік має свою локальну чергу. Це дозволяє більш ефективно керувати завданнями. Наприклад, перший потік виконує чергову задачу, а другий встиг спустошити свою чергу. Тоді він може “вкрасти” завдання з черги першого потоку, тим самим розподіливши навантаження. При цьому не буде звернення до глобальної черги завдань, що зменшить навантаження на неї. Ці дії будуть прозорі для прикладного програміста.


Якщо уважно придивитися до попереднього абзацу, можна помітити ключове слово “завдання”. Воно знаменує один з підходів до спрощення розпаралелювання програм. Бібліотека Parallel Extensions пропонує зосередити увагу на реалізується бізнес-сценарії. Слово “потік” відноситься до технічної реалізації. “Завдання” ж являє собою деякий атомарному дію, виконання якого дозволить отримати результат. При цьому не розглядається, ким і де буде виконана задача. Важливіше те, як вона реалізується і як співвідноситься з іншими завданнями. Така концепція, покладена в основі Task Parallel Library (Коротко TPL) – набору інструментів для створення, управління і виконання завдань. При цьому TPL використовує механізми оновленого пулу потоків для запуску і завершення завдань. Фактично, завдання являє собою якусь асинхронну операцію в контексті нашої програми. Звичайно, в. NET 2.0 були QueueUserWorkItem (), BackgroundWorker та ін кошти. З їх допомогою можна було досягти потрібного функціонала, що всі ми з вами успішно робили. Але TPL надає вже готовий, до деякої міри універсальний підхід. Це зменшує витрати на розпаралелювання, що в кінцевому рахунку робить проект дешевше з економічних і тимчасовим показникам.


На основі Task Parallel Library і концепції “завдань” працюють прикладні засоби по распараллеливанию. Серед них – PLINQ, тобто Parallel LINQ. Ця частина бібліотеки Parallel Extensions, що дозволяє виконувати LINQ запити одночасно в декількох потоках. Родзинка її полягає в дуже простому способі інтеграції з уже наявним кодом. Не доведеться знову переписувати купу запитів на новий лад. Можна просто викликати метод-розширення AsParallel (), і тепер LINQ запит буде виконаний паралельно:
















Послідовна версія  Паралельна версія 
from i in collection from i in collection.AsParallel()
where IsValid(i) where IsValid(i)
select i; select i;

Зрозуміло, виникає багато цікавих моментів – наприклад, в нашому прикладі ніхто не знає, в якому порядку елементи будуть оброблені при паралельному виконанні. Є свої особливості, пов’язані з обробкою винятків. Знову ж таки, не всякий LINQ-запит є сенс распараллелівать. Як завжди, потрібно правильно оцінювати ситуацію і діяти за обставинами. Але наявність такої легкої можливості распараллелить процес виконання, безумовно, радує. Ще однією перевагою PLINQ є масштабованість. Поясню: запит буде виконаний настільки швидко, наскільки це можна в даній конкретній середовищі. Наша програма без перекомпіляції і без внесення будь-яких змін буде працювати з ефективністю 1х на одноядерної машині, 1,8 х-1, 9х на двоядерній, 3,7 х-3, 8х на чотириядерних і т.д. (Числа наведені приблизні, для порівняння; реальна різниця, звичайно, залежить від завдання і способу її реалізації). Тобто чим більш потужне обладнання буде використано, тим швидше виконається запит. PLINQ фізично є набором методів-розширень, що працюють з System.Linq.ParallelQuery .


Крім того, Parallel Extensions містить кілька конструкцій, що полегшують створення распараллеленних циклів і запуск безлічі делегатів одночасно. Це набір статичних методів класу Parallel. Їх багато, але більшість є перевантаженими версіями наступних трьох:



Різні версії цих методів містять додаткові параметри, за допомогою яких можна вказати, наприклад, ступінь паралелізму, маркер відміни і т.д. Давайте розглянемо декілька простих прикладів. Припустимо, є наступний цикл foreach:


Код:
IEnumerable<string> collection =
    new[] { “The”, “quick”, “brown”, “fox”, “jumps”, “over”, “the”, “lazy”, “dog”};

foreach (var word in collection)
{
    ProcessString(word);
}


Щоб перевести цей цикл на “паралельні рейки”, досить замінити його викликом:


Код:
Parallel.ForEach(collection, (word) => { ProcessString(word); });

Тепер обробка слів буде виконана паралельно. Кожен виклик Parallel.For () і Parallel.ForEach () генерує певну кількість завдань, залежно від поточного оточення, після чого передає їх для виконання робочим потокам. Т.ч., цикл буде виконаний настільки швидко, наскільки це можливо.


А ось так можна обмежити ступінь паралелізму:


Код:
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 4;

Parallel.For(0, 100, options, (i) => { ProcessItem(i); });


Як бачите, використовується перевантажена версія Parallel.For (), приймаюча настройки (ParallelOptions).


На додачу приклад обходу дерева наступного вигляду:


Код:
class Tree<T>
{
    public T Data;
    public Tree<T> Left, Right;
   // …
}

Послідовно це можна зробити наступним чином:


Код:
static void WalkTree<T>(Tree<T> tree, Action<T> func)
{
    if (tree == null) return;
    WalkTree(tree.Left, func);
    WalkTree(tree.Right, func);
    func(tree.Data);
}

Однак, якщо обчислення не залежать від порядку обробки вузлів, то можна легко распараллелить процес:


Код:
static void WalkTree<T>(Tree<T> tree, Action<T> func)
{
    if (tree == null) return;
    Parallel.Invoke(
        () => WalkTree(tree.Left, func),
        () => WalkTree(tree.Right, func),
        () => func(tree.Data));
}

Вказуємо набір делегатів, а саме прохід по лівому і правому нащадкам поточного вузла, і обробку поточного як параметри Parallel.Invoke () і радіємо зрослої продуктивності. Треба відзначити, що Parallel.Invoke () також підтримує вказівку ParallelOptions, для цього є відповідний перевантажений метод.


На додачу до цього Parallel Extensions надає вельми зручні сховища даних з можливістю конкурентного доступу і набір примітивів синхронізації. Це виглядає абсолютно логічно: виконуючи дії в декількох потоках, непогано було б мати де-небудь результат роботи. У мене є 2 великі статті, в яких проводиться огляд цих структур даних, пропоную всім зацікавився їх прочитати:


частина 1


і


частина 2


. Тут я не стану детально на них зупинятися.


Отже, підіб’ємо підсумок. Те, що компанія Microsoft приділила велику увагу питанням паралельного виконання коду, вельми тішить. Інструментарій, на мій погляд, вийшов досить зручним у використанні і досить простим. Взагалі, малий вхідний поріг – один з козирів Parallel Extensions. Концепція та API прозорі і зрозумілі. Ми отримали (я говорю саме “отримали”, оскільки вже зараз вийшла GoLive ліцензія на VS 2010 beta 2, що дозволяє будувати комерційні додатки на її основі) зручну бібліотеку, яка дозволить мінімізувати витрати на розпаралелювання.

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


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

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

Ваш отзыв

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

*

*