How to: TBB node with multiple asynchronous inputs and multiple outputs - c++

I am new to Threading Building Blocks (TBB); I would need to implement the following logic with TBB nodes:
A node of type N receives two inputs; for instance:
1. std::vector // data
2. bool // flag
These inputs come asynchronously.
If the input is of type 1, process the data owned by the node of type N to produce two outputs, for instance:
a. std::vector
b. int
If the input is of type 2, process the data owned by the node of type N to produce one output, say a std::vector.
I have been trying to formulate the input part using a tbb::flow::or_node, and the output part using tbb::flow::multifunction_node.
If there is only one input and multiple outputs, this logic can be written with tbb::flow::multifunction_node (I tested, it works). If there is one output, and multiple inputs, I found example of code illustrating solutions. However, it is not clear to me how the case of multiple asynchronous inputs and multiple outputs can be implemented with the TBB framework. Suggestions welcome.

You should be able to do what you want with the current implementation of or_node. (We are re-designing the output of the or_node to make it more friendly, but we need input from users like you on issues with the or_node Community Preview Feature.)
One thing to remember is to turn on the CPF when you are compiling code with the or_node. The switch is -DTBB_PREVIEW_GRAPH_NODES=1 .
# define TBB_PREVIEW_GRAPH_NODES 1 // necessary to turn on the or_node community Preview Feature.
#include "tbb/flow_graph.h"
#include <vector>
using namespace tbb::flow;
// The output format of the or_node is a struct that contains
// 1. the index of the input that the message appeared on, and
// 2. a tuple, the (i-1)th element of which is the message received
typedef or_node<tuple<std::vector<double>, bool> > my_or_node_type;
// it wasn't clear from the description if you wanted to differentiate between the vectors output with
// an input of type 1. or type 2. If you need to do that you can add an extra output port to the multifunction_node.
typedef multifunction_node<my_or_node_type::output_type, tuple<std::vector<double>, int> > my_mf_node_type;
struct mf_node_body {
void operator()(const my_or_node_type::output_type &in, my_mf_node_type::output_ports_type &op) {
switch(in.indx) {
case 0: {
// do the operation for the first input (the std::vector) The vector will be in
// get<0>(in.result). Remember you are copying vectors here, so if you have big
// vectors you will probably want to do some buffer management on your own and
// pass refs to the vector instead.
}
break;
case 1: {
// do the operation signaled by the second input (the bool.) The value of the
// input is in get<1>(in.result).
}
break;
}
}
};
main() {
graph g;
my_or_node_type multi_in(g);
my_mf_node_type multi_out(g, unlimited, mf_node_body());
// if the vector-producing node is called vpn, you attach it to the 0-th input of the or_node with
// make_edge(vpn, input_port<0>(multi_in));
//
// the bool-producing node bn can be attached similarly:
// make_edge(bn, input_port<1>(multi_in);
//
// attach the multi-in to the multi-out:
// make_edge(multi_in, multi_out);
//
// attach the vector consumer node vcn
// make_edge(output_port<0>(multi_out), vcn);
//
// attach the integer output to the int consuming node icn
// make_edge(output_port<1>(multi_out), icn);
//
// start up the graph and make sure to do a wait_for_all() at the end.
}
Remember that the multifunction_node body is invoked in parallel, so the work it does should not have race conditions (unless you want race conditions for some reason.) You can make the node body execute serially by constructing it with serial instead of unlimited. And the only way to ensure you can safely destroy the graph is to make sure no tasks are executing any of the nodes. The best way to do this is to do a g.wait_for_all().
Regards,
Chris
P.S. - one addendum. If the multifunction_node is defined serial, it will have an input buffer unless you explicitly exclude it. This may change the behavior of your graph if you are not expecting the buffer to be there.

Related

Generate a unique ID C++

I want to generate a unique identifier "ident" for my complex structure, how can i do that?
in my header, the complexe structure is:
struct Complexe {
float x;
float y;
static unsigned int ident;
};
void Init(Complexe&);
etc...
and in the cpp file, i need to attribute ident a unique int
Init(Complexe&z){
z.ident = 0;
z.y = 0;
z.x = 0;
};
May I recommend you std::hash?
std::size_t ident = std::hash<Complex>()(complexVar);
Writing it from memory but it should return you unique value (with very small chance of it being not) for each Complex type object.
Consider UUID, specifically uuid_generate on GNU/Linux, or (it seems) UuidCreate on Windows.
Generating a unique id is easy even if you want to write your own algorithm. Though your algorithm will need some thoughts if the environment you are working in is multi-threaded. In this case you will need to write a thread safe code. for example below code will generate a unique id and is also thread safe:
class Utility {
public :
static int getUniqueId();
};
int Utility::getUniqueId() {
static std::atomic<std::uint32_t> uid { 0 };
return ++uid;
}
One simple way is to just make a free-list to help you reuse IDs. So for instance you start at ID 0 and whenever you create a structure, you first check the free-list for any released IDs. It will be empty the first time through so you increment the ID counter to 1 and use that for your new structure. When you destroy a structure it's ID goes back on the free-list (which can be implemented as a stack), so that the next time you create a structure that ID will be reused. If the free-list runs out of IDs you just start incrementing the ID counter from where you left off....wash, rinse and repeat. The nice thing about this method is that you will never wrap integer range and accidentally use an already in use ID if your program ends up running a long time. The main down side is the extra storage for the stack, but the stack can always grow as you need it.

How to pass value with std::move in lambda function

First of all I apologize if it gets too long, as the problem can be detailed quickly, however, I describe a good part of the environment to perhaps come and help.
I'm developing a tool in C++ that analyzes genomic data (FASTQ data specifically). In case you don't know, a FASTQ file contains raw reads of genetic sequence, where each set of 4 lines of this file represents a read of the genome.
Well, as the processing of a read does not depend on other reads (in my case), I intend to parallelize the work with threads to save processing time.
When searching, I found some libraries ready and I'm trying to adapt. Right now I'm trying to use Thread Pool (specifically this lib -> https://github.com/log4cplus/ThreadPool/) but I'm having some problems with the integration.
I will detail the general algorithm a little.
Each iteration in the FASTQ file takes a set of 4 lines from the file and stores it in this struct.
struct READ {
std::string name; // Small size
std::string comment; // Small size
std::string seq; // String ranging between 0 and 1000 characters
std::string which; // The same amount of 'seq' characters
};
That is immediately passed as a value to the foreign function to perform the (non-trivial) task managed by the Thread Pool.
ThreadPool pool (4); // Creating thread pool with 4 threads
while (exist_read) // as long as there are reads to be processed -> read set of 4 lines
{
Read read;
read = next_read (); // read will contain 4 lines of the file read in the iteration
pool.enqueue ([read] {
Search (read); // search function performs non-trivial search work
});
}
As can be seen, I passed the values ​​in the lambda function and tested it with several threads, but I ended up having inferior performance when using single-thread.
So I was suggested to use a movable object. And with great help, I got this struct.
struct READ
{
READ () = default;
READ (READ &&) = default;
READ & operator = (READ &&) = default;
std::string name;
std::string comment;
std::string seq;
std::string which;
};
And the lambda function has changed to:
pool.enqueue ([r {std::move(read)}] {
Search (std::move(r));
});
In addition, did update the reading of the FASTQ file of:
[...]
Read read;
read.name = lines[0];
read.seq = lines[1];
read.comment = lines[2];
read.qual = lines[3];
[...]
To:
[...]
Read read;
read.name = std::move(lines [0]);
read.seq = std::move(lines [1]);
read.comment = std::move(lines [2]);
read.qual = std::move(lines [3]);
[...]
However, I still haven't been able to test the performance, because I'm having the following print_error
error: function "READ::READ (const READ &)" (declared implicitly)
cannot be referenced - it is a deleted function C / C ++ (1776)
I searched other forums but I couldn't find an answer that would suit my problem.
If anyone can help me with this problem I will be very grateful. I hope I have been clear.
In addition, I also accept suggestions for other alternatives.
Thanks.

Tuples: No matching function for call to 'get'

I have 3 structs : Student, Citizen, Employee. I want user to be able to choose what struct they want to work with (std::vector of structs, actually). Since there's no way to define type at runtime, I created all 3 vectors, but will use only one of them (depending on the user's choice), others will stay empty:
std::vector<Student> container_student;
std::vector<Citizen> container_citizen;
std::vector<Employee> container_employee;
auto containers = make_tuple(container_student, container_citizen, container_employee);
std::cout << "Enter:\n0 to operate \"Student\" struct\n1 to operate \"Citizen\" struct\n2 to operate \"Employee\" struct\n";
std::cin >> container_type;
auto container = std::get<container_type>(containers);
But I get No matching function for call to 'get', even though container_type is an int and containers is a tuple.
Edit: understandable, auto can't make magic and I still try to make container's type to depend on runtime. But even if I try to use std::get<container_type>(containers) (probably define would help) instead of container in functions etc., I get the same error, which is not understandable.
Unfortunately, what you're proposing isn't possible in C++. The C++ typing and template system works at compile-time, where information read in from the user isn't available. As a result, anything passed into a template's angle braces needs to be determinable at compile-time. In your case, the number the user enters, indicating which option they want to select, is only knowable at runtime.
There are some routes you could take to achieve the same result, though. For example, one option would be to do something like this:
if (container_type == 0) {
auto container = std::get<0>(containers);
/* ... */
} else if (container_type == 1) {
auto container = std::get<1>(containers);
/* ... */
} /* etc */
Here, all the template angle braces are worked out at compile-time. (Then again, if this is what you're going to be doing, you wouldn't need the tuple at all. ^_^)
Another option would be to use templates, like this:
template <typename T> void doSomething(std::vector<T>& container) {
/* Put your code here */
}
/* Then, back in main... */
if (container_type == 0) {
doSomething(container_student);
} else if (container_type == 1) {
doSomething(container_citizen);
} /* etc */
This still requires you to insert some code to map from integer types to the functions you want to call, but it leaves you the freedom to have a container variable (the one in doSomething) that you can treat generically at that point.
It's basically the Fundamental Theorem of Software Engineering in action - all problems can be solved by adding another layer of indirection. :-)
Hope this helps!

Concurrent_hash_map implementation throwing SIGSEGV

I am trying to use tbb’s concurrent_hash_map to increase my application’s concurrent performance. Read up about it and implemented it according to my application but I am seeing crashes..
So, my application is a multi-threadedd application where I am storing pairs,the key is char* and the value is an integer. Pseudocode looks like this:
In .h file,
typedef tbb::concurrent_hash_map<const void*, unsigned, Hasher> tbb_concurrent_hash;
tbb_concurrent_hash concurrent_hash_table;
tbb_concurrent_hash::accessor write_lock;
tbb_concurrent_hash::const_accessor read_lock;
In .c file,
void storeName(const char * name) {
int id=0;
// This creates a pair object of name and index
std::pair object(name, 0);
// read_lock is a const accessor for reading. This find function searches for char* in the table and if not found, create a write_lock.
bool found = concurrent_hash_table.find(read_lock, name);
if (found == FALSE) {
concurrent_hash_table.insert(write_lock, name);
// Get integer id somehow.
id = somefunction();
write_lock->second = id;
write_lock.release();
} else {
// if the name is found in the table then get the value and release it later
id = read_lock->second;
read_lock.release();
}
}
As per my understanding, I am good with the implementation but as I said, there are multiple crashes coming when find returns me FALSE. Crash have traces of mutexs as well.
Your 'locks', i.e. accessors are declared global in .h file. so, basically you write to a shared scoped_lock instance... which logically leads to a data race. Accessors are like fused std::shared_ptr and std::scoped_lock classes in one, or simpler - a pointer for your result and a lock guard for the data it points. You don't want to use one global pointer from multiple threads. Declare them locally in a scope you want to have that access (and you'd not need to call .release() as well)
Another problem is the data race between find() and insert (). Two or more threads can decide that they have to insert since they found nothing. In this case, the first thread will insert the new element while other threads will return existing element because insert() acts as find() if there's existing element. The problem is that your code doesn't account for that.
I can see why you might want to double check using const_accessor as the read lock is more scalable. But instead, you might want to use bool insert( const_accessor& result, const value_type& value ); with read lock (const_accessor) and value_type instead of a key only, which will initialize the whole pair in the case when a new element is added.

Is it possible to use WinRT objects in STL-like containers?

I'm trying to create a simple gesture recognizer for a D3D application. The gesture recognizer works by storing each point received into a boost::circular_buffer of capacity 3 and then counting the number of similar FrameID's in the buffer, like so:
UINT Trackball::CalculateGestureSize(Windows::UI::Input::PointerPoint ^ pPoint)
{
// shift the circular buffer queue one if it's full (common case)
if (m_pointQueue.full())
{
m_pointQueue.pop_back();
}
// then store our point
m_pointQueue.push_front(*pPoint);
// now we need to see how many of the points in the
// circular buffer match the frame Id
UINT gestureLength = 0;
for (UINT i = 0; i < MAX_GESTURE_SIZE; i += 1)
{
if (m_pointQueue[i].FrameId == pPoint->FrameId)
{
gestureLength += 1;
}
}
assert(gestureLength != 0);
return gestureLength;
}
However, the compiler is unable to figure out how to instantiate this type:
// a queue of size 3 that helps determine what kind of gesture we're working with
boost::circular_buffer<Windows::UI::Input::PointerPoint> m_pointQueue;
because & and * cannot be used on WinRT objects:
boost/concept_check.hpp(195): error C3699: '&' : cannot use this indirection on type 'const Windows::UI::Input::PointerPoint' compiler replacing '&' with '^' to continue parsing
The compiler's error list then grows long very quickly due to the cascading effects of that error.
Right now, my solution is copy the necessary information for a PointerPoint into a struct and use that as the boost::circular_buffer's typename, like so:
// So WinRT objects (like Windows::UI::Input::PointerPoint) can't
// be used in STL-like containers (like boost::circular_buffer)
// because * and & operators cannot be used on them, so I'm copying
// the necessary info into this struct and using that instead
typedef struct
{
UINT FrameId;
Windows::Foundation::Point Position;
} LocalPoint;
// a queue of size 3 that helps determine what kind of gesture we're working with
boost::circular_buffer<LocalPoint> m_pointQueue;
This definitely works, but I was wondering if there's a better solution out there.
Thanks for reading and for trying to help.
If you want to put a reference type in an STL collection, you need to use the ^ form. So you'd use: boost::circular_buffer<PointerPoint^> instead of boost::circular_buffer<PointerPoint>. A Windows::Foundation::Point is a value type so it can be used in a collection directly.
I think I accidentally found a working solution by using that Windows::Foundation::Point object in my LocalPoint struct. Just wrap the WinRT object with a struct and then the operators will work just fine, but it'll add a bit of syntactic noise.
However I'm still listening for a better solution, but I'll leave this here till then.