0

I would like to write a function anyElementSatisfiesPredicate that takes in input a predicate function p (that takes in input an object of a given type T and returns a bool) and a std::vector v of objects of type T, and returns true if and only it exists an element in v s.t. p(v) == true.

This can be easily accomplished by means of a for loop:

bool anyElementSatisfiesPredicate(std::function<bool(T&)> p, std::vector<T> v) {
  for (auto it=v.begin(); it!=v.end(); ++it)
    if (p(*it))
      return true;
  return false;
}

This works fine (given a properly defined type T) but I would like to parallelize this code testing the predicate function p on different elements of the vector v at the same time. My idea is to split the work among a fixed (machine dependent) number of cores. Each thread should evaluate the predicate on a different portion of the original vector and return true as soon as it finds that the predicate p holds on an element in its portion. As soon as any given thread returns true, the core function anyElementSatisfiesPredicate should kill the remaining threads and return true, if all threads eventually return false, it should return false instead.

Since this code will be ran on different machines with a different number of cores I would prefer not to introduce any constant defining the number of cores to be used, I'd rather have the system choose the best value for me.

Efficiency is my first concern. Is there any easy way to accomplish this (maybe using the boost threads library)?

  • So, like [`std::any_of`](http://en.cppreference.com/w/cpp/algorithm/all_any_none_of) but in parallel? Could maybe be done with e.g. [`std::async`](http://en.cppreference.com/w/cpp/thread/async). – Some programmer dude May 08 '15 at 08:21
  • *"should kill the remaining threads"* - `std::thread` doesn't support asynchronous thread cancellation (your underlying thread implementation might, but it's only safe in very careful and restricted usage)), but you can set a flag that the other threads check every so often (e.g. each time a further *n* elements have been checked), returning early if they see if set. Beyond that, it's just a matter of finding your core count, and joining the threads as they return. – Tony Delroy May 08 '15 at 08:32
  • Maybe you could go with a [std::future](http://de.cppreference.com/w/cpp/thread/future) if you are using C++11 – bazz-dee May 08 '15 at 08:32
  • You can use something like task queue - each thread will pop last element from the queue, calculate predicate and put result in another queue. Main thread monitors result queue and clears task queue to stop calculation. I'm not sure if it is directly supported by std lib... – anxieux May 08 '15 at 08:37
  • 1
    Sorry - just noticed the boost tag - the lib does provide some convenience functions for cooperative interruption: see [here](http://www.boost.org/doc/libs/1_58_0/doc/html/thread/thread_management.html#thread.thread_management.tutorial.interruption) - amounts to the same thing as the suggested flag above. – Tony Delroy May 08 '15 at 08:37

1 Answers1

1

Not the most elegant solution in the world, but something like this should work: (No boost, but requires c++ 11)

#include <thread>
#include <atomic>

template <typename T>
struct pred_evaluator
{
    static void any_element_satisfies(const std::function<bool(const T&)> & pred, const typename std::vector<T>::iterator & begin, const typename std::vector<T>::iterator & end, std::atomic<bool> & result) 
    {
        for (const auto & it=begin; it!=end; ++it)
        {
            if (result || pred(*it))
            {
                result= true;
                return;
            }
       }
    }

    static bool is_predicate_true_parallel(const std::function<bool(const T&)> & pred, const std::vector<T> & input, size_t num_threads)
    {
        size_t chunk_size = input.size() / 4;
        std::atomic<bool> result(false);
        std::vector<std::thread> threads;
        for (size_t i = 0; i < num_threads; ++i)
        {
            const auto & begin = input.begin() + i *chunk_size;
            const auto & end = input.begin() + std::min((i+1) * chunk_size, input.size());
            threads.emplace_back(any_element_satisfies,pred,begin,end,result);
        }

        for (auto & thread : threads)
            thread.join();

        return result;
    }
};

Then, you would call pred_evaluator<T>::is_predicate_true_parallel with your predicate, vector, and number of threads as input.

Sir Digby Chicken Caesar
  • 3,063
  • 1
  • 23
  • 31
  • Thank you for your answer, seems pretty much what I was looking for. I only see two problems but I might be wrong. (1) is_predicate_true_parallel does not return as soon as any thread returns true, it waits for all of them to terminate; (2) the any_predicate_done flag you use in the any_element_satisfies function is never declared, I guess your intention was to use result instead. Am I right? – Andrea Aquino May 08 '15 at 09:14
  • 1) No, the function does not return immediately. A better approach may be to signal the main thread with a condition variable when done (However, beware that conditional variables may have spurious wakeups that could give you a false positive). 2) DERP, yes `_predicate_done` was meant to be `result`. – Sir Digby Chicken Caesar May 08 '15 at 09:18
  • Also, it should be stated that if `_predicate_true_parallel` is being called multiple times, it is probably best to instead manage the threads outside the scope of this function so that they can be reused between iterations. You could encapsulate your thread(s) in a `task` \ `task pool` object which allows you to test successive predicates without excessive thread creation / destruction. Of course, this task setting method of this object would need to be mutex guarded. – Sir Digby Chicken Caesar May 08 '15 at 09:22