![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Parallel - Fork-Join -- EXPERIMENTALBoost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 35. Thread 4.7.1
|
![]() |
Warning |
---|---|
Эти функции являются экспериментальными и могут быть изменены в будущих версиях. Пока еще не слишком много тестов, поэтому возможно, что вы можете найти некоторые тривиальные ошибки : |
![]() |
Note |
---|---|
Эти функции основаны на предложенииn4088 - Task Region R3C++1y от P. Halpern, A. Robison, A. Laksberg, H. Sutter и др. Нижеследующий текст был адаптирован из этой статьи, чтобы показать различия. |
Основное отличие стандартного предложения заключается в том, что мы можем использовать общего исполнителя для нескольких целевых регионов.
![]() |
Note |
---|---|
До сих пор, Буст. Thread не реализует параллельные алгоритмы, как определено вn4105 - Информационные технологии & #8211; Языки программирования, их среды и системные программные интерфейсы & #8211; Техническая спецификация для расширений C++ для параллелизма. |
Этот модуль вводит шаблон библиотечной функции C++11/c++14<task_region
>и класс библиотеки<task_region_handle
>с функциями-членами<run
>и<wait
>, которые вместе позволяют разработчикам писать выразительный и портативный параллельный код вилочного соединения.
Рабочий проект для параллелизма ТСN4105дополняет алгоритмы STL включением политики параллельного исполнения. Программисты используют их в качестве основы для написания дополнительных алгоритмов высокого уровня, которые могут быть реализованы с точки зрения предоставленных параллельных алгоритмов. Однако область применения n4105 не включает механизмы более низкого уровня для выражения произвольного параллелизма вилочного соединения.
Функции<task_region
>,<run
>и<wait
>, предоставляемые этой библиотекой, основаны на концепции<task_group
>, которая является частью общего подмножества библиотек PPL и TBB.
Рассмотрим пример параллельного обхода дерева, где к каждому узлу дерева применяется пользовательская функция вычисления, возвращающая сумму результатов:
template<typename Func> int traverse(node *n, Func&& compute) { int left = 0, right = 0; task_region([&](task_region_handle& tr) { if (n->left) tr.run([&] { left = traverse(n->left, compute); }); if (n->right) tr.run([&] { right = traverse(n->right, compute); }); }); return compute(n) + left + right; }
Приведенный выше пример демонстрирует использование двух функций, предложенных в настоящем документе,<task_region
>и<task_region_handle::run
>. Функция<task_region
>определяет область в программном коде, потенциально содержащем вызовы задач, порожденных функцией<run
>члена класса<task_region_handle
>.
Функция запуска порождает задачу, единицу работы, которую разрешено выполнять параллельно по отношению к абоненту. Любые параллельные задачи, порожденные<run
>внутри<task_region
>, соединяются обратно в одну нить выполнения в конце<task_region
>.
<run
>берет предоставленный пользователем объект<f
>и запускает его асинхронно — то есть он может вернуться до завершения выполнения<f
>. Планировщик реализации может запускать<f
>немедленно или задерживать запуск<f
>до тех пор, пока не появятся вычислительные ресурсы.
А<task_region_handle
>может быть построено только<task_region
>, потому что у него нет общественных строителей. Таким образом,<run
>может быть вызван (прямо или косвенно) только из предоставленной пользователем функции, переданной<task_region
>:
void g(); void f(task_region_handle& tr) { tr.run(g); // OK, invoked from within task_region in h } void h() { task_region(f); } int main() { task_region_handle tr; // Error: no public constructor tr.run(g); // No way to call run outside of a task_region return 0; }
Это, безусловно, худшее осуществление функции Фибоначчи. В любом случае, вот оно, так как оно простое и четко показывает структуру вилочного соединения.<Fibonacci(n)=Fibonacci(n-1)
+Fibonacci(n-2)
>Итак, задача разложения тривиальна.
int fib_task_region(int n) { using boost::experimental::parallel::task_region; using boost::experimental::parallel::task_region_handle; if (n == 0) return 0; if (n == 1) return 1; int n1; int n2; task_region([&](task_region_handle& trh) { trh.run([&] { n1 = fib_task_region(n - 1); }); n2 = fib_task_region(n - 2); }); return n1 + n2; } int main() { for (int i = 0; i<10; ++i) { std::cout << fib_task_region(i) << " "; } std::cout << std::endl; }
В предыдущем примере используется определенный способ реализации для создания задач. Часто пользователь хочет освоить, как должна быть порождена задача. Существует перегрузка<task_region
>, которая принимает дополнительный<Executor
>параметр и функцию, которая принимает в качестве параметра<task_region_handle_gen<Executor>
>.<task_region_handle_gen<Executor>
>запуск использует этого исполнителя, чтобы порождать задачи.
template <class Ex> int fib_task_region_gen( Ex& ex, int n) { using boost::experimental::parallel::task_region; using boost::experimental::parallel::task_region_handle_gen; if (n == 0) return 0; if (n == 1) return 1; int n1; int n2; task_region(ex, [&](task_region_handle_gen<Ex>& trh) // (2) { trh.run([&] { n1 = fib_task_region(n - 1); }); n2 = fib_task_region(n - 2); }); return n1 + n2; } int main() { boost::basic_thread_pool tp; // (1) for (int i = 0; i<10; ++i) { std::cout << fib_task_region_gen(tp,i) << " "; } std::cout << std::endl; return 0; }
Конкретный исполнитель объявляется в строке (1) и используется в строке (2).
namespace boost { namespace experimental { namespace parallel { inline namespace v1 { class exception_list; } // v1 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v1 { class exception_list: public std::exception { public: typedef 'implementation defined' const_iterator; ~exception_list() noexcept {} void add(exception_ptr const& e); size_t size() const noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const char* what() const noexcept; }; } // v1 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { class task_canceled_exception; template <class Executor> class task_region_handle_gen; using default_executor = 'implementation defined'; class task_region_handle; template <typename Executor, typename F> void task_region_final(Executor& ex, F&& f); template <typename F> void task_region_final(F&& f); template <typename Executor, typename F> void task_region(Executor& ex, F&& f); template <typename F> void task_region(F&& f); } // v2 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { class task_canceled_exception: public std::exception { public: task_canceled_exception() noexcept; task_canceled_exception(const task_canceled_exception&) noexcept; task_canceled_exception& operator=(const task_canceled_exception&) noexcept; virtual const char* what() const noexcept; }; } // v2 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { template <class Executor> class task_region_handle_gen { protected: task_region_handle_gen(Executor& ex); ~task_region_handle_gen(); public: task_region_handle_gen(const task_region_handle_gen&) = delete; task_region_handle_gen& operator=(const task_region_handle_gen&) = delete; task_region_handle_gen* operator&() const = delete; template<typename F> void run(F&& f); void wait(); }; } // v2 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { using default_executor = 'implementation defined'; } // v2 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { class task_region_handle : public task_region_handle_gen<default_executor> { protected: task_region_handle(); task_region_handle(const task_region_handle&) = delete; task_region_handle& operator=(const task_region_handle&) = delete; task_region_handle* operator&() const = delete; }; } // v2 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { template <typename Executor, typename F> void task_region_final(Executor& ex, F&& f); template <typename F> void task_region_final(F&& f); } // v2 } // parallel } // experimental } // boost
namespace boost { namespace experimental { namespace parallel { inline namespace v2 { template <typename Executor, typename F> void task_region(Executor& ex, F&& f); template <typename F> void task_region(F&& f); } // v2 } // parallel } // experimental } // boost
Статья Parallel - Fork-Join -- EXPERIMENTAL раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 35. Thread 4.7.1 может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 35. Thread 4.7.1 ::
реклама |