Skip to content

Conversation

@kboyarinov
Copy link
Contributor

Description

Rewrite Flow Graph join_node and indexer_node to use variadic templates.
Follow up for #437

Fixes # - issue number(s) if exists

  • - git commit message contains an appropriate signed-off-by string (see CONTRIBUTING.md for details)

Type of change

Choose one or multiple, leave empty if none of the other choices apply

Add a respective label(s) to PR if you have permissions

  • bug fix - change that fixes an issue
  • new feature - change that adds functionality
  • tests - change in tests
  • infrastructure - change in infrastructure and CI
  • documentation - documentation update

Tests

  • added - required for new features and some bug fixes
  • not needed

Documentation

  • updated in # - add PR number
  • needs to be updated
  • not needed

Breaks backward compatibility

  • Yes
  • No
  • Unknown

Notify the following users

List users with @ to send notifications

Other information

@knoepfel
Copy link
Contributor

knoepfel commented Dec 6, 2023

What is the status of this PR?

@kboyarinov
Copy link
Contributor Author

@lin72h @knoepfel

Thank you for expressing your interest into this PR.
Current status of this change for us is a low priority enhancement of the FG implementation and there are still things that should be investigated, e.g., backward compatibility.
Could you please share your use cases that finds this change useful if any? It would be very helpful for pushing this activity forward.

@knoepfel
Copy link
Contributor

knoepfel commented Dec 13, 2023

@kboyarinov, thanks for reaching out. I have been using oneTBB flow for a while, and have greatly enjoyed it. One of the use cases I have is that some algorithms/nodes invoke library code that is not thread-safe. It is not sufficient to simply specify a concurrency level of 1 for that node, because there are other nodes that might also call the same library at the same time. Instead, for nodes that have to call the same thread-unsafe resource, I use a join node where one of the input ports receives a serialization token--only one token exists in the full graph for a given thread-unsafe library, and the node will not execute until it receives the token. And sometimes more than one token is required if there are more than one thread-unsafe libraries that must be invoked by the node.

I also use join nodes, though, with tag-matching to ensure that all of the arguments required by a particular algorithm are available before the algorithm is executed. If the algorithm requires $m$ serialization tokens, with the current implementation of the join node, that leaves $10-m$ ports for algorithm arguments. Hopefully, an algorithm will not need a significant number of serialization tokens, and a large number of algorithm arguments, but with the current implementation, I have to think about the 10-port max restriction, instead of just letting the compiler specify a maximum number of arguments as allowed in a variadic parameter pack.

Does that help explain the situation? I can also produce a diagram if that would be helpful.

@kboyarinov kboyarinov added this to the 2022.4.0 milestone Oct 31, 2025
@kboyarinov kboyarinov changed the title WIP: Flow Graph - remove C++03 legacy Flow Graph - remove C++03 legacy Oct 31, 2025
Comment on lines 816 to 835
template <std::size_t N>
struct edge_maker {
template <typename Sender, typename NodeType>
static void make(Sender& sender, NodeType& node) {
oneapi::tbb::flow::make_edge(sender, oneapi::tbb::flow::input_port<N - 1>(node));
edge_maker<N - 1>::make(sender, node);
}

template <typename Sender, typename NodeType>
static void make(std::vector<Sender>& senders, NodeType& node) {
oneapi::tbb::flow::make_edge(senders[N - 1], oneapi::tbb::flow::input_port<N - 1>(node));
edge_maker<N - 1>::make(senders, node);
}
};

template <>
struct edge_maker<0> {
template <typename Sender, typename NodeType>
static void make(Sender&, NodeType&) {}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move graph-related helpers into common "graph-helpers" header?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to common/graph_utils.h

Comment on lines +398 to +400
for (auto& buffer : buffers) {
make_edge(submitter, buffer);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a helper in edge_maker for this as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep it as-is since it can be implemented as a runtime loop and edge_maker is intended to implement a compile-time loop since input_port<N> should be used. I don't think it makes sense to overcomplicate it by implementing using a compile-time logic.


std::size_t body_counter = 0;
function_node<output_tuple> function(g, serial, [&](const output_tuple& tuple) {
tuple_assert<N>(tuple, message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to something more readable? For example,

Suggested change
tuple_assert<N>(tuple, message);
assert_all_items_equal_to<N>(tuple, message);

Similarly to edge_maker, this can also be extended to support other containers.
By the way, std::get already supports other collections: std::array, std::pair, std::variant, etc. So, such helper will automatically support them.
Therefore, consider renaming (since tuple could already be not necessarily a tuple) and moving this useful helper to some common place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed tuple_assert, moved to common header and renamed first argument to be tuple-like.
I would not add tuple-like support to edge_maker now since it will require adding constraint that std::get is valid to disambiguate with the existing overload of edge_maker::make. It is not used in the existing tests, so I would it as-is for now.

Comment on lines 1524 to 1528
unfolded_join_node(graph &g)
: base_type(g,
func_initializer_type(new type_to_key_function_body_leaf<Types, K,
key_from_message_body<K, Types>>(key_from_message_body<K, Types>())...))
{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also replace type_to_key_function_body[_leaf] types with std::function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a good idea, but I would consider it as part of the separate PR.
Otherwise, this one would be overcomplicated

kboyarinov and others added 14 commits November 4, 2025 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants