如何将其累积在减少(或其他平行的STL算法)中?

发布于 2025-02-02 16:37:36 字数 2347 浏览 1 评论 0原文

我正在为课程项目开发C ++中的Barnes -Hut模拟。 在这里您可以找到算法的详细说明。

我简要说明以后将使用的数据类型。

  • 一个主体由正文数据类型表示,该型号是一个结构,其中包含vector2f表示位置的结构,而质量的浮点数为float)。
  • Quadtree中的节点由节点 datatype表示。当然,节点结构包含一些数据,这些数据可能是:1)其四个孩子,对应于其四个子界。这只保存节点是树中的叉子。 2)它的身体。仅当节点是树的叶子时,才能保持。 3)空。当节点不包含任何主体时,这会成立。 因此,数据是std ::变体

我编写了递归功能,该功能计算出作用于身体的净力。它在输入中获取node(在第一个调用,Quadtree的根)和body我们要查询,并返回vector2f表示代表作用在身体上的净力。 当然,该功能需要访问变体并派遣到正确的lambda。

Vector2f compute_approximate_net_force_on_body(const Node& node,
                                               const Body& body) {
  const auto visit_empty = [](const Empty&) -> Vector2f { return {0, 0}; };

  const auto visit_body = [body](const Body& visited) -> Vector2f {
    return compute_gravitational_force(visited, body);
  };

  const auto visit_region = [&](const Subquadrants& subquadrants) -> Vector2f {
    float distance = (body.m_position - node.center_of_mass()).norm();
    if (node.length() / distance < OMEGA) {
      // Approximation
      return bh::compute_gravitational_force(
          {node.center_of_mass(), node.total_mass()}, body);
    } else {
      return std::accumulate(
          subquadrants.begin(), subquadrants.end(), Vector2f{0, 0},
          [body](const Vector2f& total, const std::shared_ptr<Node>& curr) {
            return (total + compute_approximate_net_force_on_body(*curr, body))
                .eval();
          });
    }
  };

  return std::visit(overloaded{visit_empty, visit_body, visit_region},
                    node.data());
}

有趣的部分是累积的部分。从本质上讲,它以相同的节点和四个节点的子细分递归调用算法,并将结果累积到vector2f中。

由于这四个呼叫是完全独立的,所以我认为我可以平行计算。最初,我将累积的转换为redable,但后来发现这是行不通的,因为

  1. 二进制操作函数的签名类型必须相同(我的不是);
  2. 二进制操作必须是关联和交换性的(我的不是)。

我正在寻找有关如何使用STL库并平行递归调用的建议。如果可能的话,C ++标准必须为C ++ 17或以下。我想到的一种方法是使用std :: asyncstd :: Future,但它不如累积的 。还有其他吗?

谢谢您的见解。

I am developing a Barnes–Hut simulation in C++ for a course project. Here you can find a detailed explanation of the algorithm.

I briefly explain the datatypes I will use later.

  • A body is represented by the Body datatype, which is a struct containing a Vector2f representing the position, and a float for the mass).
  • A node in the quadtree is represented by the Node datatype. Of course, the node struct contains some data, which can be: 1) Its four children, corresponding to its four subquadrants. This holds only the node is a fork in the tree. 2) Its body. This holds only when the node is a leaf of the tree. 3) Empty. This holds when the node does not contain any body. Therefore, data is a std::variant.

I wrote the recursive function that calculates the net force acting on a body. It takes in input a Node (at the first call, the quadtree's root) and the Body we want to query, and returns a Vector2f representing the net force acting on the body.
Of course, the function needs to visit the variant and dispatch to the correct lambda.

Vector2f compute_approximate_net_force_on_body(const Node& node,
                                               const Body& body) {
  const auto visit_empty = [](const Empty&) -> Vector2f { return {0, 0}; };

  const auto visit_body = [body](const Body& visited) -> Vector2f {
    return compute_gravitational_force(visited, body);
  };

  const auto visit_region = [&](const Subquadrants& subquadrants) -> Vector2f {
    float distance = (body.m_position - node.center_of_mass()).norm();
    if (node.length() / distance < OMEGA) {
      // Approximation
      return bh::compute_gravitational_force(
          {node.center_of_mass(), node.total_mass()}, body);
    } else {
      return std::accumulate(
          subquadrants.begin(), subquadrants.end(), Vector2f{0, 0},
          [body](const Vector2f& total, const std::shared_ptr<Node>& curr) {
            return (total + compute_approximate_net_force_on_body(*curr, body))
                .eval();
          });
    }
  };

  return std::visit(overloaded{visit_empty, visit_body, visit_region},
                    node.data());
}

The interesting part is the one with accumulate. Essentially, it invokes the algorithm recursively, with the same node and the four node's subquadrants, and accumulates the result into a Vector2f.

Since that the four calls are completely independent, I thought that I could make the computation parallel. Initially, I converted the accumulate into a reduce, but I later discovered that this can't work because

  1. the types of the signature of the binary operation function must be identical (mine are not);
  2. The binary operation must be associative and commutative (mine is not).

I am looking for suggestions on how to parallelize the recursive calls, possibly using the STL library. If possible, the C++ standard must be C++17 or below. One approach that I have in mind is to use std::async and std::future, but it is less elegant than the accumulate-like one. Are there any other else?

Thank you for your insights.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文