From: Wolfgang Bangerth Date: Sun, 5 Dec 2021 23:09:09 +0000 (-0700) Subject: Also inline the worker class as a lambda function. X-Git-Tag: v9.4.0-rc1~769^2~3 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=84fbe1136590c704858a699a71f6efee21a1fc68;p=dealii.git Also inline the worker class as a lambda function. --- diff --git a/include/deal.II/base/work_stream.h b/include/deal.II/base/work_stream.h index 882ca45562..ecb6c52121 100644 --- a/include/deal.II/base/work_stream.h +++ b/include/deal.II/base/work_stream.h @@ -461,149 +461,6 @@ namespace WorkStream - /** - * A class that manages calling the worker function on a number of - * parallel threads. Note that it is, in the TBB notation, a filter that - * can run in parallel. - */ - template - class TBBWorker - { - public: - using ItemType = typename IteratorRangeToItemStream::ItemType; - - /** - * Constructor. Takes a reference to the object on which we will - * operate as well as a pointer to the function that will do the - * assembly. - */ - TBBWorker( - const std::function - & worker, - bool copier_exist = true) - : worker(worker) - , copier_exist(copier_exist) - {} - - - /** - * Work on an item. - */ - ItemType * - operator()(ItemType *¤t_item) const - { - // we need to find an unused scratch data object in the list that - // corresponds to the current thread and then mark it as used. if - // we can't find one, create one - // - // as discussed in the discussion of the documentation of the - // IteratorRangeToItemStream::scratch_data variable, there is no - // need to synchronize access to this variable using a mutex - // as long as we have no yield-point in between. this means that - // we can't take an iterator into the list now and expect it to - // still be valid after calling the worker, but we at least do - // not have to lock the following section - ScratchData *scratch_data = nullptr; - { - typename ItemType::ScratchDataList &scratch_data_list = - current_item->scratch_data->get(); - - // see if there is an unused object. if so, grab it and mark - // it as used - for (typename ItemType::ScratchDataList::iterator p = - scratch_data_list.begin(); - p != scratch_data_list.end(); - ++p) - if (p->currently_in_use == false) - { - scratch_data = p->scratch_data.get(); - p->currently_in_use = true; - break; - } - - // if no object was found, create one and mark it as used - if (scratch_data == nullptr) - { - scratch_data = - new ScratchData(*current_item->sample_scratch_data); - - typename ItemType::ScratchDataList::value_type - new_scratch_object(scratch_data, true); - scratch_data_list.push_back(std::move(new_scratch_object)); - } - } - - // then call the worker function on each element of the chunk we were - // given. since these worker functions are called on separate threads, - // nothing good can happen if they throw an exception and we are best - // off catching it and showing an error message - for (unsigned int i = 0; i < current_item->n_items; ++i) - { - try - { - if (worker) - worker(current_item->work_items[i], - *scratch_data, - current_item->copy_datas[i]); - } - catch (const std::exception &exc) - { - Threads::internal::handle_std_exception(exc); - } - catch (...) - { - Threads::internal::handle_unknown_exception(); - } - } - - // finally mark the scratch object as unused again. as above, there - // is no need to lock anything here since the object we work on - // is thread-local - { - typename ItemType::ScratchDataList &scratch_data_list = - current_item->scratch_data->get(); - - for (typename ItemType::ScratchDataList::iterator p = - scratch_data_list.begin(); - p != scratch_data_list.end(); - ++p) - if (p->scratch_data.get() == scratch_data) - { - Assert(p->currently_in_use == true, ExcInternalError()); - p->currently_in_use = false; - } - } - - // if there is no copier, mark current item as usable again - if (copier_exist == false) - current_item->currently_in_use = false; - - - // Then return the original pointer - // to the now modified object. The copier will work on it next. - return current_item; - } - - - private: - /** - * Pointer to the function that does the assembling on the sequence of - * cells. - */ - const std::function - worker; - - /** - * This flag is true if the copier stage exist. If it does not, the - * worker has to free the buffer. Otherwise the copier will do it. - */ - const bool copier_exist; - }; - - - template worker_filter(worker); - auto tbb_worker_filter = - tbb::make_filter(tbb::filter::parallel, - worker_filter); + auto tbb_worker_filter = tbb::make_filter( + tbb::filter::parallel, + [worker = + std::function( + worker), + copier_exist = + static_cast(std::function(copier))]( + ItemType *¤t_item) { + // we need to find an unused scratch data object in the list that + // corresponds to the current thread and then mark it as used. if + // we can't find one, create one + // + // as discussed in the discussion of the documentation of the + // IteratorRangeToItemStream::scratch_data variable, there is no + // need to synchronize access to this variable using a mutex + // as long as we have no yield-point in between. this means that + // we can't take an iterator into the list now and expect it to + // still be valid after calling the worker, but we at least do + // not have to lock the following section + ScratchData *scratch_data = nullptr; + { + typename ItemType::ScratchDataList &scratch_data_list = + current_item->scratch_data->get(); + + // see if there is an unused object. if so, grab it and mark + // it as used + for (typename ItemType::ScratchDataList::iterator p = + scratch_data_list.begin(); + p != scratch_data_list.end(); + ++p) + if (p->currently_in_use == false) + { + scratch_data = p->scratch_data.get(); + p->currently_in_use = true; + break; + } + + // if no object was found, create one and mark it as used + if (scratch_data == nullptr) + { + scratch_data = + new ScratchData(*current_item->sample_scratch_data); + + typename ItemType::ScratchDataList::value_type + new_scratch_object(scratch_data, true); + scratch_data_list.push_back(std::move(new_scratch_object)); + } + } + + // then call the worker function on each element of the chunk we + // were given. since these worker functions are called on separate + // threads, nothing good can happen if they throw an exception and + // we are best off catching it and showing an error message + for (unsigned int i = 0; i < current_item->n_items; ++i) + { + try + { + if (worker) + worker(current_item->work_items[i], + *scratch_data, + current_item->copy_datas[i]); + } + catch (const std::exception &exc) + { + Threads::internal::handle_std_exception(exc); + } + catch (...) + { + Threads::internal::handle_unknown_exception(); + } + } + + // finally mark the scratch object as unused again. as above, there + // is no need to lock anything here since the object we work on + // is thread-local + { + typename ItemType::ScratchDataList &scratch_data_list = + current_item->scratch_data->get(); + + for (typename ItemType::ScratchDataList::iterator p = + scratch_data_list.begin(); + p != scratch_data_list.end(); + ++p) + if (p->scratch_data.get() == scratch_data) + { + Assert(p->currently_in_use == true, ExcInternalError()); + p->currently_in_use = false; + } + } + + // if there is no copier, mark current item as usable again + if (copier_exist == false) + current_item->currently_in_use = false; + + + // Then return the original pointer + // to the now modified object. The copier will work on it next. + return current_item; + }); auto tbb_copier_filter = tbb::make_filter( tbb::filter::serial,