Project
Loading...
Searching...
No Matches
TopologyPolicy.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
13#include "Framework/Signpost.h"
14#include <string>
15#include <regex>
16
18
19namespace o2::framework
20{
21
22struct TopologyPolicyHelpers {
24 static TopologyPolicy::DataProcessorMatcher matchByName(std::string const& name);
25 static TopologyPolicy::DataProcessorMatcher matchByRegex(std::string const& re);
28};
29
31{
32 return [](DataProcessorSpec const& spec) {
33 return true;
34 };
35}
36
38{
39 return [name](DataProcessorSpec const& spec) {
40 return spec.name == name;
41 };
42}
43
45{
46 return [re](DataProcessorSpec const& spec) -> bool {
47 const std::regex matcher(re);
48 // Check if regex applies
49 std::cmatch m;
50 return std::regex_match(spec.name.data(), m, matcher);
51 };
52}
53
57{
58 for (size_t ii = 0; ii < a.inputs.size(); ++ii) {
59 for (size_t oi = 0; oi < b.outputs.size(); ++oi) {
60 try {
61 if (DataSpecUtils::match(a.inputs[ii], b.outputs[oi])) {
62 return true;
63 }
64 } catch (...) {
65 continue;
66 }
67 }
68 }
69 return false;
70}
71
72// This is to make sure that if a device has sporadic / timer inputs
73// it gets sorted after one which does not, in case there is no other
74// dependencies between the two.
76{
77 auto checkSporadic = [](InputSpec const& input) {
78 return input.lifetime == Lifetime::Sporadic;
79 };
80 bool isBWithSporadicInput = std::find_if(b.inputs.begin(), b.inputs.end(), checkSporadic) != b.inputs.end();
81 bool isAWithSporadicInput = std::find_if(a.inputs.begin(), a.inputs.end(), checkSporadic) != a.inputs.end();
82 // If neither has sporadic inputs, we return false and sort as usual
83 if (!isAWithSporadicInput && !isBWithSporadicInput) {
84 return false;
85 }
86 // If both have sporadic inputs, we return false and sort as usual.
87 if (isAWithSporadicInput && isBWithSporadicInput) {
88 return false;
89 }
90
91 // We have a with sporadic inputs. We sort it later, unless there was already some actual
92 // dependency between A and B.
93 if (isAWithSporadicInput) {
94 bool hasDependency = dataDeps(b, a);
95 return !hasDependency;
96 }
97
98 // b is has sporadic inputs and a does not. We are fine as it is.
99 return false;
100}
101
103{
104 O2_SIGNPOST_ID_GENERATE(sid, topology);
105 O2_SIGNPOST_START(topology, sid, "expendableDataDeps", "Checking if %s depends on %s", a.name.c_str(), b.name.c_str());
106 if (a.name.find("internal-dpl-injected-dummy-sink") != std::string::npos &&
107 b.name.find("internal-dpl-injected-dummy-sink") != std::string::npos) {
108 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "false. Dummy sink never depends on itself.");
109 return false;
110 }
111 // We never put anything behind the dummy sink.
112 if (b.name.find("internal-dpl-injected-dummy-sink") != std::string::npos) {
113 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "false. %s is dummy sink and it nothing can depend on it.", b.name.c_str());
114 return false;
115 }
116 if (a.name.find("internal-dpl-injected-dummy-sink") != std::string::npos) {
117 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "true. %s is dummy sink and it nothing can depend on it.", a.name.c_str());
118 return true;
119 }
121 if (dataDeps(a, b)) {
122 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "true. %s has a data dependency on %s", a.name.c_str(), b.name.c_str());
123 return true;
124 }
125 // If we are here we do not have any data dependency,
126 // however we still consider a dependent on b if
127 // a has the "expendable" label and b does not.
128 auto checkExpendable = [](DataProcessorLabel const& label) {
129 if (label.value == "expendable") {
130 return true;
131 }
132 return false;
133 };
134 // A task marked as expendable or resilient can be put after an expendable task
135 auto checkResilient = [](DataProcessorLabel const& label) {
136 if (label.value == "resilient") {
137 return true;
138 }
139 return false;
140 };
141
142 bool isBExpendable = std::find_if(b.labels.begin(), b.labels.end(), checkExpendable) != b.labels.end();
143 bool isAExpendable = std::find_if(a.labels.begin(), a.labels.end(), checkExpendable) != a.labels.end();
144 bool bResilient = std::find_if(b.labels.begin(), b.labels.end(), checkResilient) != b.labels.end();
145 const std::regex matcher(".*output-proxy.*");
146 std::cmatch m;
147 bool isBOutputProxy = std::regex_match(b.name.data(), m, matcher);
148
149 // If none is expendable. We simply return false and sort as usual.
150 if (!isAExpendable && !isBExpendable) {
151 bool sporadic = sporadicDataDeps(a, b);
152 if (sporadic && !isBOutputProxy) {
153 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "true. Neither %s nor %s are expendable. However the former has sporadic inputs so we sort it after.",
154 a.name.c_str(), b.name.c_str());
155 return true;
156 }
157 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "false. Neither %s nor %s are expendable. No dependency beyond data deps.",
158 a.name.c_str(), b.name.c_str());
159 return false;
160 }
161 // If both are expendable. We return false and sort as usual.
162 if (isAExpendable && isBExpendable) {
163 bool sporadic = sporadicDataDeps(a, b);
164 if (sporadic && !isBOutputProxy) {
165 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "true. Both %s and %s are expendable. However the former has sporadic inputs, so we sort it after.",
166 a.name.c_str(), b.name.c_str());
167 return true;
168 }
169 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "false. Both %s and %s are expendable. No dependency.",
170 a.name.c_str(), b.name.c_str());
171 return false;
172 }
173
174 // If a is expendable but b is resilient, we can keep the same order.
175 if (isAExpendable && bResilient) {
176 bool sporadic = sporadicDataDeps(a, b);
177 if (sporadic && !isBOutputProxy) {
178 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "true. %s is expendable but %s is resilient, however the former also has sporadic inputs, so we sort it after.",
179 a.name.c_str(), b.name.c_str());
180 return true;
181 }
182 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "false. %s is expendable but %s is resilient. No need to do do anything.",
183 a.name.c_str(), b.name.c_str());
184 return false;
185 }
186 // If a is expendable we consider it as if there was a dependency from a to b,
187 // however we still need to check if there is not one already from b to a.
188 if (isAExpendable) {
189 bool hasDependency = dataDeps(b, a);
190 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "%s is expendable. %s from %s to %s => %s.",
191 a.name.c_str(), hasDependency ? "There is however an inverse dependency" : "No inverse dependency", b.name.c_str(), a.name.c_str(),
192 !hasDependency ? "true" : "false");
193 if (!hasDependency) {
194 return true;
195 }
196 bool sporadic = sporadicDataDeps(a, b);
197 if (sporadic && !isBOutputProxy) {
198 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "%s is expendable. No inverse dependency from %s to %s. However the former has an occasioanl input => true.",
199 a.name.c_str(), b.name.c_str(), a.name.c_str());
200 return true;
201 }
202 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "%s is expendable. No inverse dependency from %s to %s => false.",
203 a.name.c_str(), b.name.c_str(), a.name.c_str());
204 return false;
205 }
206 // b is expendable and a is not. We are fine with no dependency.
207 O2_SIGNPOST_END(topology, sid, "expendableDataDeps", "false. %s is expendable but %s is not. No need to add an unneeded dependency.",
208 b.name.c_str(), a.name.c_str());
209
210 return false;
211};
212
219
221{
222 return [](DataProcessorSpec const& dependent, DataProcessorSpec const& ancestor) {
223 O2_SIGNPOST_ID_GENERATE(sid, topology);
224 O2_SIGNPOST_START(topology, sid, "alwaysDependent", "Checking if %s depends on %s", dependent.name.c_str(), ancestor.name.c_str());
225 if (dependent.name == ancestor.name) {
226 O2_SIGNPOST_END(topology, sid, "alwaysDependent", "false. %s and %s are the same.", dependent.name.c_str(), ancestor.name.c_str());
227 return false;
228 }
229 if (ancestor.name.find("internal-dpl-injected-dummy-sink") != std::string::npos) {
230 O2_SIGNPOST_END(topology, sid, "alwaysDependent", "false. Nothing can depend on %s by policy.", ancestor.name.c_str());
231 return false;
232 }
233 // We never put anything behind the dummy sink.
234 if (dependent.name.find("internal-dpl-injected-dummy-sink") != std::string::npos) {
235 O2_SIGNPOST_END(topology, sid, "alwaysDependent", "true. %s is always last.", ancestor.name.c_str());
236 return true;
237 }
238 const std::regex matcher(".*output-proxy.*");
239 // Check if regex applies
240 std::cmatch m;
241 bool isAncestorOutputProxy = std::regex_match(ancestor.name.data(), m, matcher);
242 // For now dependent is always an output proxy.
243 assert(std::regex_match(dependent.name.data(), m, matcher));
244 bool isAncestorExpendable = std::find_if(ancestor.labels.begin(), ancestor.labels.end(), [](DataProcessorLabel const& label) {
245 return label.value == "expendable";
246 }) != ancestor.labels.end();
247
248 bool isDependentResilient = std::find_if(dependent.labels.begin(), dependent.labels.end(), [](DataProcessorLabel const& label) {
249 return label.value == "resilient";
250 }) != dependent.labels.end();
251 bool isAncestorResilient = std::find_if(ancestor.labels.begin(), ancestor.labels.end(), [](DataProcessorLabel const& label) {
252 return label.value == "resilient";
253 }) != ancestor.labels.end();
254
255 if (!isDependentResilient && isAncestorExpendable) {
256 O2_SIGNPOST_END(topology, sid, "alwaysDependent", "false. Ancestor %s is expendable while %s is non-resilient output proxy (dependent).",
257 ancestor.name.c_str(), dependent.name.c_str());
258 return false;
259 }
260
261 if (isAncestorOutputProxy || (!isDependentResilient && isAncestorResilient)) {
262 bool hasDependency = dataDeps(dependent, ancestor);
263 O2_SIGNPOST_END(topology, sid, "alwaysDependent", "%s. Dependent %s %s a dependency on ancestor %s.",
264 hasDependency ? "true" : "false", dependent.name.c_str(), hasDependency ? "has" : "has not", ancestor.name.c_str());
265 return hasDependency;
266 }
267
268 O2_SIGNPOST_END(topology, sid, "alwaysDependent", "true by default. Ancestor %s is not an output proxy.", ancestor.name.c_str());
269 return true;
270 };
271}
272
279
280} // namespace o2::framework
#define O2_DECLARE_DYNAMIC_LOG(name)
Definition Signpost.h:483
#define O2_SIGNPOST_END(log, id, name, format,...)
Definition Signpost.h:602
#define O2_SIGNPOST_ID_GENERATE(name, log)
Definition Signpost.h:500
#define O2_SIGNPOST_START(log, id, name, format,...)
Definition Signpost.h:596
const GLfloat * m
Definition glcorearb.h:4066
GLuint const GLchar * name
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
bool expendableDataDeps(DataProcessorSpec const &a, DataProcessorSpec const &b)
bool sporadicDataDeps(DataProcessorSpec const &a, DataProcessorSpec const &b)
bool dataDeps(DataProcessorSpec const &a, DataProcessorSpec const &b)
A label that can be associated to a DataProcessorSpec.
std::vector< DataProcessorLabel > labels
static bool match(InputSpec const &spec, ConcreteDataMatcher const &target)
static TopologyPolicy::DependencyChecker dataDependency()
static TopologyPolicy::DataProcessorMatcher matchByRegex(std::string const &re)
static TopologyPolicy::DataProcessorMatcher matchAll()
static TopologyPolicy::DependencyChecker alwaysDependent()
static TopologyPolicy::DataProcessorMatcher matchByName(std::string const &name)
static std::vector< TopologyPolicy > createDefaultPolicies()
std::function< bool(DataProcessorSpec const &dependent, DataProcessorSpec const &ascendant)> DependencyChecker
std::function< bool(DataProcessorSpec const &device)> DataProcessorMatcher