Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
2467 Discussions

Is it possible to run a dynamically created number of functions together in a single task?

Victor_D_
New Contributor I
391 Views

I have a function foo() and would like to run multiple instances of foo() in a single task. Sometimes it will be 3 foo() instances and at other times it may be 6 instances, or 8, or 9. It is not known in advance how many instances of foo() to run in a single task.

Each instance of foo() also needs to have its arguments be passed by value.

Is this possible to do with TBB?

Basically, I would like to group several functions together and run them in a single task. And, then I would like to do it again and again, with different number of functions per each task to be run in parallel. The functions are actually the same, but with different argument values.

Thank you,

-Victor

0 Kudos
1 Solution
Mark_L_Intel
Moderator
323 Views

Something like this (modified previous version to hopefully better reflect what you are asking, lines 22-26)?

 

#include <iostream>
#include <tbb/task_group.h>
#include <vector>
#include <functional>

void foo(int arg) {
    std::cout << "arg = " << arg << std::endl;
}

int main() {
    tbb::task_group g;

    // Create a vector of function calls
    std::vector<std::function<void()>> function_bundles;

    // Populate the vector with function calls
    function_bundles.push_back([] { foo(1); });
    function_bundles.push_back([] { foo(2); });
    // Add more function calls as needed

    // Run all function calls as a single task
    g.run([&function_bundles] { 
        for (const auto& func : function_bundles) {
            func(); // Execute the function
        }
    });

    g.wait(); // Wait for all tasks to finish
}

 

 

 

 

  

View solution in original post

0 Kudos
4 Replies
Mark_L_Intel
Moderator
344 Views

@Victor_D_ ,

 

does this help:

 

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

void foo(int arg) {
   std::cout << "arg = " << arg << std::endl;
}


int main() {
    tbb::task_group g;

    // args is a vector of arguments you want to pass by value
    std::vector<int> args = { 1, 2, 3, 4, 5 };

    for (int arg : args) {
        g.run([=] { foo(arg); }); // Capture arg by value
    }

    g.wait(); // Wait for all tasks to finish
    }
}

 

 

For more in-depth regarding task_group: migrating to task_group 

For example of using task_group:  task_group sample 

 

0 Kudos
Victor_D_
New Contributor I
334 Views

Thank you Mark. I use task groups in the above way already and they are great for one function to one task mapping.

How would you pass two or more calls (dynamically determined how many calls) to a single g.run([=]) instance, with each call to foo() with its own arguments?

Basically, this would be a method of running a bundle/vector/list of function calls by a single task. This comes up in cases where there are many instances of a function are used, but each instance is too small to be processed efficiently in parallel on its own, so it would be more efficient to bundle several small instances and have one worker/task run each bundle.

0 Kudos
Mark_L_Intel
Moderator
324 Views

Something like this (modified previous version to hopefully better reflect what you are asking, lines 22-26)?

 

#include <iostream>
#include <tbb/task_group.h>
#include <vector>
#include <functional>

void foo(int arg) {
    std::cout << "arg = " << arg << std::endl;
}

int main() {
    tbb::task_group g;

    // Create a vector of function calls
    std::vector<std::function<void()>> function_bundles;

    // Populate the vector with function calls
    function_bundles.push_back([] { foo(1); });
    function_bundles.push_back([] { foo(2); });
    // Add more function calls as needed

    // Run all function calls as a single task
    g.run([&function_bundles] { 
        for (const auto& func : function_bundles) {
            func(); // Execute the function
        }
    });

    g.wait(); // Wait for all tasks to finish
}

 

 

 

 

  

0 Kudos
Victor_D_
New Contributor I
212 Views

Thank you Mark. The above implementation is fabulous and is much more advanced than one I came up with. It supports any "foo" function, and even supports multiple functions (such as foo and bar) to be used, each with its own arguments, and any kinds of arguments. Pretty amazing!

One thing that I changed in my usage is using pass by value instead of reference, since I'm creating many bundles. This creates a copy of the bundle for each task. Sadly, it's an additional overhead, and maybe an improvement that can be made.

I added this code, with credit, to a recent blog about this topic (https://duvanenko.tech.blog/2024/04/15/parallel-pattern-of-bundling-small-work-items/) - still in the works.

 

Thank you for your help! This solution will definitely be used.

-Victor

0 Kudos
Reply