Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Parallel - Fork-Join -- EXPERIMENTAL

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 35. Thread 4.7.1

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext
[Warning] Warning

Эти функции являются экспериментальными и могут быть изменены в будущих версиях. Пока еще не слишком много тестов, поэтому возможно, что вы можете найти некоторые тривиальные ошибки :

[Note] Note

Эти функции основаны на предложенииn4088 - Task Region R3C++1y от P. Halpern, A. Robison, A. Laksberg, H. Sutter и др. Нижеследующий текст был адаптирован из этой статьи, чтобы показать различия.

Основное отличие стандартного предложения заключается в том, что мы можем использовать общего исполнителя для нескольких целевых регионов.

Этот модуль вводит шаблон библиотечной функции 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

PrevUpHomeNext

Статья 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 ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:45:50/0.0087010860443115/0