Project
Loading...
Searching...
No Matches
ArrowTableSlicingCache.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.
11
15
16#include <arrow/compute/api_aggregate.h>
17#include <arrow/compute/kernel.h>
18#include <arrow/table.h>
19
20namespace o2::framework
21{
22
23namespace
24{
25// ASCII-only lowercase. Column labels are plain identifiers, so we deliberately
26// avoid the locale-aware std::tolower: it goes through the C locale facet on
27// every character and dominated getIndexFromLabel in profiles.
28constexpr inline char asciiToLower(char c)
29{
30 return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c;
31}
32
33arrow::ChunkedArray* getIndexFromLabel(arrow::Table* table, std::string_view label)
34{
35 auto field = std::ranges::find_if(table->schema()->fields(), [label](std::shared_ptr<arrow::Field> const& field) {
36 std::string_view name = field->name();
37 return name == label ||
38 std::ranges::equal(label, name, [](char c1, char c2) {
39 return asciiToLower(c1) == asciiToLower(c2);
40 });
41 });
42 if (field == table->schema()->fields().end()) {
43 throw runtime_error_f("Unable to find column with label %s.", label);
44 }
45 return table->column(std::distance(table->schema()->fields().begin(), field)).get();
46}
47} // namespace
48
50{
51 auto locate = std::find(list.begin(), list.end(), entry);
52 if (locate == list.end()) {
53 list.emplace_back(entry);
54 } else if (!locate->enabled && entry.enabled) {
55 locate->enabled = true;
56 }
57}
58
59std::pair<int64_t, int64_t> SliceInfoPtr::getSliceFor(int value) const
60{
61 if ((size_t)value >= offsets.size()) {
62 return {0, 0};
63 }
64
65 return {offsets[value], sizes[value]};
66}
67
68std::span<const int64_t> SliceInfoUnsortedPtr::getSliceFor(int value) const
69{
70 if (values.empty()) {
71 return {};
72 }
73 if (value > values[values.size() - 1]) {
74 return {};
75 }
76
77 return {(*groups)[value].data(), (*groups)[value].size()};
78}
79
80void ArrowTableSlicingCacheDef::setCaches(Cache&& bsks)
81{
82 bindingsKeys = bsks;
83}
84
85void ArrowTableSlicingCacheDef::setCachesUnsorted(Cache&& bsks)
86{
87 bindingsKeysUnsorted = bsks;
88}
89
90ArrowTableSlicingCache::ArrowTableSlicingCache(Cache&& bsks, Cache&& bsksUnsorted, header::DataOrigin newOrigin_)
91 : bindingsKeys{bsks},
92 bindingsKeysUnsorted{bsksUnsorted},
93 newOrigin{newOrigin_}
94{
95 offsets.resize(bindingsKeys.size());
96 sizes.resize(bindingsKeys.size());
97
99 groups.resize(bindingsKeysUnsorted.size());
100}
101
103{
104 bindingsKeys = bsks;
105 bindingsKeysUnsorted = bsksUnsorted;
106 offsets.clear();
107 offsets.resize(bindingsKeys.size());
108 sizes.clear();
109 sizes.resize(bindingsKeys.size());
110 valuesUnsorted.clear();
112 groups.clear();
113 groups.resize(bindingsKeysUnsorted.size());
114}
115
116arrow::Status ArrowTableSlicingCache::updateCacheEntry(int pos, std::shared_ptr<arrow::Table> const& table)
117{
118 offsets[pos].clear();
119 sizes[pos].clear();
120 if (table->num_rows() == 0) {
121 return arrow::Status::OK();
122 }
123 auto& [b, m, k, e] = bindingsKeys[pos];
124 if (!e) {
125 throw runtime_error_f("Disabled cache (%s) %s/%s update requested", DataSpecUtils::describe(m).c_str(), b.c_str(), k.c_str());
126 }
128
129 int maxValue = -1;
130 auto column = getIndexFromLabel(table.get(), k);
131
132 // starting from the end, find the first positive value, in a sorted column it is the largest index
133 for (auto iChunk = column->num_chunks() - 1; iChunk >= 0; --iChunk) {
134 auto chunk = static_cast<arrow::NumericArray<arrow::Int32Type>>(column->chunk(iChunk)->data());
135 for (auto iElement = chunk.length() - 1; iElement >= 0; --iElement) {
136 auto value = chunk.Value(iElement);
137 if (value < 0) {
138 continue;
139 } else {
140 maxValue = value;
141 break;
142 }
143 }
144 if (maxValue >= 0) {
145 break;
146 }
147 }
148
149 offsets[pos].resize(maxValue + 1);
150 sizes[pos].resize(maxValue + 1);
151
152 // loop over the index and collect size/offset
153 int lastValue = std::numeric_limits<int>::max();
154 int globalRow = 0;
155 for (auto iChunk = 0; iChunk < column->num_chunks(); ++iChunk) {
156 auto chunk = static_cast<arrow::NumericArray<arrow::Int32Type>>(column->chunk(iChunk)->data());
157 for (auto iElement = 0; iElement < chunk.length(); ++iElement) {
158 auto v = chunk.Value(iElement);
159 if (v >= 0) {
160 if (v == lastValue) {
161 ++sizes[pos][v];
162 } else {
163 lastValue = v;
164 ++sizes[pos][v];
165 offsets[pos][v] = globalRow;
166 }
167 }
168 ++globalRow;
169 }
170 }
171
172 return arrow::Status::OK();
173}
174
175arrow::Status ArrowTableSlicingCache::updateCacheEntryUnsorted(int pos, std::shared_ptr<arrow::Table> const& table)
176{
177 valuesUnsorted[pos].clear();
178 groups[pos].clear();
179 if (table->num_rows() == 0) {
180 return arrow::Status::OK();
181 }
182 auto& [b, m, k, e] = bindingsKeysUnsorted[pos];
183 if (!e) {
184 throw runtime_error_f("Disabled unsorted cache %s/%s update requested", b.c_str(), k.c_str());
185 }
186 auto column = getIndexFromLabel(table.get(), k);
187 auto row = 0;
188 for (auto iChunk = 0; iChunk < column->num_chunks(); ++iChunk) {
189 auto chunk = static_cast<arrow::NumericArray<arrow::Int32Type>>(column->chunk(iChunk)->data());
190 for (auto iElement = 0; iElement < chunk.length(); ++iElement) {
191 auto v = chunk.Value(iElement);
192 if (v >= 0) {
193 if (std::find(valuesUnsorted[pos].begin(), valuesUnsorted[pos].end(), v) == valuesUnsorted[pos].end()) {
194 valuesUnsorted[pos].push_back(v);
195 }
196 if ((int)groups[pos].size() <= v) {
197 groups[pos].resize(v + 1);
198 }
199 (groups[pos])[v].push_back(row);
200 }
201 ++row;
202 }
203 }
204 std::sort(valuesUnsorted[pos].begin(), valuesUnsorted[pos].end());
205 return arrow::Status::OK();
206}
207
208std::pair<int, bool> ArrowTableSlicingCache::getCachePos(const Entry& bindingKey) const
209{
210 auto pos = getCachePosSortedFor(bindingKey);
211 if (pos != -1) {
212 return {pos, true};
213 }
214 pos = getCachePosUnsortedFor(bindingKey);
215 if (pos != -1) {
216 return {pos, false};
217 }
218 throw runtime_error_f("(%s) %s/%s not found neither in sorted or unsorted cache", DataSpecUtils::describe(bindingKey.matcher).c_str(), bindingKey.binding.c_str(), bindingKey.key.c_str());
219}
220
222{
223 auto locate = std::ranges::find(bindingsKeys, bindingKey);
224 if (locate != bindingsKeys.end()) {
225 return std::distance(bindingsKeys.begin(), locate);
226 }
227 return -1;
228}
229
231{
232 auto locate_unsorted = std::ranges::find(bindingsKeysUnsorted, bindingKey);
233 if (locate_unsorted != bindingsKeysUnsorted.end()) {
234 return std::distance(bindingsKeysUnsorted.begin(), locate_unsorted);
235 }
236 return -1;
237}
239{
240 auto [p, s] = getCachePos(bindingKey);
241 if (!s) {
242 throw runtime_error_f("%s/%s is found in unsorted cache", bindingKey.binding.c_str(), bindingKey.key.c_str());
243 }
244 if (!bindingsKeys[p].enabled) {
245 throw runtime_error_f("Disabled cache %s/%s is requested", bindingKey.binding.c_str(), bindingKey.key.c_str());
246 }
247
248 return getCacheForPos(p);
249}
250
252{
253 auto [p, s] = getCachePos(bindingKey);
254 if (s) {
255 throw runtime_error_f("(%s) %s/%s is found in sorted cache", DataSpecUtils::describe(bindingKey.matcher).c_str(), bindingKey.binding.c_str(), bindingKey.key.c_str());
256 }
258 throw runtime_error_f("Disabled unsorted cache (%s) %s/%s is requested", DataSpecUtils::describe(bindingKey.matcher).c_str(), bindingKey.binding.c_str(), bindingKey.key.c_str());
259 }
260
261 return getCacheUnsortedForPos(p);
262}
263
265{
266 return {
267 gsl::span{offsets[pos].data(), offsets[pos].size()}, //
268 gsl::span(sizes[pos].data(), sizes[pos].size()) //
269 };
270}
271
273{
274 return {
275 {reinterpret_cast<int const*>(valuesUnsorted[pos].data()), valuesUnsorted[pos].size()},
276 &(groups[pos]) //
277 };
278}
279
280std::shared_ptr<arrow::Table> ArrowTableSlicingCache::getEmptySliceFor(std::shared_ptr<arrow::Table> const& table)
281{
282 if (emptySlice.first != table.get()) {
283 emptySlice = {table.get(), table->Slice(0, 0)};
284 }
285 return emptySlice.second;
286}
287
288void ArrowTableSlicingCache::validateOrder(Entry const& bindingKey, const std::shared_ptr<arrow::Table>& input)
289{
290 auto const& [target, matcher, key, enabled] = bindingKey;
291 if (!enabled) {
292 return;
293 }
294 auto column = getIndexFromLabel(input.get(), key);
295 auto array = static_cast<arrow::NumericArray<arrow::Int32Type>>(column->chunk(0)->data());
296 int32_t cur = array.Value(0);
297 int32_t lastNeg = cur < 0 ? cur : 0;
298 int32_t lastPos = cur < 0 ? -1 : cur;
299 for (auto i = 0; i < column->num_chunks(); ++i) {
300 array = static_cast<arrow::NumericArray<arrow::Int32Type>>(column->chunk(i)->data());
301 for (auto e = 0; e < array.length(); ++e) {
302 int32_t prev = cur;
303 if (prev >= 0) {
304 lastPos = prev;
305 } else {
306 lastNeg = prev;
307 }
308 cur = array.Value(e);
309 if (cur >= 0) {
310 if (lastPos > cur) {
311 throw runtime_error_f("Table %s index %s is not sorted: next value %d < previous value %d!", target.c_str(), key.c_str(), cur, lastPos);
312 }
313 if (lastPos == cur && prev < 0) {
314 throw runtime_error_f("Table %s index %s has a group with index %d that is split by %d", target.c_str(), key.c_str(), cur, prev);
315 }
316 } else {
317 if (lastNeg < cur) {
318 throw runtime_error_f("Table %s index %s is not sorted: next negative value %d > previous negative value %d!", target.c_str(), key.c_str(), cur, lastNeg);
319 }
320 if (lastNeg == cur && prev >= 0) {
321 throw runtime_error_f("Table %s index %s has a group with index %d that is split by %d", target.c_str(), key.c_str(), cur, prev);
322 }
323 }
324 }
325 }
326}
327} // namespace o2::framework
int32_t i
uint16_t pos
Definition RawData.h:3
uint32_t c
Definition RawData.h:2
StringRef key
const GLfloat * m
Definition glcorearb.h:4066
GLsizei GLuint * groups
Definition glcorearb.h:3984
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLsizei const GLuint const GLintptr * offsets
Definition glcorearb.h:2595
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
Definition glcorearb.h:2595
GLenum array
Definition glcorearb.h:4274
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition glcorearb.h:2513
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum target
Definition glcorearb.h:1641
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
void updatePairList(Cache &list, Entry &entry)
std::vector< Entry > Cache
RuntimeErrorRef runtime_error_f(const char *,...)
constexpr char asciiToLower(char c)
Definition ASoA.h:71
arrow::ChunkedArray * getIndexFromLabel(arrow::Table *table, std::string_view label)
Definition ASoA.cxx:210
SliceInfoUnsortedPtr getCacheUnsortedFor(Entry const &bindingKey) const
arrow::Status updateCacheEntryUnsorted(int pos, std::shared_ptr< arrow::Table > const &table)
int getCachePosSortedFor(Entry const &bindingKey) const
arrow::Status updateCacheEntry(int pos, std::shared_ptr< arrow::Table > const &table)
std::pair< int, bool > getCachePos(Entry const &bindingKey) const
SliceInfoPtr getCacheFor(Entry const &bindingKey) const
void setCaches(Cache &&bsks, Cache &&bsksUnsorted={})
std::pair< arrow::Table const *, std::shared_ptr< arrow::Table > > emptySlice
SliceInfoUnsortedPtr getCacheUnsortedForPos(int pos) const
std::shared_ptr< arrow::Table > getEmptySliceFor(std::shared_ptr< arrow::Table > const &table)
int getCachePosUnsortedFor(Entry const &bindingKey) const
std::vector< std::vector< int > > valuesUnsorted
static void validateOrder(Entry const &bindingKey, std::shared_ptr< arrow::Table > const &input)
static std::string describe(InputSpec const &spec)
ConcreteDataMatcher matcher
std::vector< int > row