Обчислення інтеграла і Parallel.For
Проведемо ще один заключний експеримент ефективності застосування Parallel.For в порівнянні з іншими методами розпаралелювання. Для цього повернемося до вже добре знайомої завданню обчислення певного інтеграла, розглянутої в попередніх розділах. В "Паралельні алгоритми" ми почали проектувати клас NewIntegral. додаючи в нього в наступних розділах різні методи, що дозволяють обчислити інтеграл. Додамо ще один метод, в якому для розпаралелювання циклів використовується виклик Parallel.For:
Це найпростіше і саме зрозуміле рішення. Цикл складається з одного рядка. У циклі викликається метод EvalIntegral. якому передається індекс ітерації циклу. У методі EvalIntegral формуються межі інтервалу інтегрування, і викликається послідовний метод, який здійснює інтегрування на заданому відрізку. Ось код цього методу:
Робота з класом NewIntegral. виклик різних методів цього класу, оцінка часу, необхідного для обчислень тим чи іншим методом, здійснюється в интерфейсном проект, що представляє традиційний Windows Forms проект. Не буду зупинятися детально на його описі. На наступному малюнку показаний вид цього проекту, що містить результати роботи кожного з розглянутих методів і той час, який кожен з методів витратив на обчислення інтеграла:
В цьому експерименті все паралельні методи в 4-5 разів ефективніше послідовних методів. Кращий результат в це раз показав метод, який використовує потоки. Зауважте, при порівняно великій кількості потоків цей метод працює добре при довгих ітераціях, як в даному випадку. Проте, для розпаралелювання циклів слід застосовувати метод Parallel.For як найбільш простий, інтуїтивно зрозумілий метод, відповідний звичного для нас оператору for.
Цикл while і Parallel.For
До сих пір, кажучи про розпаралелювання циклів, ми розглядали виключно цикл типу for. Більш загальною формою циклу є форма з циклом while:
Як распараллелить такий цикл, коли заголовок циклу не визначає число ітерацій, необхідних для завершення циклу. Приклад на цю тему у нас вже зустрічався, коли ми розглядали числа-градини. Там же, по суті, дано і рішення виникаючої проблеми. Рішення базується на можливості використання оператора break в паралельно виконуваних ітераціях циклу. Умова виходу (B) перевіряється в ході виконання ітерації і при його істинності здійснюється переривання виконання виконуваних ітерацій. При цьому забезпечується можливість з'ясування найменшого індексу ітерації, для якого виконується умова виходу. Детальна семантика процесу переривання вже описана в цьому розділі. Давайте розглянемо схему заміни циклу while паралельним циклом Parallel.For. Вона виглядає наступним чином:
Тіло циклу оформляється як метод, яким передаються два параметри - індекс поточної ітерації і параметр класу ParallelLoopState:
Залишається питання, яке слід розв'язати, - як задати параметр N. Найчастіше, відомо максимально можливе число ітерацій. Наприклад, у класичній задачі пошуку за зразком, що використовує цикл while. число ітерацій не може перевищувати числа елементів масиву. В інших завданнях це число вибирається з якихось зовнішніх припущень, як наприклад в завданні про числах-градини, нас цікавила відповідь для деякого фіксованого інтервалу чисел. Наприклад, для погано сходяться процесів розумно задавати деяке максимальне число ітерацій, при досягненні якого процес припиняється. Так що число N в цій схемі - це число, що обмежує максимальне число ітерацій. Наша схема завжди дозволяє з'ясувати, як закінчився цикл - або по досягненню максимуму ітерацій, або по досягненню істинності умови B на ітерації. В останньому випадку відомий мінімальний індекс ітерації, на якому ця умова стала справжнім.
Підводячи підсумок, можна сказати, що цикли while також можуть бути досить просто распараллеліть. Як завжди, головна проблема полягає в забезпеченні незалежності ітерацій циклу. Відповідальність за вирішення цієї проблеми несе програміст.