У попередній статті про Intel Threading Building Blocks розповідалося про переваги цієї бібліотеки, а також було розглянуто приклад алгоритму parallel_for. Розгляньмо приклад використання алгоритму parallel_reduce, який відрізняється від функції parallel_for тим, що дає змогу обʼєднувати часткові результати, отримані в паралельних потоках.

Передусім потрібно підключити необхідні заголовні файли:

`#include

include

include

include

* This source code was highlighted with Source Code Highlighter.`

Тут stdio.h використовується для підключення функції printf (виведення на екран), blocked_range.h — для використання класу blocked_range (блоковий діапазон), parallel_reduce.h — для підключення відповідного алгоритму, а task_scheduler_inti.h — для підключення планувальника задач.

Як було сказано раніше, стандартні алгоритми Intel TBB — parallel_for, parallel_reduce — використовують концепцію класу-задачі. У прикладі нижче показано подібний клас для розвʼязання задачі знаходження значення константи Пі (приклад навіяний конкурсом Інтела, а також самостійною роботою мого студента). Суть знаходження числа полягає в обчисленні площі криволінійної трапеції, яку приблизно описано великою кількістю прямокутників. Що більше прямокутників, то точніше значення, але тим більше часу потрібно для обчислення. Ілюстрацію до цієї задачі можна подивитися на сайті «Вольфрам-Альфа».

`// Класс-задача, отвечает за подсчет значения пи на заданном диапазаоне

class PiCalculation {

private: 

// Количество шагов в интервале, влияет на точность   

long num_steps;   // Ширина шага   double step; public: // Частичное значение пи   double pi;   // Вычислить частичное значение пи на заданном диапазоне   void operator () (const tbb::blocked_range<long> &r)   {     double sum = 0.0;     long end = r.end();     for (int i = r.begin(); i != end; i++)     {       double x = (i + 0.5) * step;       sum += 4.0/(1.0 + x * x);     }     pi += sum * step;   }   // Объеденить частичные результаты   void join(PiCalculation &p)   {     pi += p.pi;   }   // Разделяющий конструктор   PiCalculation(PiCalculation &p, tbb::split)   {     pi = 0.0;     num_steps = p.num_steps;     step = p.step;   }      // Конструктор   PiCalculation(long steps)   {     pi = 0.0;     num_steps = steps;     step = 1./(double)num_steps;   } }; * This source code was highlighted with Source Code Highlighter.`

Важливими в цьому класі є такі два методи: operator () і join.

Метод void operator () (const blocked_range &r) обчислює частковий результат в інтервалі [r.begin, r.end). Бібліотека Intel TBB і клас blocked_range дбають про те, щоб рекурсивно розбити початковий простір ітерацій і «згодовувати» невеликими порціями методу operator (). Результат накопичується у змінній pi, яка є приватною для потоку. Дається гарантія, що operator () для одного класу буде викликано тільки з одного потоку, тому помилки типу «гонки за даними» (data races, race conditions) виключені.

Метод void join (PiCalculation &p) підсумовує часткові результати різних потоків. Тут проявляється важлива відмінність бібліотеки Intel TBB від бібліотек OpenMP і MPI, у яких кількість операцій для обʼєднання часткових результатів (reduction, редукування) обмежена. Intel TBB дає змогу реалізувати довільні функції обʼєднання результатів у тілі методу join.

Розгляньмо запуск алгоритму parallel_reduce.

<font size="2" face="Courier New" color="black"><font color="#0000ff">int</font> main() {   <font color="#008000">// Инициализация библиотеки Intel TBB</font>   tbb::task_scheduler_init init;   <font color="#008000">// Количество итераций</font>   <font color="#0000ff">const</font> <font color="#0000ff">long</font> steps = 100000000;   <font color="#008000">// Создание объекта-задачи по вычислению пи</font>   <font color="#008000">// Передается параметр: количество итераций</font>   PiCalculation pi(steps);   <font color="#008000">// Запуск алгоритма parallel_reduce над диапазоном [0, steps)</font>   tbb::parallel_reduce(tbb::blocked_range<<font color="#0000ff">long</font>>(0, steps, 1000000), pi);   printf (<font color="#A31515">"Pi is %3.20f\n"</font>, pi.pi);   <font color="#0000ff">return</font> 0; } </font> <font size="1" color="gray">* This source code was highlighted with [<font size="1" color="gray">Source Code Highlighter</font>](http://virtser.net/blog/post/source-code-highlighter.aspx).</font>

Для цього необхідно виконати такі кроки: створити обʼєкт планувальника задач Intel TBB, створити обʼєкт задачі PiCalculation і викликати функцію parallel_reduce. Тут функція приймає два параметри. Перший — це клас діапазону. У нашому випадку ми вказуємо кількість ітерацій [0, steps), тобто під час обчислення інтеграла від 0 до 1 буде використано steps = 100 000 000 кроків. Другий параметр — це посилання на обʼєкт задачі, у якому після повернення з функції parallel_reduce міститиметься значення пі.