Skip to content

Commit a6674c4

Browse files
authored
DPL Analysis: do not build the tasks a device does not run (#16036)
1 parent d1cef57 commit a6674c4

7 files changed

Lines changed: 120 additions & 0 deletions

File tree

‎Framework/Core/include/Framework/AnalysisTask.h‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "Framework/ConfigParamsHelper.h"
2020
#include "Framework/ControlService.h"
2121
#include "Framework/DataProcessorSpec.h"
22+
#include "Framework/DataProcessorSpecHelpers.h"
2223
#include "Framework/Expressions.h"
2324
#include "Framework/EndOfStreamContext.h"
2425
#include "Framework/GroupSlicer.h"
@@ -450,6 +451,24 @@ struct TaskName {
450451

451452
namespace
452453
{
454+
template <typename T, typename... A>
455+
std::string getTaskName(TaskName const& first, A const&...)
456+
{
457+
return first.value;
458+
}
459+
460+
template <typename T, typename... A>
461+
std::string getTaskName(SetDefaultProcesses const&, TaskName const& second, A const&...)
462+
{
463+
return second.value;
464+
}
465+
466+
template <typename T, typename... A>
467+
std::string getTaskName(A const&...)
468+
{
469+
return type_to_task_name(type_name<T>());
470+
}
471+
453472
template <typename T, typename... A>
454473
auto getTaskNameSetProcesses(std::string& outputName, TaskName first, SetDefaultProcesses second, A... args)
455474
{
@@ -519,6 +538,10 @@ auto getTaskNameSetProcesses(std::string& outputName, A... args)
519538
template <typename T, typename... Args>
520539
DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args)
521540
{
541+
if (auto name = getTaskName<T>(args...) + ctx.options().get<std::string>("workflow-suffix"); !ctx.mayRun(name)) {
542+
return DataProcessorSpecHelpers::placeholder(name);
543+
}
544+
522545
TH1::AddDirectory(false);
523546

524547
std::string name_str;

‎Framework/Core/include/Framework/ConfigContext.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ class ConfigContext
2929
[[nodiscard]] ServiceRegistryRef services() const { return mServices; }
3030

3131
[[nodiscard]] bool helpOnCommandLine() const;
32+
/// Whether this process may run @a processor. A device only runs its own;
33+
/// it gets the specs of the others from the driver, so they need not be built.
34+
[[nodiscard]] bool mayRun(std::string_view processor) const;
3235

3336
[[nodiscard]] int argc() const { return mArgc; }
3437
[[nodiscard]] char* const* argv() const { return mArgv; }

‎Framework/Core/include/Framework/DataProcessorSpecHelpers.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ namespace o2::framework
1818
{
1919
struct DataProcessorSpecHelpers {
2020
static bool hasLabel(DataProcessorSpec const& spec, char const* name);
21+
/// Stands for a processor this process does not run: everything but the name
22+
/// is filled from the workflow the driver passes to the device.
23+
static DataProcessorSpec placeholder(std::string name);
24+
static bool isPlaceholder(DataProcessorSpec const& spec);
2125
};
2226
} // namespace o2::framework
2327
#endif

‎Framework/Core/src/ConfigContext.cxx‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// or submit itself to any jurisdiction.
1111

1212
#include "Framework/ConfigContext.h"
13+
#include <regex>
14+
#include <sys/stat.h>
15+
#include <unistd.h>
1316

1417
namespace o2::framework
1518
{
@@ -29,4 +32,23 @@ bool ConfigContext::helpOnCommandLine() const
2932
return helpasked;
3033
}
3134

35+
bool ConfigContext::mayRun(std::string_view processor) const
36+
{
37+
std::string_view id;
38+
for (int argi = 0; argi + 1 < mArgc; ++argi) {
39+
if (strcmp(mArgv[argi], "--id") == 0) {
40+
id = mArgv[argi + 1];
41+
}
42+
}
43+
// The driver, a device without a workflow on stdin and clones, which keep the
44+
// name of their template, build everything.
45+
struct stat s;
46+
if (id.empty() || fstat(STDIN_FILENO, &s) != 0 || !(S_ISFIFO(s.st_mode) || S_ISREG(s.st_mode)) || !mOptions.get<std::string>("clone").empty()) {
47+
return true;
48+
}
49+
// Pipelined devices get a _t<N> suffix, DDS ones a _dds<...> one.
50+
auto name = std::regex_replace(std::regex_replace(std::string{id}, std::regex{"_dds.*"}, ""), std::regex{"_t[0-9]+$"}, "");
51+
return id == processor || name == processor;
52+
}
53+
3254
} // namespace o2::framework

‎Framework/Core/src/DataProcessorSpecHelpers.cxx‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// or submit itself to any jurisdiction.
1111

1212
#include "Framework/DataProcessorSpecHelpers.h"
13+
#include "Framework/Logger.h"
1314
#include <string>
1415
#include <algorithm>
1516

@@ -20,4 +21,20 @@ bool DataProcessorSpecHelpers::hasLabel(DataProcessorSpec const& spec, char cons
2021
auto sameLabel = [other = DataProcessorLabel{{label}}](DataProcessorLabel const& label) { return label == other; };
2122
return std::find_if(spec.labels.begin(), spec.labels.end(), sameLabel) != spec.labels.end();
2223
}
24+
25+
DataProcessorSpec DataProcessorSpecHelpers::placeholder(std::string name)
26+
{
27+
return DataProcessorSpec{
28+
.name = name,
29+
.algorithm = AlgorithmSpec::InitCallback{[name](InitContext&) -> AlgorithmSpec::ProcessCallback {
30+
LOGP(fatal, "{} was not built in this process, which therefore cannot run it.", name);
31+
return {};
32+
}},
33+
.metadata = {{"dpl-placeholder", "true"}}};
34+
}
35+
36+
bool DataProcessorSpecHelpers::isPlaceholder(DataProcessorSpec const& spec)
37+
{
38+
return std::ranges::any_of(spec.metadata, [](auto const& m) { return m.key == "dpl-placeholder"; });
39+
}
2340
} // namespace o2::framework

‎Framework/Core/src/runDataProcessing.cxx‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "Framework/DataProcessingDevice.h"
2727
#include "Framework/DataProcessingContext.h"
2828
#include "Framework/DataProcessorSpec.h"
29+
#include "Framework/DataProcessorSpecHelpers.h"
2930
#include "Framework/PluginManager.h"
3031
#include "Framework/DeviceControl.h"
3132
#include "Framework/DeviceExecution.h"
@@ -3038,9 +3039,18 @@ int doMain(int argc, char** argv, o2::framework::WorkflowSpec const& workflow,
30383039
if (found == physicalWorkflow.end()) {
30393040
physicalWorkflow.push_back(dp);
30403041
rankIndex.insert(std::make_pair(dp.name, workflowHashB));
3042+
} else if (DataProcessorSpecHelpers::isPlaceholder(*found)) {
3043+
auto algorithm = std::move(found->algorithm);
3044+
*found = dp;
3045+
found->algorithm = std::move(algorithm);
30413046
}
30423047
}
30433048
}
3049+
for (auto& dp : physicalWorkflow) {
3050+
if (DataProcessorSpecHelpers::isPlaceholder(dp)) {
3051+
LOGP(fatal, "{} was not built, but the driver did not pass its spec.", dp.name);
3052+
}
3053+
}
30443054

30453055
/// Iterate over the physicalWorkflow, any DataProcessorSpec that has a
30463056
/// expendable label should have all the timeframe lifetime outputs changed

‎Framework/Core/test/test_AnalysisTask.cxx‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "Framework/AnalysisTask.h"
1515
#include "Framework/AnalysisDataModel.h"
1616
#include <iostream>
17+
#include <unistd.h>
1718

1819
#include <catch_amalgamated.hpp>
1920

@@ -197,6 +198,46 @@ struct MTask {
197198
void process(aod::McCollision const&, soa::SmallGroups<soa::Join<aod::Collisions, aod::McCollisionLabels>> const&) {}
198199
};
199200

201+
struct BuildCounter {
202+
static inline int built = 0;
203+
BuildCounter() { ++built; }
204+
};
205+
206+
struct CountedTask {
207+
BuildCounter counter;
208+
void process(aod::Tracks const&) {}
209+
};
210+
211+
TEST_CASE("AdaptorBuildsOnlyTheTaskOfTheDevice")
212+
{
213+
auto cfgc = makeEmptyConfigContext();
214+
// A device gets the workflow from the driver on stdin.
215+
int fds[2];
216+
REQUIRE(pipe(fds) == 0);
217+
int stdinCopy = dup(STDIN_FILENO);
218+
dup2(fds[0], STDIN_FILENO);
219+
char const* otherArgs[] = {"workflow", "--id", "another-task"};
220+
char const* ownArgs[] = {"workflow", "--id", "counted-task_t1"};
221+
ConfigContext otherDevice{cfgc->options(), cfgc->services(), 3, const_cast<char**>(otherArgs)};
222+
ConfigContext ownDevice{cfgc->options(), cfgc->services(), 3, const_cast<char**>(ownArgs)};
223+
auto skipped = adaptAnalysisTask<CountedTask>(otherDevice);
224+
auto own = adaptAnalysisTask<CountedTask>(ownDevice);
225+
auto renamed = adaptAnalysisTask<CountedTask>(otherDevice, SetDefaultProcesses{}, TaskName{"another-task"});
226+
dup2(stdinCopy, STDIN_FILENO);
227+
close(stdinCopy);
228+
close(fds[0]);
229+
close(fds[1]);
230+
auto driver = adaptAnalysisTask<CountedTask>(*cfgc);
231+
232+
REQUIRE(BuildCounter::built == 3);
233+
REQUIRE(DataProcessorSpecHelpers::isPlaceholder(skipped));
234+
REQUIRE(skipped.name == own.name);
235+
REQUIRE(skipped.inputs.empty());
236+
REQUIRE(!DataProcessorSpecHelpers::isPlaceholder(own));
237+
REQUIRE(own.inputs.size() == driver.inputs.size());
238+
REQUIRE(!DataProcessorSpecHelpers::isPlaceholder(renamed));
239+
}
240+
200241
TEST_CASE("AdaptorCompilation")
201242
{
202243
auto cfgc = makeEmptyConfigContext();

0 commit comments

Comments
 (0)