pthreadpool
Портируемая и эффективная реализация пула потоков (thread pool) для языка C. Библиотека предоставляет функциональность, аналогичную директиве #pragma omp parallel for, но с дополнительными возможностями. Она позволяет легко параллелизировать выполнение циклов и других задач, эффективно распределяя работу между потоками с помощью алгоритма кражи задач.
Особенности
- интерфейс на чистом C (совместимый с C++);
- поддержка циклов с 1 по 6 измерений с возможностью задания шага;
- автоматическое определение числа потоков или ручное указание;
- алгоритм кражи задач для эффективной балансировки нагрузки;
- наличие микро-бенчмарков для измерения пропускной способности и задержек.
Основные компоненты
pthreadpool предоставляет простой API для создания и управления пулом потоков, а также для распараллеливания выполнения задач (подробнее с документацией можно ознакомиться здесь).
Управление пулом потоков
Центральным объектом библиотеки является непрозрачный указатель pthreadpool_t, который представляет собой пул потоков. Функции этого раздела позволяют создавать пул с нужным количеством потоков, получать информацию о нём и уничтожать его.
Основные функции управления пулом:
pthreadpool_t pthreadpool_create(size_t threads_count)— создаёт пул с указанным числом потоков. Еслиthreads_countравно 0, пул создаётся с количеством потоков, равным числу процессорных ядер в системе;size_t pthreadpool_get_threads_count(pthreadpool_t threadpool)— возвращает фактическое количество потоков в созданном пуле;void pthreadpool_destroy(pthreadpool_t threadpool)— завершает все потоки в пуле и освобождает связанные ресурсы. Доступ к пулу после вызова этой функции приводит к неопределённому поведению.
Выполнение параллельных задач
Основное назначение библиотеки — параллельное выполнение задач над набором элементов. Пользователь предоставляет функцию обратного вызова, которая будет вызвана для каждого элемента, а библиотека распределяет эти вызовы между потоками пула.
Основные функции для распараллеливания:
void pthreadpool_compute_1d(pthreadpool_t threadpool, pthreadpool_function_1d_t function, void* argument, size_t range)— выполняетfunctionдля каждого элемента в диапазоне[0, range-1]. Функция должна иметь типvoid (*)(void*, size_t);void pthreadpool_compute_2d(pthreadpool_t threadpool, pthreadpool_function_2d_t function, void* argument, size_t range_i, size_t range_j)— выполняет двухмерную итерацию по диапазонамiиj;void pthreadpool_compute_2d_tiled(pthreadpool_t threadpool, pthreadpool_function_2d_tiled_t function, void* argument, size_t range_i, size_t range_j, size_t tile_i, size_t tile_j)— выполняет двухмерную итерацию, разбивая её на прямоугольные блоки (тайлы) размеромtile_i x tile_j.
Внутренняя архитектура и оптимизация
pthreadpool спроектирован с учётом требований к производительности на современных многоядерных системах:
- Work-stealing: простаивающие потоки могут «воровать» задачи у занятых потоков, что обеспечивает равномерную загрузку всех ядер процессора;
- Структуры данных, выровненные по кэш-линии: внутренние структуры выровнены по границе 64 байт, что предотвращает ложный обмен кэша между потоками;
- Синхронизация без блокировок: для доступа к разделяемым счётчикам задач используются атомарные операции (
__sync_val_compare_and_swap), что снижает накладные расходы на синхронизацию; - Условные переменные: для эффективного управления состояниями потоков используются условные переменные.