liveBook · Manning

Find

As explained in C++ Concurrency in Action, Second Edition

`std::find` is a C++ standard library algorithm that returns the first element in a range equal to a specified value, or the end of the range if none is found.

`std::find` returns the first matching element, or the range end if none matches. Because it can stop as soon as a match is found, a parallel search should let other workers stop too; otherwise, a serial search may finish sooner. section 8.5.2

Stopping other workers

Searching attic boxes for a photograph illustrates early completion: once one person finds it, the others can stop. In a parallel search, an atomic flag can signal that the answer is known. section 8.5.2

Listing 8.9. An implementation of a parallel find algorithm
template<typename Iterator,typename MatchType>
Iterator parallel_find(Iterator first,Iterator last,MatchType match)
{
    struct find_element                                               #1
    {
        void operator()(Iterator begin,Iterator end,
                        MatchType match,
                        std::promise<Iterator>* result,
                        std::atomic<bool>* done_flag)
        {
            try
            {
                for(;(begin!=end) && !done_flag->load();++begin)      #2
                {
                    if(*begin==match)
                    {
                        result->set_value(begin);                     #3
                        done_flag->store(true);                       #4
                        return;
                    }
                }
            }
            catch(...)                                                #5
            {
                try
                {
                    result->set_exception(std::current_exception());  #6
                    done_flag->store(true);
                }
                catch(...)                                            #7
                {}
            }
        }
    };
    unsigned long const length=std::distance(first,last);
    if(!length)
        return last;
    unsigned long const min_per_thread=25;
    unsigned long const max_threads=
        (length+min_per_thread-1)/min_per_thread;
    unsigned long const hardware_threads=
        std::thread::hardware_concurrency();
    unsigned long const num_threads=
        std::min(hardware_threads!=0?hardware_threads:2,max_threads);
    unsigned long const block_size=length/num_threads;
    std::promise<Iterator> result;                                    #8
    std::atomic<bool> done_flag(false);                               #9
    std::vector<std::thread> threads(num_threads-1);
    {                                                                 #10
        join_threads joiner(threads);
        Iterator block_start=first;
        for(unsigned long i=0;i<(num_threads-1);++i)
        {
            Iterator block_end=block_start;
            std::advance(block_end,block_size);
            threads[i]=std::thread(find_element(),                    #11
                                   block_start,block_end,match,
                                   &result,&done_flag);
            block_start=block_end;
        }
        find_element()(block_start,last,match,&result,&done_flag);    #12
    }
    if(!done_flag.load())                                             #13
    {
        return last;
    }
    return result.get_future().get();                                 #14
}


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":3,\"ch\":70},{\"line\":3,\"ch\":72}],[{\"line\":48,\"ch\":70},{\"line\":48,\"ch\":72}],[{\"line\":55,\"ch\":70},{\"line\":55,\"ch\":72}],[{\"line\":60,\"ch\":70},{\"line\":60,\"ch\":72}],[{\"line\":62,\"ch\":70},{\"line\":62,\"ch\":72}],[{\"line\":66,\"ch\":70},{\"line\":66,\"ch\":72}],[{\"line\":12,\"ch\":70},{\"line\":12,\"ch\":72}],[{\"line\":16,\"ch\":70},{\"line\":16,\"ch\":72}],[{\"line\":17,\"ch\":70},{\"line\":17,\"ch\":72}],[{\"line\":22,\"ch\":70},{\"line\":22,\"ch\":72}],[{\"line\":26,\"ch\":70},{\"line\":26,\"ch\":72}],[{\"line\":29,\"ch\":70},{\"line\":29,\"ch\":72}],[{\"line\":45,\"ch\":70},{\"line\":45,\"ch\":72}],[{\"line\":46,\"ch\":70},{\"line\":46,\"ch\":72}],[{\"line\":48,\"ch\":70},{\"line\":48,\"ch\":73}],[{\"line\":55,\"ch\":70},{\"line\":55,\"ch\":73}],[{\"line\":60,\"ch\":70},{\"line\":60,\"ch\":73}],[{\"line\":62,\"ch\":70},{\"line\":62,\"ch\":73}],[{\"line\":66,\"ch\":70},{\"line\":66,\"ch\":73}]]"}
!@%STYLE%@!

See a parallel implementation that shares a result promise and an atomic flag so workers can stop after a match is found. section 8.5.2

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Explains early completion and atomic cancellation through a parallel implementation.
Examples
Uses an attic photograph search and a four-thread comparison to illustrate early completion.
What you can do after
Evaluate when parallel search can lose to serial search and use a flag to signal a found match.
How it connects
Connects parallel search with atomic variables, data access patterns, std::equal, and std::any_of.

Also explained in

Compare how 4 books explain find →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.