From c7ae6b8967649d0b621340c5557dcf7adc00fcae Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sat, 3 Feb 2024 17:42:15 +0100 Subject: [PATCH 01/14] Add processing strategy --- include/vrv/functor.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/vrv/functor.h b/include/vrv/functor.h index 6a08fac2bea..f97914f6a5d 100644 --- a/include/vrv/functor.h +++ b/include/vrv/functor.h @@ -16,6 +16,9 @@ namespace vrv { class Doc; +// Helper enum classes +enum class ProcessingStrategy { Sequential, MeasureParallel, SystemParallel }; + //---------------------------------------------------------------------------- // FunctorBase //---------------------------------------------------------------------------- @@ -76,6 +79,14 @@ class FunctorBase { */ virtual bool ImplementsEndInterface() const = 0; + /** + * Override to enable parallel functor processing + */ + ///@{ + virtual ProcessingStrategy GetProcessingStrategy() const { return ProcessingStrategy::Sequential; } + virtual int GetMaxNumberOfThreads() const { return 1; } + ///@} + private: // public: From 9a5b69c5fdd34c79b77b4c8106b307c3e2c016bb Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sat, 3 Feb 2024 23:47:18 +0100 Subject: [PATCH 02/14] Set max number of threads via option --- Verovio.xcodeproj/project.pbxproj | 10 +++++++++ include/vrv/functor.h | 10 +++++++++ include/vrv/options.h | 1 + src/functor.cpp | 34 +++++++++++++++++++++++++++++++ src/options.cpp | 5 +++++ 5 files changed, 60 insertions(+) create mode 100644 src/functor.cpp diff --git a/Verovio.xcodeproj/project.pbxproj b/Verovio.xcodeproj/project.pbxproj index e2fda8406a0..5e3bf1fe461 100644 --- a/Verovio.xcodeproj/project.pbxproj +++ b/Verovio.xcodeproj/project.pbxproj @@ -1470,6 +1470,10 @@ E76046C128D4829000C36204 /* calcledgerlinesfunctor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E76046BF28D4829000C36204 /* calcledgerlinesfunctor.cpp */; }; E76046C228D496B300C36204 /* calcledgerlinesfunctor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E76046BF28D4829000C36204 /* calcledgerlinesfunctor.cpp */; }; E76046C328D496B400C36204 /* calcledgerlinesfunctor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E76046BF28D4829000C36204 /* calcledgerlinesfunctor.cpp */; }; + E7605A2E2B6EF47E00903A6A /* functor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7605A2D2B6EF47E00903A6A /* functor.cpp */; }; + E7605A2F2B6EF47E00903A6A /* functor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7605A2D2B6EF47E00903A6A /* functor.cpp */; }; + E7605A302B6EF47E00903A6A /* functor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7605A2D2B6EF47E00903A6A /* functor.cpp */; }; + E7605A312B6EF47E00903A6A /* functor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7605A2D2B6EF47E00903A6A /* functor.cpp */; }; E763EF3F29E939C00029E56D /* convertfunctor.h in Headers */ = {isa = PBXBuildFile; fileRef = E763EF3E29E939C00029E56D /* convertfunctor.h */; }; E763EF4029E939C00029E56D /* convertfunctor.h in Headers */ = {isa = PBXBuildFile; fileRef = E763EF3E29E939C00029E56D /* convertfunctor.h */; settings = {ATTRIBUTES = (Public, ); }; }; E763EF4229E939FB0029E56D /* convertfunctor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E763EF4129E939FB0029E56D /* convertfunctor.cpp */; }; @@ -2206,6 +2210,7 @@ E75EA9FC29CC3A88003A97A7 /* calcarticfunctor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = calcarticfunctor.cpp; path = src/calcarticfunctor.cpp; sourceTree = ""; }; E76046BC28D4828200C36204 /* calcledgerlinesfunctor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = calcledgerlinesfunctor.h; path = include/vrv/calcledgerlinesfunctor.h; sourceTree = ""; }; E76046BF28D4829000C36204 /* calcledgerlinesfunctor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = calcledgerlinesfunctor.cpp; path = src/calcledgerlinesfunctor.cpp; sourceTree = ""; }; + E7605A2D2B6EF47E00903A6A /* functor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functor.cpp; path = src/functor.cpp; sourceTree = ""; }; E763EF3E29E939C00029E56D /* convertfunctor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convertfunctor.h; path = include/vrv/convertfunctor.h; sourceTree = ""; }; E763EF4129E939FB0029E56D /* convertfunctor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = convertfunctor.cpp; path = src/convertfunctor.cpp; sourceTree = ""; }; E765675828BBFBA400BC6490 /* functorinterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = functorinterface.h; path = include/vrv/functorinterface.h; sourceTree = ""; }; @@ -3036,6 +3041,7 @@ E74A806528BC97D5005274E7 /* findfunctor.h */, E722106528F856C4002CD6E9 /* findlayerelementsfunctor.cpp */, E722106228F8569F002CD6E9 /* findlayerelementsfunctor.h */, + E7605A2D2B6EF47E00903A6A /* functor.cpp */, E765675B28BC019F00BC6490 /* functor.h */, E74A806828BC9842005274E7 /* functorinterface.cpp */, E765675828BBFBA400BC6490 /* functorinterface.h */, @@ -4153,6 +4159,7 @@ 4DEC4D9721C81E3B00D1D273 /* expan.cpp in Sources */, 4D766EFF20ACAD6D006875D8 /* neume.cpp in Sources */, 4D1694581E3A44F300569BF4 /* accid.cpp in Sources */, + E7605A2F2B6EF47E00903A6A /* functor.cpp in Sources */, 4DACC9912990F29A00B55913 /* atts_critapp.cpp in Sources */, 4D1694591E3A44F300569BF4 /* custos.cpp in Sources */, 4D16945A1E3A44F300569BF4 /* dot.cpp in Sources */, @@ -4430,6 +4437,7 @@ 4DC34BA819BC4A83006175CD /* accid.cpp in Sources */, 4DC34BAA19BC4A83006175CD /* custos.cpp in Sources */, 4D7927D020ECCC6D0002A45D /* view_slur.cpp in Sources */, + E7605A2E2B6EF47E00903A6A /* functor.cpp in Sources */, 4D81351E2322C41800F59C01 /* keyaccid.cpp in Sources */, BD2E4D95287587FD00B04350 /* stem.cpp in Sources */, E77C198328CD31AD00F5BADA /* calcdotsfunctor.cpp in Sources */, @@ -4720,6 +4728,7 @@ 4DC34BAD19BC4A83006175CD /* dot.cpp in Sources */, 4D2073FD22A3BCE000E0765F /* tabgrp.cpp in Sources */, 4D7927D220ECCC6D0002A45D /* view_slur.cpp in Sources */, + E7605A302B6EF47E00903A6A /* functor.cpp in Sources */, 4DACC9922990F29A00B55913 /* atts_critapp.cpp in Sources */, 4DB3D8B91F83D0C600B5FC2B /* systemmilestone.cpp in Sources */, 4D2073FA22A3BCE000E0765F /* tabdursym.cpp in Sources */, @@ -5004,6 +5013,7 @@ BB4C4B7F22A932DF001F6AF0 /* fb.cpp in Sources */, BB4C4BA322A932E5001F6AF0 /* textdirinterface.cpp in Sources */, BB4C4B3922A932CF001F6AF0 /* turn.cpp in Sources */, + E7605A312B6EF47E00903A6A /* functor.cpp in Sources */, 4DACC9932990F29A00B55913 /* atts_critapp.cpp in Sources */, BB4C4BA122A932E5001F6AF0 /* scoredefinterface.cpp in Sources */, BB4C4B4322A932D7001F6AF0 /* beatrpt.cpp in Sources */, diff --git a/include/vrv/functor.h b/include/vrv/functor.h index f97914f6a5d..371f3ba7ddb 100644 --- a/include/vrv/functor.h +++ b/include/vrv/functor.h @@ -174,6 +174,11 @@ class DocFunctor : public Functor { */ Doc *GetDoc() { return m_doc; } + /* + * Get the maximal number of threads + */ + int GetMaxNumberOfThreads() const final; + private: // public: @@ -208,6 +213,11 @@ class DocConstFunctor : public ConstFunctor { */ const Doc *GetDoc() const { return m_doc; } + /* + * Get the maximal number of threads + */ + int GetMaxNumberOfThreads() const final; + private: // public: diff --git a/include/vrv/options.h b/include/vrv/options.h index 98a66d74f36..9e7e80b1f11 100644 --- a/include/vrv/options.h +++ b/include/vrv/options.h @@ -632,6 +632,7 @@ class Options { OptionBool m_incip; OptionBool m_justifyVertically; OptionBool m_landscape; + OptionInt m_maxThreads; OptionDbl m_minLastJustification; OptionBool m_mmOutput; OptionBool m_moveScoreDefinitionToStaff; diff --git a/src/functor.cpp b/src/functor.cpp new file mode 100644 index 00000000000..a5c23ff775a --- /dev/null +++ b/src/functor.cpp @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: functor.cpp +// Author: David Bauer +// Created: 2024 +// Copyright (c) Authors and others. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#include "functor.h" + +//---------------------------------------------------------------------------- + +#include "doc.h" + +namespace vrv { + +//---------------------------------------------------------------------------- +// DocFunctor +//---------------------------------------------------------------------------- + +int DocFunctor::GetMaxNumberOfThreads() const +{ + return m_doc->GetOptions()->m_maxThreads.GetValue(); +} + +//---------------------------------------------------------------------------- +// DocConstFunctor +//---------------------------------------------------------------------------- + +int DocConstFunctor::GetMaxNumberOfThreads() const +{ + return m_doc->GetOptions()->m_maxThreads.GetValue(); +} + +} // namespace vrv diff --git a/src/options.cpp b/src/options.cpp index 0a24f1c16bd..af9dfa6d89d 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -1050,6 +1050,11 @@ Options::Options() m_landscape.Init(false); this->Register(&m_landscape, "landscape", &m_general); + m_maxThreads.SetInfo( + "Maximal number of threads", "The maximal number of threads (values above 1 activate parallelization)"); + m_maxThreads.Init(1, 1, 256); + this->Register(&m_maxThreads, "maxThreads", &m_general); + m_minLastJustification.SetInfo("Minimum last-system-justification width", "The last system is only justified if the unjustified width is greater than this percent"); m_minLastJustification.Init(0.8, 0.0, 1.0); From b29e8e3a2757594801ac074660d0df6a84649cc8 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sun, 4 Feb 2024 21:36:14 +0100 Subject: [PATCH 03/14] Clone and merge functors --- include/vrv/functor.h | 24 ++++++++++++++++++++++++ src/functor.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/include/vrv/functor.h b/include/vrv/functor.h index 371f3ba7ddb..7037f6b7935 100644 --- a/include/vrv/functor.h +++ b/include/vrv/functor.h @@ -119,6 +119,18 @@ class Functor : public FunctorBase, public FunctorInterface { virtual ~Functor() = default; ///@} + /** + * Copy child classes + * Must be overridden in order to use it (e.g. during parallelization) + */ + virtual Functor *CloneFunctor() const; + + /** + * Merge child classes, i.e. combine the state of the functor passed in with the current one + * The default implementation only considers the functor code + */ + virtual void MergeFunctor(const Functor &functor); + private: // public: @@ -144,6 +156,18 @@ class ConstFunctor : public FunctorBase, public ConstFunctorInterface { virtual ~ConstFunctor() = default; ///@} + /** + * Copy child classes + * Must be overridden in order to use it (e.g. during parallelization) + */ + virtual ConstFunctor *CloneFunctor() const; + + /** + * Merge child classes, i.e. combine the state of the functor passed in with the current one + * The default implementation only considers the functor code + */ + virtual void MergeFunctor(const ConstFunctor &functor); + private: // public: diff --git a/src/functor.cpp b/src/functor.cpp index a5c23ff775a..2b7d3584b80 100644 --- a/src/functor.cpp +++ b/src/functor.cpp @@ -13,6 +13,40 @@ namespace vrv { +//---------------------------------------------------------------------------- +// Functor +//---------------------------------------------------------------------------- + +Functor *Functor::CloneFunctor() const +{ + assert(false); + return NULL; +} + +void Functor::MergeFunctor(const Functor &functor) +{ + if (functor.GetCode() == FUNCTOR_STOP) { + this->SetCode(FUNCTOR_STOP); + } +} + +//---------------------------------------------------------------------------- +// ConstFunctor +//---------------------------------------------------------------------------- + +ConstFunctor *ConstFunctor::CloneFunctor() const +{ + assert(false); + return NULL; +} + +void ConstFunctor::MergeFunctor(const ConstFunctor &functor) +{ + if (functor.GetCode() == FUNCTOR_STOP) { + this->SetCode(FUNCTOR_STOP); + } +} + //---------------------------------------------------------------------------- // DocFunctor //---------------------------------------------------------------------------- From 1cb58349a00d16e966c475cc525228a232825730 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Tue, 6 Feb 2024 23:17:46 +0100 Subject: [PATCH 04/14] Collect parallel processed objects --- include/vrv/functor.h | 10 ++++++++++ src/functor.cpp | 16 ++++++++++++++++ src/object.cpp | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/include/vrv/functor.h b/include/vrv/functor.h index 7037f6b7935..31fbabcab79 100644 --- a/include/vrv/functor.h +++ b/include/vrv/functor.h @@ -8,6 +8,10 @@ #ifndef __VRV_FUNCTOR_H__ #define __VRV_FUNCTOR_H__ +#include + +//---------------------------------------------------------------------------- + #include "comparison.h" #include "functorinterface.h" #include "vrvdef.h" @@ -87,6 +91,12 @@ class FunctorBase { virtual int GetMaxNumberOfThreads() const { return 1; } ///@} + /** + * Returns the object class on which parallelization is applied + * Additionally checks if we have more than one thread + */ + std::optional GetParallelizationClass() const; + private: // public: diff --git a/src/functor.cpp b/src/functor.cpp index 2b7d3584b80..e7e440bae7f 100644 --- a/src/functor.cpp +++ b/src/functor.cpp @@ -13,6 +13,22 @@ namespace vrv { +//---------------------------------------------------------------------------- +// FunctorBase +//---------------------------------------------------------------------------- + +std::optional FunctorBase::GetParallelizationClass() const +{ + if (this->GetMaxNumberOfThreads() > 1) { + switch (this->GetProcessingStrategy()) { + case ProcessingStrategy::MeasureParallel: return MEASURE; + case ProcessingStrategy::SystemParallel: return SYSTEM; + default: break; + } + } + return std::nullopt; +} + //---------------------------------------------------------------------------- // Functor //---------------------------------------------------------------------------- diff --git a/src/object.cpp b/src/object.cpp index 086e4589db2..f54d589cd07 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1039,6 +1039,9 @@ void Object::Process(Functor &functor, int deepness, bool skipFirst) --deepness; if (!this->SkipChildren(functor.VisibleOnly())) { + // Objects which will be processed in parallel are collected here + ArrayOfObjects parallelProcessed; + const std::optional branchClass = functor.GetParallelizationClass(); // We need a pointer to the array for the option to work on a reversed copy ArrayOfObjects *children = &m_children; Filters *filters = functor.GetFilters(); @@ -1046,7 +1049,12 @@ void Object::Process(Functor &functor, int deepness, bool skipFirst) for (ArrayOfObjects::reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - (*iter)->Process(functor, deepness); + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); + } } } } @@ -1054,7 +1062,12 @@ void Object::Process(Functor &functor, int deepness, bool skipFirst) for (ArrayOfObjects::iterator iter = children->begin(); iter != children->end(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - (*iter)->Process(functor, deepness); + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); + } } } } @@ -1093,6 +1106,9 @@ void Object::Process(ConstFunctor &functor, int deepness, bool skipFirst) const --deepness; if (!this->SkipChildren(functor.VisibleOnly())) { + // Objects which will be processed in parallel are collected here + ArrayOfConstObjects parallelProcessed; + const std::optional branchClass = functor.GetParallelizationClass(); // We need a pointer to the array for the option to work on a reversed copy const ArrayOfObjects *children = &m_children; Filters *filters = functor.GetFilters(); @@ -1100,7 +1116,12 @@ void Object::Process(ConstFunctor &functor, int deepness, bool skipFirst) const for (ArrayOfObjects::const_reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - (*iter)->Process(functor, deepness); + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); + } } } } @@ -1108,7 +1129,12 @@ void Object::Process(ConstFunctor &functor, int deepness, bool skipFirst) const for (ArrayOfObjects::const_iterator iter = children->begin(); iter != children->end(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - (*iter)->Process(functor, deepness); + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); + } } } } From 6bec8cf53bce2414ffd29c42d533e5b9b2e65c4e Mon Sep 17 00:00:00 2001 From: David Bauer Date: Tue, 6 Feb 2024 23:35:30 +0100 Subject: [PATCH 05/14] Introduce ProcessChildren method --- include/vrv/object.h | 2 + src/object.cpp | 134 +++++++++++++++++++++++-------------------- 2 files changed, 74 insertions(+), 62 deletions(-) diff --git a/include/vrv/object.h b/include/vrv/object.h index 31bbf863f95..e7abc0e83be 100644 --- a/include/vrv/object.h +++ b/include/vrv/object.h @@ -646,7 +646,9 @@ class Object : public BoundingBox { */ ///@{ void Process(Functor &functor, int deepness = UNLIMITED_DEPTH, bool skipFirst = false); + void ProcessChildren(Functor &functor, int deepness); void Process(ConstFunctor &functor, int deepness = UNLIMITED_DEPTH, bool skipFirst = false) const; + void ProcessChildren(ConstFunctor &functor, int deepness) const; ///@} /** diff --git a/src/object.cpp b/src/object.cpp index f54d589cd07..2dae794d2dc 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1039,44 +1039,49 @@ void Object::Process(Functor &functor, int deepness, bool skipFirst) --deepness; if (!this->SkipChildren(functor.VisibleOnly())) { - // Objects which will be processed in parallel are collected here - ArrayOfObjects parallelProcessed; - const std::optional branchClass = functor.GetParallelizationClass(); - // We need a pointer to the array for the option to work on a reversed copy - ArrayOfObjects *children = &m_children; - Filters *filters = functor.GetFilters(); - if (functor.GetDirection() == BACKWARD) { - for (ArrayOfObjects::reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { - // we will end here if there is no filter at all or for the current child type - if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { - parallelProcessed.push_back(*iter); - } - else { - (*iter)->Process(functor, deepness); - } + this->ProcessChildren(functor, deepness); + } + + if (functor.ImplementsEndInterface() && !skipFirst) { + FunctorCode code = this->AcceptEnd(functor); + functor.SetCode(code); + } +} + +void Object::ProcessChildren(Functor &functor, int deepness) +{ + // Objects which will be processed in parallel are collected here + ArrayOfObjects parallelProcessed; + const std::optional branchClass = functor.GetParallelizationClass(); + // We need a pointer to the array for the option to work on a reversed copy + ArrayOfObjects *children = &m_children; + Filters *filters = functor.GetFilters(); + if (functor.GetDirection() == BACKWARD) { + for (ArrayOfObjects::reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { + // we will end here if there is no filter at all or for the current child type + if (this->FiltersApply(filters, *iter)) { + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); } } } - else { - for (ArrayOfObjects::iterator iter = children->begin(); iter != children->end(); ++iter) { - // we will end here if there is no filter at all or for the current child type - if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { - parallelProcessed.push_back(*iter); - } - else { - (*iter)->Process(functor, deepness); - } + } + else { + for (ArrayOfObjects::iterator iter = children->begin(); iter != children->end(); ++iter) { + // we will end here if there is no filter at all or for the current child type + if (this->FiltersApply(filters, *iter)) { + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); } } } } - - if (functor.ImplementsEndInterface() && !skipFirst) { - FunctorCode code = this->AcceptEnd(functor); - functor.SetCode(code); - } } void Object::Process(ConstFunctor &functor, int deepness, bool skipFirst) const @@ -1106,44 +1111,49 @@ void Object::Process(ConstFunctor &functor, int deepness, bool skipFirst) const --deepness; if (!this->SkipChildren(functor.VisibleOnly())) { - // Objects which will be processed in parallel are collected here - ArrayOfConstObjects parallelProcessed; - const std::optional branchClass = functor.GetParallelizationClass(); - // We need a pointer to the array for the option to work on a reversed copy - const ArrayOfObjects *children = &m_children; - Filters *filters = functor.GetFilters(); - if (functor.GetDirection() == BACKWARD) { - for (ArrayOfObjects::const_reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { - // we will end here if there is no filter at all or for the current child type - if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { - parallelProcessed.push_back(*iter); - } - else { - (*iter)->Process(functor, deepness); - } + this->ProcessChildren(functor, deepness); + } + + if (functor.ImplementsEndInterface() && !skipFirst) { + FunctorCode code = this->AcceptEnd(functor); + functor.SetCode(code); + } +} + +void Object::ProcessChildren(ConstFunctor &functor, int deepness) const +{ + // Objects which will be processed in parallel are collected here + ArrayOfConstObjects parallelProcessed; + const std::optional branchClass = functor.GetParallelizationClass(); + // We need a pointer to the array for the option to work on a reversed copy + const ArrayOfObjects *children = &m_children; + Filters *filters = functor.GetFilters(); + if (functor.GetDirection() == BACKWARD) { + for (ArrayOfObjects::const_reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { + // we will end here if there is no filter at all or for the current child type + if (this->FiltersApply(filters, *iter)) { + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); } } } - else { - for (ArrayOfObjects::const_iterator iter = children->begin(); iter != children->end(); ++iter) { - // we will end here if there is no filter at all or for the current child type - if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { - parallelProcessed.push_back(*iter); - } - else { - (*iter)->Process(functor, deepness); - } + } + else { + for (ArrayOfObjects::const_iterator iter = children->begin(); iter != children->end(); ++iter) { + // we will end here if there is no filter at all or for the current child type + if (this->FiltersApply(filters, *iter)) { + if (branchClass && (*iter)->Is(*branchClass)) { + parallelProcessed.push_back(*iter); + } + else { + (*iter)->Process(functor, deepness); } } } } - - if (functor.ImplementsEndInterface() && !skipFirst) { - FunctorCode code = this->AcceptEnd(functor); - functor.SetCode(code); - } } FunctorCode Object::Accept(Functor &functor) From 0ce9a87a1b8f764b4841e1a9cc10b9b7f83a0c40 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 8 Feb 2024 18:16:45 +0100 Subject: [PATCH 06/14] Process objects in parallel --- include/vrv/functor.h | 2 +- include/vrv/object.h | 2 ++ src/functor.cpp | 2 +- src/object.cpp | 84 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/include/vrv/functor.h b/include/vrv/functor.h index 31fbabcab79..8026f596da4 100644 --- a/include/vrv/functor.h +++ b/include/vrv/functor.h @@ -95,7 +95,7 @@ class FunctorBase { * Returns the object class on which parallelization is applied * Additionally checks if we have more than one thread */ - std::optional GetParallelizationClass() const; + std::optional GetConcurrentClass() const; private: // diff --git a/include/vrv/object.h b/include/vrv/object.h index e7abc0e83be..7d74e7136fa 100644 --- a/include/vrv/object.h +++ b/include/vrv/object.h @@ -647,8 +647,10 @@ class Object : public BoundingBox { ///@{ void Process(Functor &functor, int deepness = UNLIMITED_DEPTH, bool skipFirst = false); void ProcessChildren(Functor &functor, int deepness); + void ProcessInParallel(Functor &functor, int deepness, const ArrayOfObjects &objects); void Process(ConstFunctor &functor, int deepness = UNLIMITED_DEPTH, bool skipFirst = false) const; void ProcessChildren(ConstFunctor &functor, int deepness) const; + void ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayOfConstObjects &objects); ///@} /** diff --git a/src/functor.cpp b/src/functor.cpp index e7e440bae7f..8fb57914a67 100644 --- a/src/functor.cpp +++ b/src/functor.cpp @@ -17,7 +17,7 @@ namespace vrv { // FunctorBase //---------------------------------------------------------------------------- -std::optional FunctorBase::GetParallelizationClass() const +std::optional FunctorBase::GetConcurrentClass() const { if (this->GetMaxNumberOfThreads() > 1) { switch (this->GetProcessingStrategy()) { diff --git a/src/object.cpp b/src/object.cpp index 2dae794d2dc..fc985adc431 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -11,10 +11,12 @@ #include #include +#include #include #include #include #include +#include //---------------------------------------------------------------------------- @@ -1052,7 +1054,7 @@ void Object::ProcessChildren(Functor &functor, int deepness) { // Objects which will be processed in parallel are collected here ArrayOfObjects parallelProcessed; - const std::optional branchClass = functor.GetParallelizationClass(); + const std::optional concurrentClass = functor.GetConcurrentClass(); // We need a pointer to the array for the option to work on a reversed copy ArrayOfObjects *children = &m_children; Filters *filters = functor.GetFilters(); @@ -1060,7 +1062,7 @@ void Object::ProcessChildren(Functor &functor, int deepness) for (ArrayOfObjects::reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { + if (concurrentClass && (*iter)->Is(*concurrentClass)) { parallelProcessed.push_back(*iter); } else { @@ -1073,7 +1075,7 @@ void Object::ProcessChildren(Functor &functor, int deepness) for (ArrayOfObjects::iterator iter = children->begin(); iter != children->end(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { + if (concurrentClass && (*iter)->Is(*concurrentClass)) { parallelProcessed.push_back(*iter); } else { @@ -1084,6 +1086,41 @@ void Object::ProcessChildren(Functor &functor, int deepness) } } +void Object::ProcessInParallel(Functor &functor, int deepness, const ArrayOfObjects &objects) +{ + const int hardwareLimit = static_cast(std::thread::hardware_concurrency()); + const int concurrency = std::min(functor.GetMaxNumberOfThreads(), hardwareLimit); + assert(concurrency >= 1); + + // Assign the objects to tasks + std::vector objectsPerTask(concurrency); + for (int index = 0; index < objects.size(); ++index) { + objectsPerTask[index % concurrency].push_back(objects[index]); + } + + // Clone the functor for each task + std::vector functorClones(concurrency, functor.CloneFunctor()); + + // Launch parallel tasks + std::vector> futures; + for (int taskIndex = 0; taskIndex < concurrency; ++taskIndex) { + futures.push_back(std::async(std::launch::async, [&objectsPerTask, &functorClones, taskIndex, deepness] { + for (Object *object : objectsPerTask[taskIndex]) { + object->Process(*functorClones[taskIndex], deepness); + } + })); + } + + // Synchronize and merge + for (std::future &future : futures) { + future.get(); + } + for (Functor *clone : functorClones) { + functor.MergeFunctor(*clone); + delete clone; + } +} + void Object::Process(ConstFunctor &functor, int deepness, bool skipFirst) const { if (functor.GetCode() == FUNCTOR_STOP) { @@ -1124,7 +1161,7 @@ void Object::ProcessChildren(ConstFunctor &functor, int deepness) const { // Objects which will be processed in parallel are collected here ArrayOfConstObjects parallelProcessed; - const std::optional branchClass = functor.GetParallelizationClass(); + const std::optional concurrentClass = functor.GetConcurrentClass(); // We need a pointer to the array for the option to work on a reversed copy const ArrayOfObjects *children = &m_children; Filters *filters = functor.GetFilters(); @@ -1132,7 +1169,7 @@ void Object::ProcessChildren(ConstFunctor &functor, int deepness) const for (ArrayOfObjects::const_reverse_iterator iter = children->rbegin(); iter != children->rend(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { + if (concurrentClass && (*iter)->Is(*concurrentClass)) { parallelProcessed.push_back(*iter); } else { @@ -1145,7 +1182,7 @@ void Object::ProcessChildren(ConstFunctor &functor, int deepness) const for (ArrayOfObjects::const_iterator iter = children->begin(); iter != children->end(); ++iter) { // we will end here if there is no filter at all or for the current child type if (this->FiltersApply(filters, *iter)) { - if (branchClass && (*iter)->Is(*branchClass)) { + if (concurrentClass && (*iter)->Is(*concurrentClass)) { parallelProcessed.push_back(*iter); } else { @@ -1156,6 +1193,41 @@ void Object::ProcessChildren(ConstFunctor &functor, int deepness) const } } +void Object::ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayOfConstObjects &objects) +{ + const int hardwareLimit = static_cast(std::thread::hardware_concurrency()); + const int concurrency = std::min(functor.GetMaxNumberOfThreads(), hardwareLimit); + assert(concurrency >= 1); + + // Assign the objects to tasks + std::vector objectsPerTask(concurrency); + for (int index = 0; index < objects.size(); ++index) { + objectsPerTask[index % concurrency].push_back(objects[index]); + } + + // Clone the functor for each task + std::vector functorClones(concurrency, functor.CloneFunctor()); + + // Launch parallel tasks + std::vector> futures; + for (int taskIndex = 0; taskIndex < concurrency; ++taskIndex) { + futures.push_back(std::async(std::launch::async, [&objectsPerTask, &functorClones, taskIndex, deepness] { + for (const Object *object : objectsPerTask[taskIndex]) { + object->Process(*functorClones[taskIndex], deepness); + } + })); + } + + // Synchronize and merge + for (std::future &future : futures) { + future.get(); + } + for (ConstFunctor *clone : functorClones) { + functor.MergeFunctor(*clone); + delete clone; + } +} + FunctorCode Object::Accept(Functor &functor) { return functor.VisitObject(this); From cd728e73e8f1679ee594be8e04a4eaf01e77cff4 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 8 Feb 2024 22:15:48 +0100 Subject: [PATCH 07/14] Add missing call --- include/vrv/object.h | 2 +- src/object.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/vrv/object.h b/include/vrv/object.h index 7d74e7136fa..4e779c49373 100644 --- a/include/vrv/object.h +++ b/include/vrv/object.h @@ -650,7 +650,7 @@ class Object : public BoundingBox { void ProcessInParallel(Functor &functor, int deepness, const ArrayOfObjects &objects); void Process(ConstFunctor &functor, int deepness = UNLIMITED_DEPTH, bool skipFirst = false) const; void ProcessChildren(ConstFunctor &functor, int deepness) const; - void ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayOfConstObjects &objects); + void ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayOfConstObjects &objects) const; ///@} /** diff --git a/src/object.cpp b/src/object.cpp index fc985adc431..fb047097df9 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1084,6 +1084,10 @@ void Object::ProcessChildren(Functor &functor, int deepness) } } } + // Perform parallel processing + if (!parallelProcessed.empty()) { + this->ProcessInParallel(functor, deepness, parallelProcessed); + } } void Object::ProcessInParallel(Functor &functor, int deepness, const ArrayOfObjects &objects) @@ -1191,9 +1195,13 @@ void Object::ProcessChildren(ConstFunctor &functor, int deepness) const } } } + // Perform parallel processing + if (!parallelProcessed.empty()) { + this->ProcessInParallel(functor, deepness, parallelProcessed); + } } -void Object::ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayOfConstObjects &objects) +void Object::ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayOfConstObjects &objects) const { const int hardwareLimit = static_cast(std::thread::hardware_concurrency()); const int concurrency = std::min(functor.GetMaxNumberOfThreads(), hardwareLimit); From c3d34780d2c6588fb0c0edc928b34bd1b3739344 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 8 Feb 2024 22:35:52 +0100 Subject: [PATCH 08/14] Fix functor cloning --- src/object.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/object.cpp b/src/object.cpp index fb047097df9..dbff0550f9b 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1103,7 +1103,10 @@ void Object::ProcessInParallel(Functor &functor, int deepness, const ArrayOfObje } // Clone the functor for each task - std::vector functorClones(concurrency, functor.CloneFunctor()); + std::vector functorClones; + for (int taskIndex = 0; taskIndex < concurrency; ++taskIndex) { + functorClones.push_back(functor.CloneFunctor()); + } // Launch parallel tasks std::vector> futures; @@ -1214,7 +1217,10 @@ void Object::ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayO } // Clone the functor for each task - std::vector functorClones(concurrency, functor.CloneFunctor()); + std::vector functorClones; + for (int taskIndex = 0; taskIndex < concurrency; ++taskIndex) { + functorClones.push_back(functor.CloneFunctor()); + } // Launch parallel tasks std::vector> futures; From ef8be154ab078d1b65e1819f7d690178a43cd71c Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 8 Feb 2024 22:45:20 +0100 Subject: [PATCH 09/14] Parallelize the CalcStemFunctor --- include/vrv/calcstemfunctor.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/vrv/calcstemfunctor.h b/include/vrv/calcstemfunctor.h index ee414083668..f49d0002d4a 100644 --- a/include/vrv/calcstemfunctor.h +++ b/include/vrv/calcstemfunctor.h @@ -34,6 +34,14 @@ class CalcStemFunctor : public DocFunctor { */ bool ImplementsEndInterface() const override { return false; } + /* + * Enable parallelization + */ + ///@{ + ProcessingStrategy GetProcessingStrategy() const override { return ProcessingStrategy::MeasureParallel; } + CalcStemFunctor *CloneFunctor() const override { return new CalcStemFunctor(*this); } + ///@} + /* * Functor interface */ From e0ccd4031ba0ebd03b56e89572562d64206f593d Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 8 Feb 2024 22:59:54 +0100 Subject: [PATCH 10/14] Parallelize the CalcDotsFunctor --- include/vrv/calcdotsfunctor.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/vrv/calcdotsfunctor.h b/include/vrv/calcdotsfunctor.h index 7c490bac9f5..04413c55f54 100644 --- a/include/vrv/calcdotsfunctor.h +++ b/include/vrv/calcdotsfunctor.h @@ -34,6 +34,14 @@ class CalcDotsFunctor : public DocFunctor { */ bool ImplementsEndInterface() const override { return false; } + /* + * Enable parallelization + */ + ///@{ + ProcessingStrategy GetProcessingStrategy() const override { return ProcessingStrategy::MeasureParallel; } + CalcDotsFunctor *CloneFunctor() const override { return new CalcDotsFunctor(*this); } + ///@} + /* * Functor interface */ From 200a4d33e34312a4c03d002a586f76e7bd9fbc1c Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 8 Feb 2024 23:16:42 +0100 Subject: [PATCH 11/14] Parallelize ResetFunctor --- include/vrv/resetfunctor.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/vrv/resetfunctor.h b/include/vrv/resetfunctor.h index f8f4d5c1032..876040c79d0 100644 --- a/include/vrv/resetfunctor.h +++ b/include/vrv/resetfunctor.h @@ -109,6 +109,17 @@ class ResetHorizontalAlignmentFunctor : public Functor { */ bool ImplementsEndInterface() const override { return false; } + /* + * Enable parallelization + */ + ///@{ + ProcessingStrategy GetProcessingStrategy() const override { return ProcessingStrategy::MeasureParallel; } + ResetHorizontalAlignmentFunctor *CloneFunctor() const override + { + return new ResetHorizontalAlignmentFunctor(*this); + } + ///@} + /* * Functor interface */ @@ -167,6 +178,14 @@ class ResetVerticalAlignmentFunctor : public Functor { */ bool ImplementsEndInterface() const override { return false; } + /* + * Enable parallelization + */ + ///@{ + ProcessingStrategy GetProcessingStrategy() const override { return ProcessingStrategy::MeasureParallel; } + ResetVerticalAlignmentFunctor *CloneFunctor() const override { return new ResetVerticalAlignmentFunctor(*this); } + ///@} + /* * Functor interface */ From 12dd3ab3940402032a1898ee1847ab051282958c Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sat, 10 Feb 2024 22:13:15 +0100 Subject: [PATCH 12/14] Parallelize CalcBBoxOverflows --- include/vrv/calcbboxoverflowsfunctor.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/vrv/calcbboxoverflowsfunctor.h b/include/vrv/calcbboxoverflowsfunctor.h index e5580cab0a7..2fbe5e94ea9 100644 --- a/include/vrv/calcbboxoverflowsfunctor.h +++ b/include/vrv/calcbboxoverflowsfunctor.h @@ -35,6 +35,14 @@ class CalcBBoxOverflowsFunctor : public DocFunctor { */ bool ImplementsEndInterface() const override { return true; } + /* + * Enable parallelization + */ + ///@{ + ProcessingStrategy GetProcessingStrategy() const override { return ProcessingStrategy::SystemParallel; } + CalcBBoxOverflowsFunctor *CloneFunctor() const override { return new CalcBBoxOverflowsFunctor(*this); } + ///@} + /* * Functor interface */ From d93a14348e042a1dbb723a05b452c02e9cc7e9ef Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sun, 11 Feb 2024 13:54:01 +0100 Subject: [PATCH 13/14] Parallelize AdjustSlurs --- include/vrv/adjustslursfunctor.h | 9 +++++++++ include/vrv/functor.h | 4 ++-- src/adjustslursfunctor.cpp | 8 ++++++++ src/functor.cpp | 8 ++++---- src/object.cpp | 4 ++-- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/include/vrv/adjustslursfunctor.h b/include/vrv/adjustslursfunctor.h index cc4665938fc..047f173d6e5 100644 --- a/include/vrv/adjustslursfunctor.h +++ b/include/vrv/adjustslursfunctor.h @@ -61,6 +61,15 @@ class AdjustSlursFunctor : public DocFunctor { */ bool ImplementsEndInterface() const override { return false; } + /* + * Enable parallelization + */ + ///@{ + ProcessingStrategy GetProcessingStrategy() const override { return ProcessingStrategy::SystemParallel; } + AdjustSlursFunctor *CloneFunctor() const override { return new AdjustSlursFunctor(*this); } + void MergeFunctor(const Functor *functor) override; + ///@} + /* * Check existence of cross-staff slurs */ diff --git a/include/vrv/functor.h b/include/vrv/functor.h index 8026f596da4..06fab581253 100644 --- a/include/vrv/functor.h +++ b/include/vrv/functor.h @@ -139,7 +139,7 @@ class Functor : public FunctorBase, public FunctorInterface { * Merge child classes, i.e. combine the state of the functor passed in with the current one * The default implementation only considers the functor code */ - virtual void MergeFunctor(const Functor &functor); + virtual void MergeFunctor(const Functor *functor); private: // @@ -176,7 +176,7 @@ class ConstFunctor : public FunctorBase, public ConstFunctorInterface { * Merge child classes, i.e. combine the state of the functor passed in with the current one * The default implementation only considers the functor code */ - virtual void MergeFunctor(const ConstFunctor &functor); + virtual void MergeFunctor(const ConstFunctor *functor); private: // diff --git a/src/adjustslursfunctor.cpp b/src/adjustslursfunctor.cpp index 34d5bb61c6b..2a8460b2112 100644 --- a/src/adjustslursfunctor.cpp +++ b/src/adjustslursfunctor.cpp @@ -30,6 +30,14 @@ AdjustSlursFunctor::AdjustSlursFunctor(Doc *doc) : DocFunctor(doc) this->ResetCurrent(); } +void AdjustSlursFunctor::MergeFunctor(const Functor *functor) +{ + const AdjustSlursFunctor *adjustSlursFunctor = dynamic_cast(functor); + if (adjustSlursFunctor && adjustSlursFunctor->HasCrossStaffSlurs()) { + m_crossStaffSlurs = true; + } +} + void AdjustSlursFunctor::ResetCurrent() { m_currentCurve = NULL; diff --git a/src/functor.cpp b/src/functor.cpp index 8fb57914a67..7056adf9fd6 100644 --- a/src/functor.cpp +++ b/src/functor.cpp @@ -39,9 +39,9 @@ Functor *Functor::CloneFunctor() const return NULL; } -void Functor::MergeFunctor(const Functor &functor) +void Functor::MergeFunctor(const Functor *functor) { - if (functor.GetCode() == FUNCTOR_STOP) { + if (functor->GetCode() == FUNCTOR_STOP) { this->SetCode(FUNCTOR_STOP); } } @@ -56,9 +56,9 @@ ConstFunctor *ConstFunctor::CloneFunctor() const return NULL; } -void ConstFunctor::MergeFunctor(const ConstFunctor &functor) +void ConstFunctor::MergeFunctor(const ConstFunctor *functor) { - if (functor.GetCode() == FUNCTOR_STOP) { + if (functor->GetCode() == FUNCTOR_STOP) { this->SetCode(FUNCTOR_STOP); } } diff --git a/src/object.cpp b/src/object.cpp index dbff0550f9b..092e800890b 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -1123,7 +1123,7 @@ void Object::ProcessInParallel(Functor &functor, int deepness, const ArrayOfObje future.get(); } for (Functor *clone : functorClones) { - functor.MergeFunctor(*clone); + functor.MergeFunctor(clone); delete clone; } } @@ -1237,7 +1237,7 @@ void Object::ProcessInParallel(ConstFunctor &functor, int deepness, const ArrayO future.get(); } for (ConstFunctor *clone : functorClones) { - functor.MergeFunctor(*clone); + functor.MergeFunctor(clone); delete clone; } } From ab6c225dfa9e3470fe55664bc5c5adff8e6975bb Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sun, 11 Feb 2024 21:51:49 +0100 Subject: [PATCH 14/14] Call base method --- src/adjustslursfunctor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/adjustslursfunctor.cpp b/src/adjustslursfunctor.cpp index 2a8460b2112..2d2d709abb2 100644 --- a/src/adjustslursfunctor.cpp +++ b/src/adjustslursfunctor.cpp @@ -32,6 +32,8 @@ AdjustSlursFunctor::AdjustSlursFunctor(Doc *doc) : DocFunctor(doc) void AdjustSlursFunctor::MergeFunctor(const Functor *functor) { + Functor::MergeFunctor(functor); + const AdjustSlursFunctor *adjustSlursFunctor = dynamic_cast(functor); if (adjustSlursFunctor && adjustSlursFunctor->HasCrossStaffSlurs()) { m_crossStaffSlurs = true;