Merge pull request #639 from aothms/multithreaded

Multithreaded
This commit is contained in:
Thomas Krijnen
2019-08-02 15:37:00 +02:00
committed by GitHub
16 changed files with 650 additions and 232 deletions
@@ -14,8 +14,8 @@ namespace IfcGeom {
namespace {
template <typename P, typename PP>
struct MAKE_TYPE_NAME(factory_t) {
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) const {
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters);
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) const {
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters, num_threads);
}
};
}
+282 -37
View File
@@ -63,6 +63,11 @@
#include <vector>
#include <limits>
#include <algorithm>
#include <atomic>
#include <future>
#include <thread>
#include <chrono>
#include <boost/algorithm/string.hpp>
@@ -84,6 +89,8 @@
#include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
#include "../ifcgeom_schema_agnostic/IteratorImplementation.h"
#include <atomic>
// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration
#ifdef min
#undef min
@@ -92,12 +99,93 @@
#undef max
#endif
namespace {
template <typename P, typename PP=P>
struct geometry_conversion_task {
int index;
IfcSchema::IfcRepresentation *representation;
IfcSchema::IfcProduct::list::ptr products;
std::vector<IfcGeom::BRepElement<P, PP>*> breps;
std::vector<IfcGeom::Element<P, PP>*> elements;
};
template <typename P, typename PP=P>
IfcGeom::Element<P, PP>* process_based_on_settings(
const IfcGeom::IteratorSettings& settings,
IfcGeom::BRepElement<P, PP>* elem,
IfcGeom::TriangulationElement<P, PP>* previous=nullptr)
{
if (settings.get(IfcGeom::IteratorSettings::USE_BREP_DATA)) {
try {
return new IfcGeom::SerializedElement<P, PP>(*elem);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed.");
return nullptr;
}
} else if (!settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) {
try {
if (!previous) {
return new IfcGeom::TriangulationElement<P, PP>(*elem);
} else {
return new IfcGeom::TriangulationElement<P, PP>(*elem, previous->geometry_pointer());
}
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed.");
return nullptr;
}
} else {
return elem;
}
}
template <typename P, typename PP = P>
void create_element(
IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel,
const IfcGeom::IteratorSettings& settings,
geometry_conversion_task<P, PP>* rep)
{
IfcSchema::IfcRepresentation *representation = rep->representation;
IfcSchema::IfcProduct *product = *rep->products->begin();
auto brep = kernel->create_brep_for_representation_and_product<P, PP>(settings, representation, product);
if (!brep) {
return;
}
auto elem = process_based_on_settings(settings, brep);
if (!elem) {
return;
}
rep->breps = { brep };
rep->elements = { elem };
for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) {
auto brep2 = kernel->create_brep_for_processed_representation<P, PP>(settings, representation, *it, brep);
if (brep2) {
auto elem2 = process_based_on_settings(settings, brep, dynamic_cast<IfcGeom::TriangulationElement<P, PP>*>(elem));
if (elem2) {
rep->breps.push_back(brep2);
rep->elements.push_back(elem2);
}
}
}
}
}
namespace IfcGeom {
template <typename P, typename PP>
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
private:
int num_threads_;
std::atomic<int> progress_;
std::vector<geometry_conversion_task<P, PP>> tasks_;
std::vector<IfcGeom::Element<P, PP>*> all_processed_elements_;
std::vector<IfcGeom::BRepElement<P, PP>*> all_processed_native_elements_;
typename std::vector<IfcGeom::Element<P, PP>*>::const_iterator task_result_iterator_;
typename std::vector<IfcGeom::BRepElement<P, PP>*>::const_iterator native_task_result_iterator_;
MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
@@ -282,16 +370,118 @@ namespace IfcGeom {
representation_iterator = representations->begin();
ifcproducts.reset();
if (!create()) {
return false;
}
done = 0;
total = representations->size();
if (num_threads_ != 1) {
collect();
process_concurrently();
} else {
if (!create()) {
return false;
}
}
return true;
}
void collect() {
int i = 0;
IfcSchema::IfcProduct::list* previous = nullptr;
while (auto rp = get_next_task()) {
// Note that get_next_task() mutates the state of the iterator
// we use that capture all products that can be processed as
// part of this representation and then keep iterating until
// the underlying list of products changes.
if (ifcproducts.get() != previous) {
previous = ifcproducts.get();
geometry_conversion_task<P, PP> t;
t.index = i++;
t.representation = *representation_iterator;
t.products = ifcproducts;
tasks_.emplace_back(t);
}
_nextShape();
}
}
void process_concurrently() {
size_t conc_threads = num_threads_;
if (conc_threads > tasks_.size()) {
conc_threads = tasks_.size();
}
std::vector<MAKE_TYPE_NAME(Kernel)*> kernel_pool;
kernel_pool.reserve(conc_threads);
for (unsigned i = 0; i < conc_threads; ++i) {
kernel_pool.push_back(new MAKE_TYPE_NAME(Kernel)(kernel));
}
std::vector<std::future<void>> threadpool;
int old_progress = -1;
int processed = 0;
Logger::ProgressBar(0);
for (auto& rep : tasks_) {
MAKE_TYPE_NAME(Kernel)* K = nullptr;
if (threadpool.size() < kernel_pool.size()) {
K = kernel_pool[threadpool.size()];
}
while (threadpool.size() == conc_threads) {
for (int i = 0; i < (int)threadpool.size(); i++) {
std::future<void> &fu = threadpool[i];
std::future_status status;
status = fu.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
fu.get();
processed += 1;
progress_ = processed * 50 / tasks_.size();
if (progress_ != old_progress) {
Logger::ProgressBar(progress_);
old_progress = progress_;
}
std::swap(threadpool[i], threadpool.back());
threadpool.pop_back();
std::swap(kernel_pool[i], kernel_pool.back());
K = kernel_pool.back();
break;
} // if
} // for
} // while
std::future<void> fu = std::async(std::launch::async, create_element<P, PP>, K, std::ref(settings), &rep);
threadpool.emplace_back(std::move(fu));
}
for (std::future<void> &fu : threadpool) {
fu.get();
processed += 1;
progress_ = processed * 50 / tasks_.size();
if (progress_ != old_progress) {
Logger::ProgressBar(progress_);
old_progress = progress_;
}
}
for (auto& rep : tasks_) {
all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end());
all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end());
}
task_result_iterator_ = all_processed_elements_.begin();
native_task_result_iterator_ = all_processed_native_elements_.begin();
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(all_processed_elements_.size()) +
" objects) ");
}
/// Computes model's bounding box (bounds_min and bounds_max).
/// @note Can take several minutes for large files.
void compute_bounds()
@@ -332,7 +522,13 @@ namespace IfcGeom {
}
}
int progress() const { return 100 * done / total; }
int progress() const {
if (num_threads_ == 1) {
return 100 * done / total;
} else {
return progress_;
}
}
const std::string& getUnitName() const { return unit_name; }
@@ -403,13 +599,13 @@ namespace IfcGeom {
return associated_single_materials.size() == 1;
}
BRepElement<P, PP>* create_shape_model_for_next_entity() {
boost::optional<std::pair<IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*>> get_next_task() {
for (;;) {
IfcSchema::IfcRepresentation* representation;
if ( representation_iterator == representations->end() ) {
if (representation_iterator == representations->end()) {
representations.reset();
return 0; // reached the end of our list of representations
return boost::none; // reached the end of our list of representations
}
representation = *representation_iterator;
@@ -417,20 +613,20 @@ namespace IfcGeom {
// Init. the list of filtered IfcProducts for this representation
ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list);
IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel.products_represented_by(representation);
// Include only the desired products for processing.
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
IfcSchema::IfcProduct* prod = *jt;
if (boost::all(filters_, filter_match(prod))) {
ifcproducts->push(prod);
}
}
// Include only the desired products for processing.
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
IfcSchema::IfcProduct* prod = *jt;
if (boost::all(filters_, filter_match(prod))) {
ifcproducts->push(prod);
}
}
if (ifcproducts->size() == 0) {
_nextShape();
continue;
}
if (ifcproducts->size() == 0) {
_nextShape();
continue;
}
geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts);
geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts);
IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
@@ -450,14 +646,14 @@ namespace IfcGeom {
// Check if this represenation has (or will be) processed as part its mapped representation
bool representation_processed_as_mapped_item = false;
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
if (representation_mapped_to) {
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to)));
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to)));
}
if (representation_processed_as_mapped_item) {
ok_mapped_representations->push(representation_mapped_to);
ok_mapped_representations->push(representation_mapped_to);
_nextShape();
continue;
}
@@ -466,13 +662,28 @@ namespace IfcGeom {
}
// Have we reached the end of our list of IfcProducts?
if ( ifcproduct_iterator == ifcproducts->end() ) {
if (ifcproduct_iterator == ifcproducts->end()) {
_nextShape();
continue;
}
IfcSchema::IfcProduct* product = *ifcproduct_iterator;
Logger::SetProduct(product);
return std::make_pair(representation, product);
}
}
BRepElement<P, PP>* create_shape_model_for_next_entity() {
for (;;) {
auto rp = get_next_task();
if (!rp) {
return nullptr;
}
auto representation = rp->first;
auto product = rp->second;
Logger::SetProduct(product);
BRepElement<P, PP>* element;
if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) {
@@ -520,13 +731,23 @@ namespace IfcGeom {
/// Moves to the next shape representation, create its geometry, and returns the associated product.
/// Use get() to retrieve the created geometry.
IfcUtil::IfcBaseClass* next() {
// Increment the iterator over the list of products using the current
// shape representation
if (ifcproducts) {
++ifcproduct_iterator;
}
if (num_threads_ != 1) {
task_result_iterator_++;
native_task_result_iterator_++;
if (task_result_iterator_ == all_processed_elements_.end()) {
return nullptr;
} else {
return (*task_result_iterator_)->product();
}
} else {
// Increment the iterator over the list of products using the current
// shape representation
if (ifcproducts) {
++ifcproduct_iterator;
}
return create();
return create();
}
}
/// Gets the representation of the current geometrical entity.
@@ -534,9 +755,18 @@ namespace IfcGeom {
{
// TODO: Test settings and throw
Element<P, PP>* ret = 0;
if (current_triangulation) { ret = current_triangulation; }
else if (current_serialization) { ret = current_serialization; }
else if (current_shape_model) { ret = current_shape_model; }
if (num_threads_ != 1) {
ret = *task_result_iterator_;
} else {
if (current_triangulation) {
ret = current_triangulation;
} else if (current_serialization) {
ret = current_serialization;
} else if (current_shape_model) {
ret = current_shape_model;
}
}
// If we want to organize the element considering their hierarchy
if (settings.get(IteratorSettings::SEARCH_FLOOR))
@@ -591,7 +821,11 @@ namespace IfcGeom {
BRepElement<P, PP>* get_native()
{
// TODO: Test settings and throw
return current_shape_model;
if (num_threads_ != 1) {
return *native_task_result_iterator_;
} else {
return current_shape_model;
}
}
const Element<P, PP>* get_object(int id) {
@@ -721,11 +955,12 @@ namespace IfcGeom {
bool owns_ifc_file;
public:
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters)
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads)
: settings(settings)
, ifc_file(file)
, filters_(filters)
, owns_ifc_file(false)
, num_threads_(num_threads)
{
_initialize();
}
@@ -735,6 +970,16 @@ namespace IfcGeom {
delete ifc_file;
}
if (settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) {
for (auto& p : all_processed_native_elements_) {
delete p;
}
}
for (auto& p : all_processed_elements_) {
delete p;
}
free_shapes();
}
};