Skip to content

Commit 5b27406

Browse files
ktfehellbar
authored andcommitted
DPL: refactor InputSpan
Now that navigating the MessageSet does not have an index anymore it does not make sense to use a random accessor to implement the InputSpan::Iterator, because that results in an O(N^2) loop for normal linear iterations. It is much better to simply implement a proper advancement operator, so that looping stays O(N). This resumes the performance when iterating on old-style multiparts which are apparently still used in some cases. General modernization of InputSpan also done.
1 parent 4b43e40 commit 5b27406

17 files changed

+253
-172
lines changed

Framework/Core/include/Framework/DataModelViews.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "DomainInfoHeader.h"
1717
#include "SourceInfoHeader.h"
1818
#include "Headers/DataHeader.h"
19+
#include "Framework/DataRef.h"
1920
#include "Framework/TimesliceSlot.h"
2021
#include <ranges>
2122
#include <span>
@@ -80,10 +81,7 @@ struct count_parts {
8081
}
8182
};
8283

83-
struct DataRefIndices {
84-
size_t headerIdx;
85-
size_t payloadIdx;
86-
};
84+
// DataRefIndices is defined in Framework/DataRef.h
8785

8886
struct get_pair {
8987
size_t pairId;

Framework/Core/include/Framework/DataRef.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define FRAMEWORK_DATAREF_H
1313

1414
#include <cstddef> // for size_t
15+
#include <compare>
1516

1617
namespace o2
1718
{
@@ -29,6 +30,15 @@ struct DataRef {
2930
size_t payloadSize = 0;
3031
};
3132

33+
/// Raw indices into the message vector for one (header, payload) pair.
34+
/// Kept in a lightweight header so InputSpan can use it without pulling in FairMQ.
35+
struct DataRefIndices {
36+
size_t headerIdx;
37+
size_t payloadIdx;
38+
bool operator==(const DataRefIndices&) const = default;
39+
auto operator<=>(const DataRefIndices&) const = default;
40+
};
41+
3242
} // namespace framework
3343
} // namespace o2
3444

Framework/Core/include/Framework/InputRecord.h

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "Framework/DataRef.h"
1515
#include "Framework/DataRefUtils.h"
16+
#include "Framework/InputSpan.h"
1617
#include "Framework/InputRoute.h"
1718
#include "Framework/TypeTraits.h"
1819
#include "Framework/TableConsumer.h"
@@ -202,6 +203,15 @@ class InputRecord
202203

203204
[[nodiscard]] size_t getNofParts(int pos) const;
204205

206+
/// O(1) access to the part described by @a indices in slot @a pos.
207+
[[nodiscard]] DataRef getAtIndices(int pos, DataRefIndices indices) const;
208+
209+
/// O(1) advance from @a current to the next part's indices in slot @a pos.
210+
[[nodiscard]] DataRefIndices nextIndices(int pos, DataRefIndices current) const
211+
{
212+
return mSpan.nextIndices(pos, current);
213+
}
214+
205215
// Given a binding by string, return the associated DataRef
206216
DataRef getDataRefByString(const char* bindingName, int part = 0) const
207217
{
@@ -568,8 +578,8 @@ class InputRecord
568578

569579
Iterator() = delete;
570580

571-
Iterator(ParentType const* parent, size_t position = 0, size_t size = 0)
572-
: mPosition(position), mSize(size > position ? size : position), mParent(parent), mElement{nullptr, nullptr, nullptr}
581+
Iterator(ParentType const* parent, bool isEnd = false)
582+
: mPosition(isEnd ? parent->size() : 0), mSize(parent->size()), mParent(parent), mElement{nullptr, nullptr, nullptr}
573583
{
574584
if (mPosition < mSize) {
575585
if (mParent->isValid(mPosition)) {
@@ -678,18 +688,29 @@ class InputRecord
678688
using reference = typename BaseType::reference;
679689
using pointer = typename BaseType::pointer;
680690
using ElementType = typename std::remove_const<value_type>::type;
681-
using iterator = Iterator<SelfType, T>;
682-
using const_iterator = Iterator<SelfType, const T>;
691+
using iterator = InputSpan::Iterator<SelfType, T>;
692+
using const_iterator = InputSpan::Iterator<SelfType, const T>;
693+
694+
InputRecordIterator(InputRecord const* parent, bool isEnd = false)
695+
: BaseType(parent, isEnd)
696+
{
697+
}
698+
699+
/// Initial indices for part-level iteration: first part starts at {headerIdx=0, payloadIdx=1}.
700+
[[nodiscard]] DataRefIndices initialIndices() const { return {0, 1}; }
701+
/// Sentinel used by nextIndicesGetter to signal end-of-slot.
702+
[[nodiscard]] DataRefIndices endIndices() const { return {size_t(-1), size_t(-1)}; }
683703

684-
InputRecordIterator(InputRecord const* parent, size_t position = 0, size_t size = 0)
685-
: BaseType(parent, position, size)
704+
/// Get element at the given raw message indices in O(1).
705+
[[nodiscard]] ElementType getAtIndices(DataRefIndices indices) const
686706
{
707+
return this->parent()->getAtIndices(this->position(), indices);
687708
}
688709

689-
/// Get element at {slotindex, partindex}
690-
[[nodiscard]] ElementType getByPos(size_t pos) const
710+
/// Advance @a current to the next part's indices in O(1).
711+
[[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const
691712
{
692-
return this->parent()->getByPos(this->position(), pos);
713+
return this->parent()->nextIndices(this->position(), current);
693714
}
694715

695716
/// Check if slot is valid, index of part is not used
@@ -709,12 +730,12 @@ class InputRecord
709730

710731
[[nodiscard]] const_iterator begin() const
711732
{
712-
return const_iterator(this, 0, size());
733+
return const_iterator(this, size() == 0);
713734
}
714735

715736
[[nodiscard]] const_iterator end() const
716737
{
717-
return const_iterator(this, size());
738+
return const_iterator(this, true);
718739
}
719740
};
720741

@@ -723,12 +744,12 @@ class InputRecord
723744

724745
[[nodiscard]] const_iterator begin() const
725746
{
726-
return {this, 0, size()};
747+
return {this, false};
727748
}
728749

729750
[[nodiscard]] const_iterator end() const
730751
{
731-
return {this, size()};
752+
return {this, true};
732753
}
733754

734755
InputSpan& span()

Framework/Core/include/Framework/InputSpan.h

Lines changed: 70 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
#include "Framework/DataRef.h"
1515
#include <functional>
1616

17-
extern template class std::function<o2::framework::DataRef(size_t)>;
18-
extern template class std::function<o2::framework::DataRef(size_t, size_t)>;
17+
extern template class std::function<o2::framework::DataRef(size_t, o2::framework::DataRefIndices)>;
18+
extern template class std::function<o2::framework::DataRefIndices(size_t, o2::framework::DataRefIndices)>;
1919

2020
namespace o2::framework
2121
{
@@ -32,37 +32,48 @@ class InputSpan
3232
InputSpan(InputSpan const&) = delete;
3333
InputSpan(InputSpan&&) = default;
3434

35-
/// @a getter is the mapping between an element of the span referred by
36-
/// index and the buffer associated.
37-
/// @a size is the number of elements in the span.
38-
InputSpan(std::function<DataRef(size_t)> getter, size_t size);
35+
/// Navigate the message store via the DataRefIndices protocol.
36+
/// get_next_pair (DataModelViews.h) provides O(1) sequential advancement for nextIndicesGetter.
37+
InputSpan(std::function<size_t(size_t)> nofPartsGetter,
38+
std::function<int(size_t)> refCountGetter,
39+
std::function<DataRef(size_t, DataRefIndices)> indicesGetter,
40+
std::function<DataRefIndices(size_t, DataRefIndices)> nextIndicesGetter,
41+
size_t size);
3942

40-
/// @a getter is the mapping between an element of the span referred by
41-
/// index and the buffer associated.
42-
/// @a size is the number of elements in the span.
43-
InputSpan(std::function<DataRef(size_t, size_t)> getter, size_t size);
43+
/// @a i-th element of the InputSpan (O(partidx) sequential scan via indices protocol)
44+
[[nodiscard]] DataRef get(size_t i, size_t partidx = 0) const
45+
{
46+
DataRefIndices idx{0, 1};
47+
for (size_t p = 0; p < partidx; ++p) {
48+
idx = mNextIndicesGetter(i, idx);
49+
}
50+
return mIndicesGetter(i, idx);
51+
}
4452

45-
/// @a getter is the mapping between an element of the span referred by
46-
/// index and the buffer associated.
47-
/// @nofPartsGetter is the getter for the number of parts associated with an index
48-
/// @a size is the number of elements in the span.
49-
InputSpan(std::function<DataRef(size_t, size_t)> getter, std::function<size_t(size_t)> nofPartsGetter, std::function<int(size_t)> refCountGetter, size_t size);
53+
/// Return the DataRef for the part described by @a indices in slot @a slotIdx in O(1).
54+
[[nodiscard]] DataRef getAtIndices(size_t slotIdx, DataRefIndices indices) const
55+
{
56+
return mIndicesGetter(slotIdx, indices);
57+
}
5058

51-
/// @a i-th element of the InputSpan
52-
[[nodiscard]] DataRef get(size_t i, size_t partidx = 0) const
59+
/// Advance from @a current to the indices of the next part in slot @a slotIdx in O(1).
60+
[[nodiscard]] DataRefIndices nextIndices(size_t slotIdx, DataRefIndices current) const
5361
{
54-
return mGetter(i, partidx);
62+
return mNextIndicesGetter(slotIdx, current);
5563
}
5664

65+
// --- slot-level Iterator protocol (headerIdx doubles as slot position) ---
66+
[[nodiscard]] DataRefIndices initialIndices() const { return {0, 0}; }
67+
[[nodiscard]] DataRefIndices endIndices() const { return {mSize, 0}; }
68+
[[nodiscard]] DataRef getAtIndices(DataRefIndices indices) const { return mIndicesGetter(indices.headerIdx, {0, 1}); }
69+
[[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { return {current.headerIdx + 1, 0}; }
70+
5771
/// @a number of parts in the i-th element of the InputSpan
5872
[[nodiscard]] size_t getNofParts(size_t i) const
5973
{
6074
if (i >= mSize) {
6175
return 0;
6276
}
63-
if (!mNofPartsGetter) {
64-
return 1;
65-
}
6677
return mNofPartsGetter(i);
6778
}
6879

@@ -94,7 +105,8 @@ class InputSpan
94105
return get(i).payload;
95106
}
96107

97-
/// an iterator class working on position within the a parent class
108+
/// An iterator over the elements of a parent container using the DataRefIndices protocol.
109+
/// ParentT must provide: initialIndices(), getAtIndices(DataRefIndices), nextIndices(DataRefIndices).
98110
template <typename ParentT, typename T>
99111
class Iterator
100112
{
@@ -110,23 +122,23 @@ class InputSpan
110122

111123
Iterator() = delete;
112124

113-
Iterator(ParentType const* parent, size_t position = 0, size_t size = 0)
114-
: mPosition(position), mSize(size > position ? size : position), mParent(parent), mElement{}
125+
Iterator(ParentType const* parent, bool isEnd = false)
126+
: mParent(parent),
127+
mCurrentIndices(isEnd ? parent->endIndices() : parent->initialIndices()),
128+
mElement{}
115129
{
116-
if (mPosition < mSize) {
117-
mElement = mParent->get(mPosition);
130+
if (mCurrentIndices != mParent->endIndices()) {
131+
mElement = mParent->getAtIndices(mCurrentIndices);
118132
}
119133
}
120134

121-
~Iterator() = default;
122-
123135
// prefix increment
124136
SelfType& operator++()
125137
{
126-
if (mPosition < mSize && ++mPosition < mSize) {
127-
mElement = mParent->get(mPosition);
138+
mCurrentIndices = mParent->nextIndices(mCurrentIndices);
139+
if (mCurrentIndices != mParent->endIndices()) {
140+
mElement = mParent->getAtIndices(mCurrentIndices);
128141
} else {
129-
// reset the element to the default value of the type
130142
mElement = ElementType{};
131143
}
132144
return *this;
@@ -145,16 +157,14 @@ class InputSpan
145157
return mElement;
146158
}
147159

148-
// comparison
149160
bool operator==(const SelfType& rh) const
150161
{
151-
return mPosition == rh.mPosition;
162+
return mCurrentIndices == rh.mCurrentIndices;
152163
}
153164

154-
// comparison
155-
bool operator!=(const SelfType& rh) const
165+
auto operator<=>(const SelfType& rh) const
156166
{
157-
return mPosition != rh.mPosition;
167+
return mCurrentIndices <=> rh.mCurrentIndices;
158168
}
159169

160170
// return pointer to parent instance
@@ -163,22 +173,21 @@ class InputSpan
163173
return mParent;
164174
}
165175

166-
// return current position
176+
// return current position (headerIdx serves as the slot index for slot-level iteration)
167177
[[nodiscard]] size_t position() const
168178
{
169-
return mPosition;
179+
return mCurrentIndices.headerIdx;
170180
}
171181

172182
private:
173-
size_t mPosition;
174-
size_t mSize;
175183
ParentType const* mParent;
184+
DataRefIndices mCurrentIndices;
176185
ElementType mElement;
177186
};
178187

179188
/// @class InputSpanIterator
180-
/// An iterator over the input slots
181-
/// It supports an iterator interface to access the parts in the slot
189+
/// An iterator over the input slots.
190+
/// It supports an iterator interface to access the parts in the slot.
182191
template <typename T>
183192
class InputSpanIterator : public Iterator<InputSpan, T>
184193
{
@@ -192,24 +201,26 @@ class InputSpan
192201
using iterator = Iterator<SelfType, T>;
193202
using const_iterator = Iterator<SelfType, const T>;
194203

195-
InputSpanIterator(InputSpan const* parent, size_t position = 0, size_t size = 0)
196-
: BaseType(parent, position, size)
204+
InputSpanIterator(InputSpan const* parent, bool isEnd = false)
205+
: BaseType(parent, isEnd)
197206
{
198207
}
199208

200-
/// Get element at {slotindex, partindex}
201-
[[nodiscard]] ElementType get(size_t pos) const
209+
/// Initial indices for part-level iteration: first part starts at {headerIdx=0, payloadIdx=1}.
210+
[[nodiscard]] DataRefIndices initialIndices() const { return {0, 1}; }
211+
/// Sentinel used by nextIndicesGetter to signal end-of-slot.
212+
[[nodiscard]] DataRefIndices endIndices() const { return {size_t(-1), size_t(-1)}; }
213+
214+
/// Get element at the given raw message indices in O(1).
215+
[[nodiscard]] ElementType getAtIndices(DataRefIndices indices) const
202216
{
203-
return this->parent()->get(this->position(), pos);
217+
return this->parent()->getAtIndices(this->position(), indices);
204218
}
205219

206-
/// Check if slot is valid, index of part is not used
207-
[[nodiscard]] bool isValid(size_t = 0) const
220+
/// Advance @a current to the next part's indices in O(1).
221+
[[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const
208222
{
209-
if (this->position() < this->parent()->size()) {
210-
return this->parent()->isValid(this->position());
211-
}
212-
return false;
223+
return this->parent()->nextIndices(this->position(), current);
213224
}
214225

215226
/// Get number of parts in input slot
@@ -218,15 +229,14 @@ class InputSpan
218229
return this->parent()->getNofParts(this->position());
219230
}
220231

221-
// iterator for the part access
222232
[[nodiscard]] const_iterator begin() const
223233
{
224-
return const_iterator(this, 0, size());
234+
return const_iterator(this, size() == 0);
225235
}
226236

227237
[[nodiscard]] const_iterator end() const
228238
{
229-
return const_iterator(this, size());
239+
return const_iterator(this, true);
230240
}
231241
};
232242

@@ -236,19 +246,19 @@ class InputSpan
236246
// supporting read-only access and returning const_iterator
237247
[[nodiscard]] const_iterator begin() const
238248
{
239-
return {this, 0, size()};
249+
return {this, false};
240250
}
241251

242-
// supporting read-only access and returning const_iterator
243252
[[nodiscard]] const_iterator end() const
244253
{
245-
return {this, size()};
254+
return {this, true};
246255
}
247256

248257
private:
249-
std::function<DataRef(size_t, size_t)> mGetter;
250258
std::function<size_t(size_t)> mNofPartsGetter;
251259
std::function<int(size_t)> mRefCountGetter;
260+
std::function<DataRef(size_t, DataRefIndices)> mIndicesGetter;
261+
std::function<DataRefIndices(size_t, DataRefIndices)> mNextIndicesGetter;
252262
size_t mSize;
253263
};
254264

0 commit comments

Comments
 (0)