Как сделать Intel TBB многофункциональным_узлом с динамическим количеством портов?

Я новичок в Библиотека Intel TBB. Как видите, мой вопрос связан с tbb :: flow :: graph. Мне нужно реализовать логику, как:

Пользователь рисует график с некоторыми логическими блоками. Каждый блок (узел) может иметь неограниченные соединения (ребра), поэтому каждый блок (узел) может выбирать, куда поместить данные дальше. Затем моя программа построит такой график с помощью библиотеки TBB и проведет расчеты.

Так что я не знаю, возможно ли построить узел (я думаю, это должен быть многофункциональный_узел) с динамическим числом выходных портов. Не могли бы вы показать мне, как это сделать, пожалуйста?

5

Решение

К сожалению, нет способа (без динамической компиляции) изменить количество выходных портов в многофункциональном узле. Вы можете создать максимальное количество портов (которое управляется макропереключателем и зависит от компилятора) и просто динамически подключаться к портам. Если вы делаете try_put с портом, и преемник не подключен, try_put завершается ошибкой, и вы можете реагировать на это во время выполнения.

Другой способ сделать это (хотя я думаю, что с некоторым разочарованием) — это построить двоичное дерево из двухпортовых многофункциональных узлов. Если вы используете класс с выходным адресатом в качестве поля, создайте каждый узел для реакции на один бит адресата и вывода на порт 0 или порт 1, в зависимости от результата маски. короткое замыкание планировщика будет относительно быстро направлять вывод через дерево, но вы заплатите немного штрафа за множественные динамические вызовы.

Или вы можете использовать другую базу, кроме 2 (например, 10).

Приложение: Поговорив с Майком (разработчиком flow :: graph), мы поняли, что есть еще один способ справиться с этим, который бы позволял динамическое количество портов. Вы должны будете сделать немного вещей низкого уровня, но это выглядит так:

#include "tbb/tbb.h"#include <iostream>

using namespace tbb::flow;

tbb::spin_mutex io_lock;
typedef broadcast_node<int> bnode_element_t;
typedef tbb::concurrent_vector<bnode_element_t *> output_port_vector_t;
struct multioutput_function_body {
output_port_vector_t &my_ports;
public:
multioutput_function_body(output_port_vector_t &_ports) : my_ports(_ports) {}
multioutput_function_body(const multioutput_function_body &other) : my_ports(other.my_ports) { }
continue_msg operator()(const int in) {
int current_size = my_ports.size();
if(in >= current_size) {
// error condition?  grow concurrent_vector?
tbb::spin_mutex::scoped_lock gl(io_lock);
std::cout << "Received input out of range(" << in << ")" << std::endl;
}
else {
// do computation
my_ports[in]->try_put(in*2);
}
return continue_msg();
}
};

struct output_function_body {
int my_prefix;
output_function_body(int i) : my_prefix(i) { }
int operator()(const int i) {
tbb::spin_mutex::scoped_lock gl(io_lock);
std::cout << " output node "<< my_prefix << " received " << i << std::endl;
return i;
}
};

int main() {
graph g;
output_port_vector_t output_ports;
function_node<int> my_node(g, unlimited, multioutput_function_body(output_ports) );
// create broadcast_nodes
for( int i = 0; i < 20; ++i) {
bnode_element_t *bp = new bnode_element_t(g);
output_ports.push_back(bp);
}

// attach the output nodes to the broadcast_nodes
for(int i = 0; i < 20; ++i) {
function_node<int,int> *fp = new function_node<int,int>(g, unlimited, output_function_body(i));
make_edge(*(output_ports[i]),*fp);
}

for( int i = 0; i < 21; ++i) {
my_node.try_put(i);
}
g.wait_for_all();
return 0;
}

Примечания к вышесказанному:

  • Мы создаем concurrent_vector указателей на broadcast_nodes, Преемники function_node прикреплены к этим broadcast_nodes, Выход из function_node игнорируется
  • Concurrent_vector передается в конструктор multioutput_function_body, В этом случае нам вообще не нужен многофункциональный_узел. multioutput_function_body решает, какой broadcast_node в try_put во время выполнения. Заметка мы делаем явное try_puts к broadcast_nodes, Это приводит к тому, что задача создается для каждого try_put, Порожденные задачи выполняются быстрее, чем задачи, поставленные в очередь, но затраты на планирование больше, чем просто возвращение значения из узла.
  • Я не добавил очистку выделенной кучи broadcast_nodes и выход function_nodes, «Очевидное» место для удаления broadcast_nodes будет в деструкторе multioutput_function_body, Вы не должны делать это, так как создание function_node приводит к созданию копии переданных функций и множественных копий function_body будет иметь ссылку на concurrent_vector из broadcast_node указатели. Делать удаление после g.wait_for_all(),

я использовал concurrent_vector потому что это позволяет получить доступ к указателям в то время как concurrent_vector в настоящее время изменяется. Вопрос о том, является ли дополнительный broadcast_node указатели могут быть добавлены во время выполнения графика. Я надеюсь, что вы только создаете узлы и используете их как есть, а не изменяете их на лету. concurrent_vectors не перераспределять и перемещать уже инициализированные элементы при наращивании структуры; Вот почему я использовал его, но не думайте, что это полный ответ, если вы надеетесь добавить дополнительные узлы во время работы графика.

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]