📘 TBB (Intel Threading Building Blocks) для C++: полное руководство
oneAPI Threading Building Blocks (oneTBB) — это кроссплатформенная библиотека на языке C++, разработанная Intel для упрощения параллельного программирования. Она предоставляет высокоуровневые абстракции, которые позволяют разработчикам эффективно использовать многоядерные процессоры, не погружаясь в низкоуровневые детали работы с потоками, синхронизацией и планированием задач.
Зачем нужна TBB? В современном мире производительность процессоров растёт не за счёт увеличения тактовой частоты, а за счёт увеличения количества ядер. Чтобы программа работала быстрее на многоядерном процессоре, её необходимо распараллелить. Ручное управление потоками (через std::thread) сложно, чревато ошибками (гонки данных, взаимные блокировки) и плохо масштабируется. TBB решает эти проблемы, предлагая:
- Автоматическое планирование задач — библиотека сама распределяет работу по доступным ядрам, используя алгоритм «work stealing» (кража работы).
- Безопасные параллельные контейнеры —
concurrent_vector,concurrent_queueи другие, которые можно использовать без ручной синхронизации. - Простота — для распараллеливания цикла достаточно заменить
forнаtbb::parallel_for.
TBB активно используется в высоконагруженных системах, научных вычислениях, обработке изображений, игровых движках и везде, где важна производительность. С 2020 года проект развивается под эгидой организации oneAPI и является открытым (Apache 2.0).
📦 Установка
Установка TBB зависит от вашей операционной системы и менеджера пакетов. Ниже приведены команды для основных платформ.
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install libtbb-dev
macOS (Homebrew)
brew install tbb
Windows (vcpkg)
vcpkg install tbb
Сборка из исходников (CMake)
git clone https://github.com/oneapi-src/oneTBB.git
cd oneTBB
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local
make -j$(nproc)
sudo make install
-ltbb:
g++ -std=c++17 -O2 my_program.cpp -ltbb -o my_program
🚀 Быстрый старт
Минимальный рабочий пример: распараллеливание цикла, который вычисляет квадраты чисел.
#include <iostream>
#include <vector>
#include <tbb/parallel_for.h>
int main() {
const int n = 100;
std::vector<int> data(n, 1);
// Параллельный for: каждый элемент умножается на 2
tbb::parallel_for(0, n, [&](int i) {
data[i] = data[i] * 2;
});
// Вывод первых 10 элементов
for (int i = 0; i < 10; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
return 0;
}
Этот код автоматически распределит итерации по всем доступным ядрам процессора. Никаких явных потоков, мьютексов или усложнённой логики — просто заменили for на tbb::parallel_for.
🧩 Основные методы, функции и классы
Рассмотрим 8 ключевых компонентов TBB, которые покрывают 90% задач параллельного программирования.
1. tbb::parallel_for
Сигнатура:
template<typename Index, typename Body>
void parallel_for(Index first, Index last, Body body,
const tbb::auto_partitioner& partitioner = tbb::auto_partitioner{});
Что делает: Параллельно выполняет итерации цикла от first до last (не включая последний). Тело цикла (body) — это функтор или лямбда, принимающая индекс.
Пример:
#include <tbb/parallel_for.h>
#include <vector>
std::vector<double> vec(1000, 1.0);
tbb::parallel_for(0, (int)vec.size(), [&](int i) {
vec[i] = std::sin(vec[i]) + std::cos(vec[i]);
});
2. tbb::parallel_reduce
Сигнатура:
template<typename Range, typename Value, typename Func, typename Reduction>
Value parallel_reduce(const Range& range, Value identity, Func func, Reduction reduction);
Что делает: Параллельно вычисляет редукцию (сумму, произведение, максимум и т.д.) элементов диапазона. identity — нейтральный элемент (0 для суммы, 1 для произведения).
Пример:
#include <tbb/parallel_reduce.h>
#include <vector>
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sum = tbb::parallel_reduce(
tbb::blocked_range<int>(0, data.size()),
0,
[&](const tbb::blocked_range<int>& r, int init) -> int {
for (int i = r.begin(); i != r.end(); ++i)
init += data[i];
return init;
},
[](int a, int b) -> int { return a + b; }
);
// sum == 55
3. tbb::parallel_for_each
Сигнатура:
template<typename InputIterator, typename Body>
void parallel_for_each(InputIterator first, InputIterator last, Body body);
Что делает: Применяет функцию body к каждому элементу в диапазоне итераторов. Аналог std::for_each, но параллельный.
Пример:
#include <tbb/parallel_for_each.h>
#include <vector>
#include <string>
std::vector<std::string> words = {"hello", "world", "tbb", "parallel"};
tbb::parallel_for_each(words.begin(), words.end(), [](std::string& s) {
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
});
// words == {"HELLO", "WORLD", "TBB", "PARALLEL"}
4. tbb::parallel_sort
Сигнатура:
template<typename RandomAccessIterator>
void parallel_sort(RandomAccessIterator first, RandomAccessIterator last);
template<typename RandomAccessIterator, typename Compare>
void parallel_sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
Что делает: Параллельная сортировка диапазона. Использует алгоритм быстрой сортировки с порогом переключения на последовательную сортировку для маленьких подмассивов.
Пример:
#include <tbb/parallel_sort.h>
#include <vector>
#include <random>
std::vector<int> v(100000);
std::generate(v.begin(), v.end(), std::rand);
tbb::parallel_sort(v.begin(), v.end()); // По возрастанию
// или с компаратором:
tbb::parallel_sort(v.begin(), v.end(), std::greater<int>()); // По убыванию
5. tbb::concurrent_vector
Сигнатура (основные методы):
template<typename T>
class concurrent_vector {
public:
void push_back(const T& value);
T& operator[](size_t index);
size_t size() const;
// ... другие методы
};
Что делает: Потокобезопасный динамический массив. Несколько потоков могут одновременно добавлять элементы (push_back) без блокировок. Доступ по индексу безопасен только если индекс уже существует.
Пример:
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for.h>
tbb::concurrent_vector<int> vec;
vec.reserve(1000); // Зарезервировать место
tbb::parallel_for(0, 1000, [&](int i) {
vec.push_back(i * i); // Безопасно из нескольких потоков
});
6. tbb::concurrent_queue
Сигнатура (основные методы):
template<typename T>
class concurrent_queue {
public:
void push(const T& value);
bool try_pop(T& value);
bool empty() const;
};
Что делает: Потокобезопасная очередь FIFO. Подходит для модели «производитель-потребитель». try_pop неблокирующий — возвращает false, если очередь пуста.
Пример:
#include <tbb/concurrent_queue.h>
#include <thread>
tbb::concurrent_queue<int> queue;
// Поток-производитель
std::thread producer([&]() {
for (int i = 0; i < 100; ++i)
queue.push(i);
});
// Поток-потребитель
std::thread consumer([&]() {
int value;
while (queue.try_pop(value)) {
// Обработка value
}
});
producer.join();
consumer.join();
7. tbb::task_group
Сигнатура (основные методы):
class task_group {
public:
template<typename Func>
void run(Func f);
void wait();
};
Что делает: Позволяет запускать несколько задач асинхронно и дожидаться их завершения. Задачи выполняются в пуле потоков TBB.
Пример:
#include <tbb/task_group.h>
#include