Threads in python app

This commit is contained in:
Thomas Krijnen
2019-07-19 15:55:22 +02:00
parent 1cd2287d86
commit 6e4a692de3
8 changed files with 105 additions and 59 deletions
+2 -2
View File
@@ -220,7 +220,7 @@ int main(int argc, char** argv) {
("calculate-quantities", "Calculate or fix the physical quantity definitions "
"based on an interpretation of the geometry when exporting IFC");
size_t num_threads;
int num_threads;
po::options_description geom_options("Geometry options");
geom_options.add_options()
@@ -735,7 +735,7 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
if (num_threads == 0) {
if (num_threads <= 0) {
num_threads = std::thread::hardware_concurrency();
Logger::Notice("Using " + std::to_string(num_threads) + " threads");
}
@@ -14,7 +14,7 @@ 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, size_t num_threads) const {
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);
}
};
+20 -11
View File
@@ -89,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
@@ -176,7 +178,8 @@ namespace IfcGeom {
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
private:
size_t num_threads_;
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_;
@@ -437,10 +440,10 @@ namespace IfcGeom {
fu.get();
processed += 1;
const int progress = processed * 50 / tasks_.size();
if (progress != old_progress) {
Logger::ProgressBar(progress);
old_progress = progress;
progress_ = processed * 50 / tasks_.size();
if (progress_ != old_progress) {
Logger::ProgressBar(progress_);
old_progress = progress_;
}
std::swap(threadpool[i], threadpool.back());
@@ -460,10 +463,10 @@ namespace IfcGeom {
fu.get();
processed += 1;
const int progress = processed * 50 / tasks_.size();
if (progress != old_progress) {
Logger::ProgressBar(progress);
old_progress = progress;
progress_ = processed * 50 / tasks_.size();
if (progress_ != old_progress) {
Logger::ProgressBar(progress_);
old_progress = progress_;
}
}
@@ -519,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; }
@@ -946,7 +955,7 @@ namespace IfcGeom {
bool owns_ifc_file;
public:
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads)
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)
@@ -83,7 +83,7 @@ namespace IfcGeom {
IteratorImplementation<P, PP>* implementation_;
public:
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, size_t num_threads = 1)
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, int num_threads = 1)
: file_(file)
, settings_(settings)
{
@@ -31,7 +31,7 @@ void IteratorFactoryImplementation<P, PP>::bind(const std::string& schema_name,
}
template <typename P, typename PP>
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads) {
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) {
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
typename std::map<std::string, typename get_factory_type<P, PP>::type>::const_iterator it;
it = this->find(schema_name_lower);
@@ -23,9 +23,9 @@ namespace IfcGeom {
class BRepElement;
}
typedef boost::function4<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_float_float_fn;
typedef boost::function4<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_float_double_fn;
typedef boost::function4<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_double_double_fn;
typedef boost::function4<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_float_float_fn;
typedef boost::function4<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_float_double_fn;
typedef boost::function4<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_double_double_fn;
template <typename P, typename PP>
struct get_factory_type {};
@@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map<std::string, typename get_
public:
IteratorFactoryImplementation();
void bind(const std::string& schema_name, typename get_factory_type<P, PP>::type fn);
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t);
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int);
};
template <typename P, typename PP>
@@ -7,6 +7,7 @@ import sys
import time
import operator
import functools
import multiprocessing
import OCC.AIS
@@ -54,6 +55,43 @@ from .. import version as ifcopenshell_version
if ifcopenshell_version < "0.6":
# not yet ported
from .. import get_supertype
class geometry_creation_signals(QtCore.QObject):
completed = QtCore.pyqtSignal('PyQt_PyObject')
progress = QtCore.pyqtSignal('PyQt_PyObject')
class geometry_creation_thread(QtCore.QThread):
def __init__(self, signals, settings, f):
QtCore.QThread.__init__(self)
self.signals = signals
self.settings = settings
self.f = f
def run(self):
t0 = time.time()
# detect concurrency from hardware, we need to have
# at least two threads because otherwise the interface
# is different
# is different
it = iterator(self.settings, self.f, max(2, multiprocessing.cpu_count()))
if not it.initialize():
self.signals.completed.emit([])
return
def _():
old_progress = -1
while True:
shape = it.get()
if shape:
yield shape
if not it.next():
break
self.signals.completed.emit((it, self.f, list(_())))
class configuration(object):
def __init__(self):
@@ -393,62 +431,59 @@ class application(QtWidgets.QApplication):
self.product_to_ais = {}
self.counter = 0
self.window = widget
self.thread = None
def initialize(self):
self.InitDriver()
self._display.Select = self.HandleSelection
def load_file(self, f, setting=None):
if setting is None:
setting = settings()
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
def finished(self, file_shapes):
it, f, shapes = file_shapes
v = self._display
t = {0: time.time()}
def update(dt=None):
t1 = time.time()
if t1 - t[0] > (dt or -1):
if dt is None or t1 - t[0] > dt:
v.FitAll()
v.Repaint()
t[0] = t1
terminate = [False]
self.window.window_closed.connect(lambda *args: operator.setitem(terminate, 0, True))
t0 = time.time()
it = iterator(setting, f)
if not it.initialize():
return
old_progress = -1
while True:
if terminate[0]:
break
shape = it.get()
product = f[shape.data.id]
for shape in shapes:
ais = display_shape(shape, viewer_handle=v)
product = f[shape.data.id]
ais.GetObject().SetSelectionPriority(self.counter)
self.ais_to_product[self.counter] = product
self.product_to_ais[product] = ais
self.counter += 1
QtWidgets.QApplication.processEvents()
if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
v.Context.Erase(ais, True)
progress = it.progress() // 2
if progress > old_progress:
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
old_progress = progress
if not it.next():
break
update(0.2)
print("\rOpened file in %.2f seconds%s" % (time.time() - t0, " " * 25))
update(1.)
update()
self.thread = None
def load_file(self, f, setting=None):
if self.thread is not None:
return
if setting is None:
setting = settings()
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
self.signals = geometry_creation_signals()
thread = self.thread = geometry_creation_thread(self.signals, setting, f)
self.window.window_closed.connect(lambda *args: thread.terminate())
self.signals.completed.connect(self.finished)
self.thread.start()
def select(self, product):
ais = self.product_to_ais.get(product)
@@ -47,9 +47,11 @@ def wrap_shape_creation(settings, shape):
if has_occ:
from . import occ_utils as utils
def wrap_shape_creation(settings, shape): return utils.create_shape_from_serialization(shape) if getattr(settings,
'use_python_opencascade',
False) else shape
def wrap_shape_creation(settings, shape):
if getattr(settings, 'use_python_opencascade', False):
return utils.create_shape_from_serialization(shape)
else:
return shape
# Subclass the settings module to provide an additional
@@ -77,13 +79,13 @@ _iterator = ifcopenshell_wrapper.iterator_double_precision
# Make sure people are able to use python's platform agnostic paths
class iterator(_iterator):
def __init__(self, settings, file_or_filename):
def __init__(self, settings, file_or_filename, num_threads = 1):
self.settings = settings
if isinstance(file_or_filename, file):
file_or_filename = file_or_filename.wrapped_data
else:
file_or_filename = os.path.abspath(file_or_filename)
_iterator.__init__(self, settings, file_or_filename)
_iterator.__init__(self, settings, file_or_filename, num_threads)
if has_occ:
def get(self):