I've been trying to use multithreading to handle time-consuming recompute recently.
I use QtConcurrent's "run" function to run the key method "_recomputeFeature".
Code: Select all
int Document::recompute(const std::vector<App::DocumentObject*> &objs, bool force, bool *hasError, int options)
{
if (d->undoing || d->rollback) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
FC_WARN("Ignore document recompute on undo/redo");
return 0;
}
int objectCount = 0;
if (testStatus(Document::PartialDoc)) {
if(mustExecute())
FC_WARN("Please reload partial document '" << Label.getValue() << "' for recomputation.");
return 0;
}
if (testStatus(Document::Recomputing)) {
// this is clearly a bug in the calling instance
FC_ERR("Recursive calling of recompute for document " << getName());
return 0;
}
// The 'SkipRecompute' flag can be (tmp.) set to avoid too many
// time expensive recomputes
if(!force && testStatus(Document::SkipRecompute)) {
signalSkipRecompute(*this,objs);
return 0;
}
// delete recompute log
d->clearRecomputeLog();
FC_TIME_INIT(t);
Base::ObjectStatusLocker<Document::Status, Document> exe(Document::Recomputing, this);
signalBeforeRecompute(*this);
#if 0
//////////////////////////////////////////////////////////////////////////
// FIXME Comment by Realthunder:
// the topologicalSrot() below cannot handle partial recompute, haven't got
// time to figure out the code yet, simply use back boost::topological_sort
// for now, that is, rely on getDependencyList() to do the sorting. The
// downside is, it didn't take advantage of the ready built InList, nor will
// it report for cyclic dependency.
//////////////////////////////////////////////////////////////////////////
// get the sorted vector of all dependent objects and go though it from the end
auto depObjs = getDependencyList(objs.empty()?d->objectArray:objs);
vector<DocumentObject*> topoSortedObjects = topologicalSort(depObjs);
if (topoSortedObjects.size() != depObjs.size()){
cerr << "App::Document::recompute(): cyclic dependency detected" << endl;
topoSortedObjects = d->partialTopologicalSort(depObjs);
}
std::reverse(topoSortedObjects.begin(),topoSortedObjects.end());
#else
auto topoSortedObjects = getDependencyList(objs.empty()?d->objectArray:objs,DepSort|options);
#endif
for(auto obj : topoSortedObjects)
obj->setStatus(ObjectStatus::PendingRecompute,true);
ParameterGrp::handle hGrp = GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Document");
bool canAbort = hGrp->GetBool("CanAbortRecompute",true);
std::set<App::DocumentObject *> filter;
size_t idx = 0;
FC_TIME_INIT(t2);
try {
// maximum two passes to allow some form of dependency inversion
for(int passes=0; passes<2 && idx<topoSortedObjects.size(); ++passes) {
std::unique_ptr<Base::SequencerLauncher> seq;
if(canAbort)
seq.reset(new Base::SequencerLauncher("Recompute...", topoSortedObjects.size()));
FC_LOG("Recompute pass " << passes);
for (; idx < topoSortedObjects.size(); ++idx) {
auto obj = topoSortedObjects[idx];
if(!obj->getNameInDocument() || filter.find(obj)!=filter.end())
continue;
// ask the object if it should be recomputed
bool doRecompute = false;
if (obj->mustRecompute()) {
doRecompute = true;
++objectCount;
QFuture<int> ans = QtConcurrent::run([&]()->int{
_recomputeFeature(obj);
});
int res = ans.result();
ans.waitForFinished();
// int res = _recomputeFeature(obj);
if(res) {
if(hasError)
*hasError = true;
if(res < 0) {
passes = 2;
break;
}
// if something happened filter all object in its
// inListRecursive from the queue then proceed
obj->getInListEx(filter,true);
filter.insert(obj);
continue;
}
}
if(obj->isTouched() || doRecompute) {
signalRecomputedObject(*obj);
obj->purgeTouched();
// set all dependent object touched to force recompute
for (auto inObjIt : obj->getInList())
inObjIt->enforceRecompute();
}
if (seq)
seq->next(true);
}
// check if all objects are recomputed but still thouched
for (size_t i=0;i<topoSortedObjects.size();++i) {
auto obj = topoSortedObjects[i];
obj->setStatus(ObjectStatus::Recompute2,false);
if(!filter.count(obj) && obj->isTouched()) {
if(passes>0)
FC_ERR(obj->getFullName() << " still touched after recompute");
else{
FC_LOG(obj->getFullName() << " still touched after recompute");
if(idx>=topoSortedObjects.size()) {
// let's start the next pass on the first touched object
idx = i;
}
obj->setStatus(ObjectStatus::Recompute2,true);
}
}
}
}
}catch(Base::Exception &e) {
e.ReportException();
}
FC_TIME_LOG(t2, "Recompute");
for(auto obj : topoSortedObjects) {
if(!obj->getNameInDocument())
continue;
obj->setStatus(ObjectStatus::PendingRecompute,false);
obj->setStatus(ObjectStatus::Recompute2,false);
if(obj->testStatus(ObjectStatus::PendingRemove))
obj->getDocument()->removeObject(obj->getNameInDocument());
}
signalRecomputed(*this,topoSortedObjects);
FC_TIME_LOG(t,"Recompute total");
if (d->_RecomputeLog.size())
Base::Console().Log("Recompute failed! Please check report view.\n");
return objectCount;
}
The parallel stack is shown in the attachment.
Could someone please give me some simple suggestions?