2

Giving a bit of context. I'm using c++17. I'm using pointer T* data because this will interop with cuda code. I'm trying write a parallel version (on CPU) of a histogram creator. The sequential version:

template <class T>
vector<uint> Histogram<T>::SortDataToHist(T* data, size_t size)
{
    vector<uint> bars{};
    bars.resize(BarCount); // BarCount is the number of histogram bars

    for (int i = 0; i < size; ++i) // size is the count of elements (in data*) to sort
    {
        // given the value of data[i] GetBarIndex will tell which bar it belongs, for counting
        auto idx = GetBarIndex(data[i]);
        // counting
        bars[idx] += 1u;
    }
    return bars;
}

The parallel version splits the consideration of data array (read only) for several threads, sorts each sub array into local histograms and then merge (reduce) each into one final histogram. There is no need for mutex.

template <class T>
vector<uint> Histogram<T>::SortDataToHistPar(T* data, size_t size, int threadsCount)
{
    vector<uint> bars{};
    bars.resize(BarCount);

    auto indexes = GetIndexes(size, threadsCount);
    vector<future<vector<uint>>> futures{};
    
    // loop to start threads
    for (int i = 0; i < indexes.size() - 1; i++)
    {
        int idxA = indexes[i];
        int idxB = indexes[i + 1];
        future<vector<uint>> future = async(LocalSortHist, data, idxA, idxB);
        // Error C3867 'Histogram<float>::LocalSortHist': non-standard syntax; use '&' to create a pointer to member
        // Error C2672 'std::async': no matching overloaded function found

        futures.push_back(future);
    }
    
    // loop to collect threads results
    for (int i = 0; i < threadsCount; ++i)
    {
        auto result = futures[i].get();
        for (int r = 0; r < BarCount; ++r)
            bars[r] += result[r];
    }
    return bars;
}

I could not find a way to use LocalSortHist as argument for async. As written, I get:

  • Error C3867 'Histogram<float>::LocalSortHist': non-standard syntax; use '&' to create a pointer to member
  • Error C2672 'std::async': no matching overloaded function found

and with &Histogram<T>::LocalSortHist (yes a template function has no address..) it yields:

  • Error C2672 'async': no matching overloaded function found
  • Error C2440 'initializing': cannot convert from 'std::vector<std::seed_seq::result_type,std::allocator<std::seed_seq::result_type>> (__cdecl Histogram<float>::* )(T *,uint,uint)' to 'std::launch'
  • Error C2893 Failed to specialize function template 'std::future<_Select_invoke_traits<decay<_Ty>::type,decay<_ArgTypes>::type...>::type> std::async(_Fty &&,_ArgTypes &&...)'

With async(LocalSortHist<T> it gives:

  • Error C2275 'T': illegal use of this type as an expression
  • Error C2059 syntax error: ','

How can I use LocalSortHist in several threads like this or so ?

For consideration, LocalSortHist. The range [idxA, idxB] is the local consideration of the data array for "local sorting" or local histogram generation.

template <class T>
vector<uint> Histogram<T>::LocalSortHist(T* data, uint idxA, uint idxB)
{
    vector<uint> bars{};
    bars.resize(BarCount);
    for (uint i = idxA; i < idxB; ++i)
    {
        auto idx = GetBarIndex(data[i]);
        bars[idx] += 1u;
    }
    return bars;
}

And GetIndexes:

template <class T>
vector<int> Histogram<T>::GetIndexes(size_t size, int threadsCount)
{
    vector<int> pidx{};
    int w = size / threadsCount;
    int idx;
    while(idx < size)
    {
        pidx.push_back(idx);
        idx += w;
    }
    if (idx != size - 1)
        pidx.push_back(size - 1);
    return pidx;
}

A AAA test method:

    TEST_METHOD(SortDataToHistParSOTest)
    {
        std::default_random_engine generator{};
        std::normal_distribution<float> distribution(15.0, 5.0);
        size_t sampleSize = 80000;
        size_t sampleSizeBytes = sampleSize * sizeof(float);
        float* samples = (float*)malloc(sampleSizeBytes);
        for (int i = 0; i < sampleSize; ++i)
        {
            float number = distribution(generator);
            samples[i] = number;
        }
        MinMax<float> mm;
        mm.Min = 0.0f;
        mm.Max = 30.0f;
        Histogram sut(mm, 15);

        auto hist = sut.SortDataToHistPar(samples, sampleSize, 16);

        wstringstream s{};
        for (auto x : hist)
            s << x << L" ";
        Logger::WriteMessage(s.str().c_str());
    }
Soleil
  • 6,404
  • 5
  • 41
  • 61
  • [How to use std::async on a member function?](https://stackoverflow.com/a/13669113/3893262) plus `futures.push_back(future);`: `std::future` is not copyable, it needs to be moved. – O'Neil Jun 16 '21 at 00:55
  • As a quick FYI, posting the error codes is not really "stackoverflow-friendly". You would be a lot more likely to get quick and good answers for your questions if you put the actual error text, alongside which line the compiler is complaining about (even if it looks unrelated to you), directly in the question. –  Jun 16 '21 at 12:27

1 Answers1

3

The issue you are having has nothing to do with templates. You cannot invoke std::async() on a member function without binding it to an instance. Wrapping the call in a lambda does the trick.

Here's an example:

#include <future>

class MyClass {
public:
  template<typename T>
  int foo(T arg) {
      return 12;
  }

  int bar() {
    auto fut = std::async([this](auto arg){return foo(arg);}, "HI");

    return fut.get();
  }
};
  • Neat trick ! But I get a C2280 with `async([this](auto x, uint a, uint b) { return LocalSortHist(x, a, b); }, data, idxA, idxB);` (attempting to reference a deleted function) – Soleil Jun 16 '21 at 00:59
  • You don't actually need the lambda. You can also do (for example) `auto fut = std::async (&MyClass::foo , this, "HI");`. – Paul Sanders Jun 16 '21 at 01:03
  • @PaulSanders is does not compile (C2280); furthermore, `SortDataToHistPar` is a template with T and the same T is used by `LocalSortHist` as well as the class, so specializing breaks the point of using the template. – Soleil Jun 16 '21 at 02:16
  • 1
    @Soleil The attempt to use a deleted function is probably in the `futures.push_back(future);` line (your compiler should be telling you this). It needs to be `futures.push_back(std::move(future));` since `std::future` is not copyable. –  Jun 16 '21 at 02:38