1

I am very new to modern C++ library, and trying to learn how to use std::async to perform some operations on a big pointer array. The sample code I have written is crashing at the point where the async task is launched.

Sample code:

#include <iostream>
#include <future>
#include <tuple>
#include <numeric>


#define maximum(a,b)            (((a) > (b)) ? (a) : (b))

class Foo {
    bool flag;

public:

    Foo(bool b) : flag(b) {}

    //******
    //
    //******
    std::tuple<long long, int> calc(int* a, int begIdx, int endIdx) {
        long sum = 0;
        int max = 0;

        if (!(*this).flag) {
            return std::make_tuple(sum, max);
        }

        if (endIdx - begIdx < 100)
        {
            for (int i = begIdx; i < endIdx; ++i)
            {
                sum += a[i];
                if (max < a[i])
                    max = a[i];
            }
            return std::make_tuple(sum, max);
        }

        int midIdx = endIdx / 2;
        auto handle = std::async(&Foo::calc, this, std::ref(a), midIdx, endIdx);
        auto resultTuple = calc(a, begIdx, midIdx);
        auto asyncTuple = handle.get();

        sum = std::get<0>(asyncTuple) +std::get<0>(resultTuple);
        max = maximum(std::get<1>(asyncTuple), std::get<1>(resultTuple));

        return std::make_tuple(sum, max);
    }

    //******
    //
    //******
    void call_calc(int*& a) {
        auto handle = std::async(&Foo::calc, this, std::ref(a), 0, 10000);
        auto resultTuple = handle.get();

        std::cout << "Sum = " << std::get<0>(resultTuple) << "  Maximum = " << std::get<1>(resultTuple) << std::endl;
    }
};

//******
//
//******
int main() {
    int* nums = new int[10000];
    for (int i = 0; i < 10000; ++i)
        nums[i] = rand() % 10000 + 1;

    Foo foo(true);
    foo.call_calc(nums);

    delete[] nums;
}

Can anyone help me to identify why does it crash? Is there any better approach to apply parallelism to operations on a big pointer array?

  • 1
    Can explain briefly what is the code suppose to do? Why do you want async in recursion ? – Sumit Jha May 09 '18 at 10:47
  • I don't see a ```async``` specification which takes a member function pointer and class pointer : https://en.cppreference.com/w/cpp/thread/async – Robert Andrzejuk May 09 '18 at 11:18
  • Aaaa but function should be able to handle it :) just learnt something new. – Robert Andrzejuk May 09 '18 at 11:25
  • Please show where exactly it crashes. I see 2 async calls. – Robert Andrzejuk May 09 '18 at 11:27
  • @RobertAndrzejuk it crashes in recursive async call – Peter Abraham May 09 '18 at 11:31
  • @SumitJha the code calculates the sum of all elements and maximum value in the array. Sum and max are just some random operations I chose. What I wanted to learn is how to use `async` to perform calculations in a pointer array. I thought calling `async` in recursion is more handy to manage the segmenting the array than an iterative approach. – Peter Abraham May 09 '18 at 11:37
  • I think there is some limitation on number of threads and total stack size available which is creating the problem here. But I am not sure. The code works on my computer for data size of 198 but fails afterward. Recursive thread is not a good option as you will run out of limit of that your CPU supports. – Sumit Jha May 09 '18 at 12:16
  • Your algorithm is very difficult to follow, as it is spawning tasks inside already created tasks. No end condition can be found. (Traces show of calling the same functions with the same values). Please consider a rewrite with a more clearer algorithm. – Robert Andrzejuk May 09 '18 at 12:22

2 Answers2

2

The fundamental problem is your code wants to launch more than array size / 100 threads. That means more than 100 threads. 100 threads won't do anything good; they'll thrash. See std::thread::hardware_concurrency, and in general don't use raw async or thread in production applications; write task pools and splice together futures and the like.

That many threads is both extremely inefficient and could exhaust system resources.

The second problem is you failed to calculate the average of 2 values.

The average of begIdx and endIdx is not endIdx/2 but rather:

int midIdx = begIdx + (endIdx-begIdx) / 2;

Live example.

You'll notice I discovered the problem with your program by adding intermediate output. In particular, I had it print out the ranges it was working on, and I noticed it was repeating ranges. This is known as "printf debugging", and is pretty powerful especially when step-based debugging isn't (with this many threads, stepping through the code will be brain-numbing)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

The problem with async calls is that they are not done in some universe where an infinite amount of tasks can be executed all at the exact same time.

Async calls are executed on a processor which has a certain amount of processors/cores and the async calls have to be lined up to be executed on them.

Now here is where problems of synchronization, and the problems of blocking, starvation, ... and other multithreaded issues come into play.

Your algorithm is very difficult to follow, as it is spawning tasks inside already created tasks. Something is happening, but it is difficult to follow.

I would solve this problem by:

  1. Creating a vector of results (which will be from async threads)
  2. In a loop execute the async calls (assigning the result to the vector)
  3. Afterwards loop through the reuslts vector gathering the results
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31