Skip to content

Commit 795c36a

Browse files
authored
Merge 13018ab into sapling-pr-archive-ktf
2 parents 363689b + 13018ab commit 795c36a

File tree

6 files changed

+223
-96
lines changed

6 files changed

+223
-96
lines changed

Framework/Core/include/Framework/DataProcessingHelpers.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "Framework/TimesliceIndex.h"
1717
#include <fairmq/FwdDecls.h>
1818
#include <vector>
19+
#include <span>
1920

2021
namespace o2::framework
2122
{
@@ -53,8 +54,13 @@ struct DataProcessingHelpers {
5354
/// starts the EoS timers and returns the new TransitionHandlingState in case as new state is requested
5455
static TransitionHandlingState updateStateTransition(ServiceRegistryRef const& ref, ProcessingPolicies const& policies);
5556
/// Helper to route messages for forwarding
56-
static std::vector<fair::mq::Parts> routeForwardedMessages(FairMQDeviceProxy& proxy, std::vector<MessageSet>& currentSetOfInputs,
57-
bool copy, bool consume);
57+
static std::vector<fair::mq::Parts> routeForwardedMessageSet(FairMQDeviceProxy& proxy, std::vector<MessageSet>& currentSetOfInputs,
58+
bool copy, bool consume);
59+
/// Helper to route messages for forwarding
60+
static void routeForwardedMessages(FairMQDeviceProxy& proxy, std::span<fair::mq::MessagePtr>& currentSetOfInputs, std::vector<fair::mq::Parts>& forwardedParts,
61+
bool copy, bool consume);
62+
/// clean the headers when finally consuming a slot
63+
static void cleanForwardedMessageSet(std::vector<MessageSet>& currentSetOfInputs);
5864
};
5965
} // namespace o2::framework
6066
#endif // O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_

Framework/Core/include/Framework/DataRelayer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ class DataRelayer
114114

115115
using OnDropCallback = std::function<void(TimesliceSlot, std::vector<MessageSet>&, TimesliceIndex::OldestOutputInfo info)>;
116116

117+
// Callback for when some messages are about to be owned by the the DataRelayer
118+
using OnInsertionCallback = std::function<void(ServiceRegistryRef&, std::span<fair::mq::MessagePtr>&)>;
119+
117120
/// Prune all the pending entries in the cache.
118121
void prunePending(OnDropCallback);
119122
/// Prune the cache for a given slot
@@ -135,6 +138,7 @@ class DataRelayer
135138
InputInfo const& info,
136139
size_t nMessages,
137140
size_t nPayloads = 1,
141+
OnInsertionCallback onInsertion = nullptr,
138142
OnDropCallback onDrop = nullptr);
139143

140144
/// This is to set the oldest possible @a timeslice this relayer can

Framework/Core/src/DataProcessingDevice.cxx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot,
592592
O2_SIGNPOST_ID_GENERATE(sid, forwarding);
593593
O2_SIGNPOST_START(forwarding, sid, "forwardInputs", "Starting forwarding for slot %zu with oldestTimeslice %zu %{public}s%{public}s%{public}s",
594594
slot.index, oldestTimeslice.timeslice.value, copy ? "with copy" : "", copy && consume ? " and " : "", consume ? "with consume" : "");
595-
auto forwardedParts = DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copy, consume);
595+
auto forwardedParts = DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copy, consume);
596596

597597
for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) {
598598
if (forwardedParts[fi].Size() == 0) {
@@ -1854,11 +1854,56 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo&
18541854
VariableContextHelpers::getTimeslice(variables);
18551855
forwardInputs(ref, slot, dropped, oldestOutputInfo, false, true);
18561856
};
1857+
1858+
auto onInsertion = [](ServiceRegistryRef& ref, std::span<fair::mq::MessagePtr>& messages) {
1859+
O2_SIGNPOST_ID_GENERATE(sid, forwarding);
1860+
1861+
auto& spec = ref.get<DeviceSpec const>();
1862+
bool hasForwards = spec.forwards.empty() == false;
1863+
auto& context = ref.get<DataProcessorContext>();
1864+
if (context.canForwardEarly && hasForwards) {
1865+
O2_SIGNPOST_EVENT_EMIT(device, sid, "device", "Early forwardinding before injecting data into relayer.");
1866+
auto& timesliceIndex = ref.get<TimesliceIndex>();
1867+
auto oldestTimeslice = timesliceIndex.getOldestPossibleOutput();
1868+
1869+
auto& proxy = ref.get<FairMQDeviceProxy>();
1870+
1871+
O2_SIGNPOST_ID_GENERATE(sid, forwarding);
1872+
O2_SIGNPOST_START(forwarding, sid, "forwardInputs", "Starting forwarding for incoming messages with oldestTimeslice %zu with copy",
1873+
oldestTimeslice.timeslice.value);
1874+
std::vector<fair::mq::Parts> forwardedParts;
1875+
forwardedParts.resize(proxy.getNumForwards());
1876+
DataProcessingHelpers::routeForwardedMessages(proxy, messages, forwardedParts, true, false);
1877+
1878+
for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) {
1879+
if (forwardedParts[fi].Size() == 0) {
1880+
continue;
1881+
}
1882+
ForwardChannelInfo info = proxy.getForwardChannelInfo(ChannelIndex{fi});
1883+
auto& parts = forwardedParts[fi];
1884+
if (info.policy == nullptr) {
1885+
O2_SIGNPOST_EVENT_EMIT_ERROR(forwarding, sid, "forwardInputs", "Forwarding to %{public}s %d has no policy.", info.name.c_str(), fi);
1886+
continue;
1887+
}
1888+
O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding to %{public}s %d", info.name.c_str(), fi);
1889+
info.policy->forward(parts, ChannelIndex{fi}, ref);
1890+
}
1891+
auto& asyncQueue = ref.get<AsyncQueue>();
1892+
auto& decongestion = ref.get<DecongestionService>();
1893+
O2_SIGNPOST_ID_GENERATE(aid, async_queue);
1894+
O2_SIGNPOST_EVENT_EMIT(async_queue, aid, "forwardInputs", "Queuing forwarding oldestPossible %zu", oldestTimeslice.timeslice.value);
1895+
AsyncQueueHelpers::post(asyncQueue, AsyncTask{.timeslice = oldestTimeslice.timeslice, .id = decongestion.oldestPossibleTimesliceTask, .debounce = -1, .callback = decongestionCallbackLate}
1896+
.user<DecongestionContext>({.ref = ref, .oldestTimeslice = oldestTimeslice}));
1897+
O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Forwarding done");
1898+
}
1899+
};
1900+
18571901
auto relayed = relayer.relay(parts.At(headerIndex)->GetData(),
18581902
&parts.At(headerIndex),
18591903
input,
18601904
nMessages,
18611905
nPayloadsPerHeader,
1906+
onInsertion,
18621907
onDrop);
18631908
switch (relayed.type) {
18641909
case DataRelayer::RelayChoice::Type::Backpressured:
@@ -2273,9 +2318,10 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v
22732318
bool consumeSomething = action.op == CompletionPolicy::CompletionOp::Consume || action.op == CompletionPolicy::CompletionOp::ConsumeExisting;
22742319

22752320
if (context.canForwardEarly && hasForwards && consumeSomething) {
2276-
O2_SIGNPOST_EVENT_EMIT(device, aid, "device", "Early forwainding: %{public}s.", fmt::format("{}", action.op).c_str());
2277-
auto& timesliceIndex = ref.get<TimesliceIndex>();
2278-
forwardInputs(ref, action.slot, currentSetOfInputs, timesliceIndex.getOldestPossibleOutput(), true, action.op == CompletionPolicy::CompletionOp::Consume);
2321+
// We used to do fowarding here, however we now do it much earlier.
2322+
// We still need to clean the inputs which were already consumed
2323+
// via ConsumeExisting and which still have an header to hold the slot.
2324+
DataProcessingHelpers::cleanForwardedMessageSet(currentSetOfInputs);
22792325
}
22802326
markInputsAsDone(action.slot);
22812327

Framework/Core/src/DataProcessingHelpers.cxx

Lines changed: 139 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -221,104 +221,170 @@ TransitionHandlingState DataProcessingHelpers::updateStateTransition(ServiceRegi
221221
}
222222
}
223223

224-
auto DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy,
225-
std::vector<MessageSet>& currentSetOfInputs,
226-
const bool copyByDefault, bool consume) -> std::vector<fair::mq::Parts>
224+
void DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, std::span<fair::mq::MessagePtr>& messages, std::vector<fair::mq::Parts>& forwardedParts,
225+
const bool copyByDefault, bool consume)
226+
{
227+
O2_SIGNPOST_ID_GENERATE(sid, forwarding);
228+
std::vector<ChannelIndex> forwardingChoices{};
229+
size_t pi = 0;
230+
while (pi < messages.size()) {
231+
auto& header = messages[pi];
232+
233+
// If is now possible that the record is not complete when
234+
// we forward it, because of a custom completion policy.
235+
// this means that we need to skip the empty entries in the
236+
// record for being forwarded.
237+
if (header->GetData() == nullptr) {
238+
pi += 2;
239+
continue;
240+
}
241+
auto dih = o2::header::get<DomainInfoHeader*>(header->GetData());
242+
if (dih) {
243+
pi += 2;
244+
continue;
245+
}
246+
auto sih = o2::header::get<SourceInfoHeader*>(header->GetData());
247+
if (sih) {
248+
pi += 2;
249+
continue;
250+
}
251+
252+
auto dph = o2::header::get<DataProcessingHeader*>(header->GetData());
253+
auto dh = o2::header::get<o2::header::DataHeader*>(header->GetData());
254+
255+
if (dph == nullptr || dh == nullptr) {
256+
// Complain only if this is not an out-of-band message
257+
LOGP(error, "Data is missing {}{}{}",
258+
dph ? "DataProcessingHeader" : "", dph || dh ? "and" : "", dh ? "DataHeader" : "");
259+
pi += 2;
260+
continue;
261+
}
262+
263+
// At least one payload.
264+
auto& payload = messages[pi + 1];
265+
// Calculate the number of messages which should be handled together
266+
// all in one go.
267+
size_t numberOfMessages = 0;
268+
if (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) {
269+
// Sequence of (header, payload[0], ... , payload[splitPayloadParts - 1]) pairs belonging together.
270+
numberOfMessages = dh->splitPayloadParts;
271+
} else {
272+
// Sequence of splitPayloadParts (header, payload) pairs belonging together.
273+
// In case splitPayloadParts = 0, we consider this as a single message pair
274+
numberOfMessages = (dh->splitPayloadParts > 0 ? dh->splitPayloadParts : 1) * 2;
275+
}
276+
277+
if (payload.get() == nullptr && consume == true) {
278+
// If the payload is not there, it means we already
279+
// processed it with ConsumeExisiting. Therefore we
280+
// need to do something only if this is the last consume.
281+
header.reset(nullptr);
282+
pi += numberOfMessages;
283+
continue;
284+
}
285+
286+
// We need to find the forward route only for the first
287+
// part of a split payload. All the others will use the same.
288+
// Therefore, we reset and recompute the forwarding choice:
289+
//
290+
// - If this is the first payload of a [header0][payload0][header0][payload1]... sequence,
291+
// which is actually always created and handled together. Notice that in this
292+
// case we have splitPayloadParts == splitPayloadIndex
293+
// - If this is the first payload of a [header0][payload0][header1][payload1]... sequence
294+
// belonging to the same multipart message (and therefore we are guaranteed that they
295+
// need to be routed together).
296+
// - If the message is not a multipart (splitPayloadParts 0) or has only one part
297+
// - If it's a message of the kind [header0][payload1][payload2][payload3]... and therefore
298+
// we will already use the same choice in the for loop below.
299+
//
300+
301+
forwardingChoices.clear();
302+
proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime);
303+
304+
if (forwardingChoices.empty()) {
305+
// Nothing to forward go to the next messageset
306+
pi += numberOfMessages;
307+
continue;
308+
}
309+
310+
// In case of more than one forward route, we need to copy the message.
311+
// This will eventually use the same memory if running with the same backend.
312+
if (copyByDefault || forwardingChoices.size() > 1) {
313+
for (auto& choice : forwardingChoices) {
314+
O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding a copy of %{public}s to route %d.",
315+
fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), choice.value);
316+
317+
for (size_t ppi = pi; ppi < pi + numberOfMessages; ++ppi) {
318+
auto&& newMsg = header->GetTransport()->CreateMessage();
319+
newMsg->Copy(*messages[ppi]);
320+
forwardedParts[choice.value].AddPart(std::move(newMsg));
321+
}
322+
}
323+
} else {
324+
O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding %{public}s to route %d.",
325+
fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), forwardingChoices.back().value);
326+
for (size_t ppi = pi; ppi < pi + numberOfMessages; ++ppi) {
327+
forwardedParts[forwardingChoices.back().value].AddPart(std::move(messages[ppi]));
328+
}
329+
}
330+
pi += numberOfMessages;
331+
}
332+
}
333+
334+
auto DataProcessingHelpers::routeForwardedMessageSet(FairMQDeviceProxy& proxy,
335+
std::vector<MessageSet>& currentSetOfInputs,
336+
const bool copyByDefault, bool consume) -> std::vector<fair::mq::Parts>
227337
{
228338
// we collect all messages per forward in a map and send them together
229339
std::vector<fair::mq::Parts> forwardedParts;
230340
forwardedParts.resize(proxy.getNumForwards());
231341
std::vector<ChannelIndex> forwardingChoices{};
232-
O2_SIGNPOST_ID_GENERATE(sid, forwarding);
233342

234343
for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) {
235-
auto& messageSet = currentSetOfInputs[ii];
236-
237-
for (size_t pi = 0; pi < messageSet.size(); ++pi) {
238-
auto& header = messageSet.header(pi);
344+
auto span = std::span<fair::mq::MessagePtr>(currentSetOfInputs[ii].messages);
345+
routeForwardedMessages(proxy, span, forwardedParts, copyByDefault, consume);
346+
}
347+
return forwardedParts;
348+
}
239349

240-
// If is now possible that the record is not complete when
241-
// we forward it, because of a custom completion policy.
242-
// this means that we need to skip the empty entries in the
243-
// record for being forwarded.
244-
if (header->GetData() == nullptr) {
245-
continue;
246-
}
350+
void DataProcessingHelpers::cleanForwardedMessageSet(std::vector<MessageSet>& currentSetOfInputs)
351+
{
352+
for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) {
353+
auto messages = std::span<fair::mq::MessagePtr>(currentSetOfInputs[ii].messages);
354+
size_t pi = 0;
355+
while (pi < messages.size()) {
356+
auto& header = messages[pi];
247357
auto dih = o2::header::get<DomainInfoHeader*>(header->GetData());
248-
if (dih) {
249-
continue;
250-
}
251358
auto sih = o2::header::get<SourceInfoHeader*>(header->GetData());
252-
if (sih) {
253-
continue;
254-
}
255-
256359
auto dph = o2::header::get<DataProcessingHeader*>(header->GetData());
257360
auto dh = o2::header::get<o2::header::DataHeader*>(header->GetData());
258-
259-
if (dph == nullptr || dh == nullptr) {
260-
// Complain only if this is not an out-of-band message
261-
LOGP(error, "Data is missing {}{}{}",
262-
dph ? "DataProcessingHeader" : "", dph || dh ? "and" : "", dh ? "DataHeader" : "");
361+
if (header->GetData() == nullptr || sih || dih || dph == nullptr || dh == nullptr) {
362+
pi += 2;
263363
continue;
264364
}
265365

266-
auto& payload = messageSet.payload(pi);
366+
// At least one payload.
367+
auto& payload = messages[pi + 1];
267368

268-
if (payload.get() == nullptr && consume == true) {
369+
if (payload.get() == nullptr) {
269370
// If the payload is not there, it means we already
270371
// processed it with ConsumeExisiting. Therefore we
271372
// need to do something only if this is the last consume.
272373
header.reset(nullptr);
273-
continue;
274374
}
275375

276-
// We need to find the forward route only for the first
277-
// part of a split payload. All the others will use the same.
278-
// Therefore, we reset and recompute the forwarding choice:
279-
//
280-
// - If this is the first payload of a [header0][payload0][header0][payload1] sequence,
281-
// which is actually always created and handled together
282-
// - If the message is not a multipart (splitPayloadParts 0) or has only one part
283-
// - If it's a message of the kind [header0][payload1][payload2][payload3]... and therefore
284-
// we will already use the same choice in the for loop below.
285-
if (dh->splitPayloadIndex == 0 || dh->splitPayloadParts <= 1 || messageSet.getNumberOfPayloads(pi) > 0) {
286-
forwardingChoices.clear();
287-
proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime);
288-
}
289-
290-
if (forwardingChoices.empty()) {
291-
// Nothing to forward go to the next messageset
292-
continue;
293-
}
294-
295-
// In case of more than one forward route, we need to copy the message.
296-
// This will eventually use the same memory if running with the same backend.
297-
if (copyByDefault || forwardingChoices.size() > 1) {
298-
for (auto& choice : forwardingChoices) {
299-
auto&& newHeader = header->GetTransport()->CreateMessage();
300-
O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding a copy of %{public}s to route %d.",
301-
fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), choice.value);
302-
newHeader->Copy(*header);
303-
forwardedParts[choice.value].AddPart(std::move(newHeader));
304-
305-
for (size_t payloadIndex = 0; payloadIndex < messageSet.getNumberOfPayloads(pi); ++payloadIndex) {
306-
auto&& newPayload = header->GetTransport()->CreateMessage();
307-
newPayload->Copy(*messageSet.payload(pi, payloadIndex));
308-
forwardedParts[choice.value].AddPart(std::move(newPayload));
309-
}
310-
}
376+
// Calculate the number of messages which should be handled together
377+
// all in one go.
378+
if (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) {
379+
// Sequence of (header, payload[0], ... , payload[splitPayloadParts - 1]) pairs belonging together.
380+
pi += dh->splitPayloadParts;
311381
} else {
312-
O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding %{public}s to route %d.",
313-
fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), forwardingChoices.back().value);
314-
forwardedParts[forwardingChoices.back().value].AddPart(std::move(messageSet.header(pi)));
315-
for (size_t payloadIndex = 0; payloadIndex < messageSet.getNumberOfPayloads(pi); ++payloadIndex) {
316-
forwardedParts[forwardingChoices.back().value].AddPart(std::move(messageSet.payload(pi, payloadIndex)));
317-
}
382+
// Sequence of splitPayloadParts (header, payload) pairs belonging together.
383+
// In case splitPayloadParts = 0, we consider this as a single message pair
384+
pi += (dh->splitPayloadParts > 0 ? dh->splitPayloadParts : 1) * 2;
318385
}
319386
}
320387
}
321-
return forwardedParts;
322-
};
388+
}
323389

324390
} // namespace o2::framework

0 commit comments

Comments
 (0)