@@ -81,104 +81,119 @@ std::vector<QueryExecutionTree*> Bind::getChildren() {
81
81
}
82
82
83
83
// _____________________________________________________________________________
84
- ProtoResult Bind::computeResult ([[maybe_unused]] bool requestLaziness) {
85
- using std::endl;
86
- LOG (DEBUG) << " Get input to BIND operation..." << endl;
87
- std::shared_ptr<const Result> subRes = _subtree->getResult ();
88
- LOG (DEBUG) << " Got input to Bind operation." << endl;
89
- IdTable idTable{getExecutionContext ()->getAllocator ()};
90
-
91
- idTable.setNumColumns (getResultWidth ());
92
-
93
- // Make a deep copy of the local vocab from `subRes` and then add to it (in
94
- // case BIND adds a new word or words).
95
- //
96
- // TODO: In most BIND operations, nothing is added to the local vocabulary, so
97
- // it would be more efficient to first share the pointer here (like with
98
- // `shareLocalVocabFrom`) and only copy it when a new word is about to be
99
- // added. Same for GROUP BY.
100
- auto localVocab = subRes->getCopyOfLocalVocab ();
101
-
102
- size_t inwidth = subRes->idTable ().numColumns ();
103
- size_t outwidth = getResultWidth ();
104
-
105
- CALL_FIXED_SIZE ((std::array{inwidth, outwidth}), &Bind::computeExpressionBind,
106
- this , &idTable, &localVocab, *subRes,
107
- _bind._expression .getPimpl ());
108
-
109
- LOG (DEBUG) << " BIND result computation done." << endl;
110
- return {std::move (idTable), resultSortedOn (), std::move (localVocab)};
84
+ IdTable Bind::cloneSubView (const IdTable& idTable,
85
+ const std::pair<size_t , size_t >& subrange) {
86
+ IdTable result (idTable.numColumns (), idTable.getAllocator ());
87
+ result.resize (subrange.second - subrange.first );
88
+ std::ranges::copy (idTable.begin () + subrange.first ,
89
+ idTable.begin () + subrange.second , result.begin ());
90
+ return result;
111
91
}
112
92
113
93
// _____________________________________________________________________________
114
- template <size_t IN_WIDTH, size_t OUT_WIDTH>
115
- void Bind::computeExpressionBind (
116
- IdTable* outputIdTable, LocalVocab* outputLocalVocab,
117
- const Result& inputResultTable,
118
- sparqlExpression::SparqlExpression* expression) const {
94
+ ProtoResult Bind::computeResult (bool requestLaziness) {
95
+ LOG (DEBUG) << " Get input to BIND operation..." << std::endl;
96
+ std::shared_ptr<const Result> subRes = _subtree->getResult (requestLaziness);
97
+ LOG (DEBUG) << " Got input to Bind operation." << std::endl;
98
+
99
+ auto applyBind = [this , subRes](IdTable idTable, LocalVocab* localVocab) {
100
+ return computeExpressionBind (localVocab, std::move (idTable),
101
+ subRes->localVocab (),
102
+ _bind._expression .getPimpl ());
103
+ };
104
+
105
+ if (subRes->isFullyMaterialized ()) {
106
+ if (requestLaziness && subRes->idTable ().size () > CHUNK_SIZE) {
107
+ auto localVocab =
108
+ std::make_shared<LocalVocab>(subRes->getCopyOfLocalVocab ());
109
+ auto generator = [](std::shared_ptr<LocalVocab> vocab, auto applyBind,
110
+ std::shared_ptr<const Result> result)
111
+ -> cppcoro::generator<IdTable> {
112
+ size_t size = result->idTable ().size ();
113
+ for (size_t offset = 0 ; offset < size; offset += CHUNK_SIZE) {
114
+ co_yield applyBind (
115
+ cloneSubView (result->idTable (),
116
+ {offset, std::min (size, offset + CHUNK_SIZE)}),
117
+ vocab.get ());
118
+ }
119
+ }(localVocab, std::move (applyBind), std::move (subRes));
120
+ return {std::move (generator), resultSortedOn (), std::move (localVocab)};
121
+ }
122
+ // Make a deep copy of the local vocab from `subRes` and then add to it (in
123
+ // case BIND adds a new word or words).
124
+ //
125
+ // Make a copy of the local vocab from`subRes`and then add to it (in case
126
+ // BIND adds new words). Note: The copy of the local vocab is shallow
127
+ // via`shared_ptr`s, so the following is also efficient if the BIND adds no
128
+ // new words.
129
+ LocalVocab localVocab = subRes->getCopyOfLocalVocab ();
130
+ IdTable result = applyBind (subRes->idTable ().clone (), &localVocab);
131
+ LOG (DEBUG) << " BIND result computation done." << std::endl;
132
+ return {std::move (result), resultSortedOn (), std::move (localVocab)};
133
+ }
134
+ auto localVocab = std::make_shared<LocalVocab>();
135
+ auto generator =
136
+ [](std::shared_ptr<LocalVocab> vocab, auto applyBind,
137
+ std::shared_ptr<const Result> result) -> cppcoro::generator<IdTable> {
138
+ for (IdTable& idTable : result->idTables ()) {
139
+ co_yield applyBind (std::move (idTable), vocab.get ());
140
+ }
141
+ std::array<const LocalVocab*, 2 > vocabs{vocab.get (), &result->localVocab ()};
142
+ *vocab = LocalVocab::merge (std::span{vocabs});
143
+ }(localVocab, std::move (applyBind), std::move (subRes));
144
+ return {std::move (generator), resultSortedOn (), std::move (localVocab)};
145
+ }
146
+
147
+ // _____________________________________________________________________________
148
+ IdTable Bind::computeExpressionBind (
149
+ LocalVocab* outputLocalVocab, IdTable idTable,
150
+ const LocalVocab& inputLocalVocab,
151
+ const sparqlExpression::SparqlExpression* expression) const {
119
152
sparqlExpression::EvaluationContext evaluationContext (
120
- *getExecutionContext (), _subtree->getVariableColumns (),
121
- inputResultTable. idTable (), getExecutionContext ()->getAllocator (),
122
- inputResultTable. localVocab (), cancellationHandle_, deadline_);
153
+ *getExecutionContext (), _subtree->getVariableColumns (), idTable,
154
+ getExecutionContext ()->getAllocator (), inputLocalVocab ,
155
+ cancellationHandle_, deadline_);
123
156
124
157
sparqlExpression::ExpressionResult expressionResult =
125
158
expression->evaluate (&evaluationContext);
126
159
127
- const auto input = inputResultTable.idTable ().asStaticView <IN_WIDTH>();
128
- auto output = std::move (*outputIdTable).toStatic <OUT_WIDTH>();
129
-
130
- // first initialize the first columns (they remain identical)
131
- const auto inSize = input.size ();
132
- output.reserve (inSize);
133
- const auto inCols = input.numColumns ();
134
- // copy the input to the first numColumns;
135
- for (size_t i = 0 ; i < inSize; ++i) {
136
- output.emplace_back ();
137
- for (size_t j = 0 ; j < inCols; ++j) {
138
- output (i, j) = input (i, j);
139
- }
140
- checkCancellation ();
141
- }
160
+ idTable.addEmptyColumn ();
161
+ auto outputColumn = idTable.getColumn (idTable.numColumns () - 1 );
142
162
143
163
auto visitor = [&]<sparqlExpression::SingleExpressionResult T>(
144
164
T&& singleResult) mutable {
145
165
constexpr static bool isVariable = std::is_same_v<T, ::Variable>;
146
166
constexpr static bool isStrongId = std::is_same_v<T, Id>;
147
167
148
168
if constexpr (isVariable) {
149
- auto column =
169
+ auto columnIndex =
150
170
getInternallyVisibleVariableColumns ().at (singleResult).columnIndex_ ;
151
- for (size_t i = 0 ; i < inSize; ++i) {
152
- output (i, inCols) = output (i, column);
153
- checkCancellation ();
154
- }
171
+ auto inputColumn = idTable.getColumn (columnIndex);
172
+ AD_CORRECTNESS_CHECK (inputColumn.size () == outputColumn.size ());
173
+ std::ranges::copy (inputColumn, outputColumn.begin ());
155
174
} else if constexpr (isStrongId) {
156
- for (size_t i = 0 ; i < inSize; ++i) {
157
- output (i, inCols) = singleResult;
158
- checkCancellation ();
159
- }
175
+ std::ranges::fill (outputColumn, singleResult);
160
176
} else {
161
177
constexpr bool isConstant = sparqlExpression::isConstantResult<T>;
162
178
163
179
auto resultGenerator = sparqlExpression::detail::makeGenerator (
164
- std::forward<T>(singleResult), inSize, &evaluationContext);
180
+ std::forward<T>(singleResult), outputColumn.size (),
181
+ &evaluationContext);
165
182
166
183
if constexpr (isConstant) {
167
184
auto it = resultGenerator.begin ();
168
185
if (it != resultGenerator.end ()) {
169
186
Id constantId =
170
187
sparqlExpression::detail::constantExpressionResultToId (
171
188
std::move (*it), *outputLocalVocab);
172
- for (size_t i = 0 ; i < inSize; ++i) {
173
- output (i, inCols) = constantId;
174
- checkCancellation ();
175
- }
189
+ checkCancellation ();
190
+ std::ranges::fill (outputColumn, constantId);
176
191
}
177
192
} else {
178
193
size_t i = 0 ;
179
194
// We deliberately move the values from the generator.
180
195
for (auto & resultValue : resultGenerator) {
181
- output (i, inCols) =
196
+ outputColumn[i] =
182
197
sparqlExpression::detail::constantExpressionResultToId (
183
198
std::move (resultValue), *outputLocalVocab);
184
199
i++;
@@ -190,5 +205,5 @@ void Bind::computeExpressionBind(
190
205
191
206
std::visit (visitor, std::move (expressionResult));
192
207
193
- *outputIdTable = std::move (output). toDynamic () ;
208
+ return idTable ;
194
209
}
0 commit comments