From 15556f0d56bdfc7708ae74a38db9aaa0225c54ca Mon Sep 17 00:00:00 2001 From: Jimmy Lu Date: Mon, 16 Sep 2024 11:34:29 -0700 Subject: [PATCH] Parquet LazyVector support (#11010) Summary: Pull Request resolved: https://github.com/facebookincubator/velox/pull/11010 Parquet reader was not generating `LazyVector` at all. Fix this and address the bugs along the way: 1. Fix the `rowIndex` passed to hook by considering `numValuesBias_`. 2. Add the missing `setNumValues` call in Parquet `StringDecoder`. 3. Fix the Parquet string dictionary column visitor call and reuse the same code in DWRF string dictionary column reader. 4. Fix write over boundary in `VectorLoader::load`. Fix https://github.com/facebookincubator/velox/issues/9563 Differential Revision: D62724551 --- velox/common/base/RawVector.cpp | 9 +- velox/common/base/RawVector.h | 3 +- velox/dwio/common/ColumnVisitors.h | 93 ++++--------------- velox/dwio/common/DirectDecoder.h | 15 ++- .../common/tests/utils/E2EFilterTestBase.h | 5 +- .../SelectiveStringDictionaryColumnReader.cpp | 35 +------ .../SelectiveStringDictionaryColumnReader.h | 76 +-------------- velox/dwio/parquet/reader/ParquetReader.cpp | 1 + velox/dwio/parquet/reader/RleBpDataDecoder.h | 10 ++ velox/dwio/parquet/reader/StringDecoder.h | 10 ++ .../tests/reader/ParquetReaderTest.cpp | 38 +++++--- .../tests/reader/ParquetTableScanTest.cpp | 20 ++++ velox/exec/AggregationHook.h | 1 + velox/vector/LazyVector.cpp | 2 +- 14 files changed, 118 insertions(+), 200 deletions(-) diff --git a/velox/common/base/RawVector.cpp b/velox/common/base/RawVector.cpp index d541bbe26b968..6dc1c048d57f6 100644 --- a/velox/common/base/RawVector.cpp +++ b/velox/common/base/RawVector.cpp @@ -30,13 +30,14 @@ bool initializeIota() { } } // namespace -const int32_t* iota(int32_t size, raw_vector& storage) { - if (iotaData.size() < size) { +const int32_t* +iota(int32_t size, raw_vector& storage, int32_t offset) { + if (iotaData.size() < offset + size) { storage.resize(size); - std::iota(&storage[0], &storage[storage.size()], 0); + std::iota(storage.begin(), storage.end(), offset); return storage.data(); } - return iotaData.data(); + return iotaData.data() + offset; } static bool FB_ANONYMOUS_VARIABLE(g_iotaConstants) = initializeIota(); diff --git a/velox/common/base/RawVector.h b/velox/common/base/RawVector.h index af1c39baeeec9..fc6e01d133208 100644 --- a/velox/common/base/RawVector.h +++ b/velox/common/base/RawVector.h @@ -201,6 +201,7 @@ class raw_vector { // SIMD width. Typically returns preallocated memory but if this is // not large enough,resizes and initializes 'storage' to the requested // size and returns storage.data(). -const int32_t* iota(int32_t size, raw_vector& storage); +const int32_t* +iota(int32_t size, raw_vector& storage, int32_t offset = 0); } // namespace facebook::velox diff --git a/velox/dwio/common/ColumnVisitors.h b/velox/dwio/common/ColumnVisitors.h index 0c665b76886ab..5d1a42526ecd1 100644 --- a/velox/dwio/common/ColumnVisitors.h +++ b/velox/dwio/common/ColumnVisitors.h @@ -419,6 +419,10 @@ class ColumnVisitor { return reader_->mutableOutputRows(size); } + int32_t numValuesBias() const { + return numValuesBias_; + } + void setNumValuesBias(int32_t bias) { numValuesBias_ = bias; } @@ -515,12 +519,12 @@ ColumnVisitor::filterFailed() { template inline void ColumnVisitor::addResult( T value) { - values_.addValue(rowIndex_, value); + values_.addValue(rowIndex_ + numValuesBias_, value); } template inline void ColumnVisitor::addNull() { - values_.template addNull(rowIndex_); + values_.template addNull(rowIndex_ + numValuesBias_); } template @@ -819,7 +823,10 @@ class DictionaryColumnVisitor translateByDict(input, numInput, values); super::values_.hook().addValues( scatter ? scatterRows + super::rowIndex_ - : velox::iota(super::numRows_, super::innerNonNullRows()) + + : velox::iota( + super::numRows_, + super::innerNonNullRows(), + super::numValuesBias_) + super::rowIndex_, values, numInput); @@ -1174,7 +1181,7 @@ class StringDictionaryColumnVisitor super::filterFailed(); } else { if (velox::common::applyFilter( - super::filter_, valueInDictionary(value, inStrideDict))) { + super::filter_, valueInDictionary(index))) { super::filterPassed(index); if (TFilter::deterministic) { DictSuper::filterCache()[index] = FilterResult::kSuccess; @@ -1217,11 +1224,10 @@ class StringDictionaryColumnVisitor if constexpr (!DictSuper::hasFilter()) { if (hasHook) { for (auto i = 0; i < numInput; ++i) { - auto value = input[i]; super::values_.addValue( scatterRows ? scatterRows[super::rowIndex_ + i] : super::rowIndex_ + i, - value); + valueInDictionary(input[i])); } } if constexpr (std::is_same_v) { @@ -1266,16 +1272,7 @@ class StringDictionaryColumnVisitor while (bits) { int index = bits::getAndClearLastSetBit(bits); int32_t value = input[i + index]; - bool result; - if (value >= DictSuper::dictionarySize()) { - result = applyFilter( - super::filter_, - valueInDictionary(value - DictSuper::dictionarySize(), true)); - } else { - result = - applyFilter(super::filter_, valueInDictionary(value, false)); - } - if (result) { + if (applyFilter(super::filter_, valueInDictionary(value))) { DictSuper::filterCache()[value] = FilterResult::kSuccess; passed |= 1 << index; } else { @@ -1355,65 +1352,15 @@ class StringDictionaryColumnVisitor } } - folly::StringPiece valueInDictionary(int64_t index, bool inStrideDict) { - if (inStrideDict) { - return folly::StringPiece(reinterpret_cast( - DictSuper::state_.dictionary2.values)[index]); - } - return folly::StringPiece(reinterpret_cast( - DictSuper::state_.dictionary.values)[index]); - } -}; - -class ExtractStringDictionaryToGenericHook { - public: - static constexpr bool kSkipNulls = true; - using HookType = ValueHook; - - ExtractStringDictionaryToGenericHook( - ValueHook* hook, - RowSet rows, - RawScanState state) - - : hook_(hook), rows_(rows), state_(state) {} - - bool acceptsNulls() { - return hook_->acceptsNulls(); - } - - template - void addNull(vector_size_t rowIndex) { - hook_->addNull(rowIndex); - } - - void addValue(vector_size_t rowIndex, int32_t value) { - // We take the string from the stripe or stride dictionary - // according to the index. Stride dictionary indices are offset up - // by the stripe dict size. - if (value < dictionarySize()) { - auto* strings = - reinterpret_cast(state_.dictionary.values); - hook_->addValue(rowIndex, strings[value]); - } else { - VELOX_DCHECK(state_.inDictionary); - auto* strings = - reinterpret_cast(state_.dictionary2.values); - hook_->addValue(rowIndex, strings[value - dictionarySize()]); + folly::StringPiece valueInDictionary(int64_t index) { + auto stripeDictSize = DictSuper::state_.dictionary.numValues; + if (index < stripeDictSize) { + return reinterpret_cast( + DictSuper::state_.dictionary.values)[index]; } + return reinterpret_cast( + DictSuper::state_.dictionary2.values)[index - stripeDictSize]; } - - ValueHook& hook() { - return *hook_; - } - - private: - int32_t dictionarySize() const { - return state_.dictionary.numValues; - } - - ValueHook* const hook_; - RowSet const rows_; - RawScanState state_; }; template diff --git a/velox/dwio/common/DirectDecoder.h b/velox/dwio/common/DirectDecoder.h index 1c243899327ae..644e41348c45b 100644 --- a/velox/dwio/common/DirectDecoder.h +++ b/velox/dwio/common/DirectDecoder.h @@ -194,6 +194,11 @@ class DirectDecoder : public IntDecoder { return; } } + if (hasHook && visitor.numValuesBias() > 0) { + for (auto& row : *outerVector) { + row += visitor.numValuesBias(); + } + } if (super::useVInts_) { if (Visitor::dense) { super::bulkRead(numNonNull, data); @@ -244,7 +249,10 @@ class DirectDecoder : public IntDecoder { rowsAsRange, 0, rowsAsRange.size(), - hasHook ? velox::iota(numRows, visitor.innerNonNullRows()) + hasHook ? velox::iota( + numRows, + visitor.innerNonNullRows(), + visitor.numValuesBias()) : nullptr, visitor.rawValues(numRows), hasFilter ? visitor.outputRows(numRows) : nullptr, @@ -254,7 +262,10 @@ class DirectDecoder : public IntDecoder { } else { dwio::common::fixedWidthScan( rowsAsRange, - hasHook ? velox::iota(numRows, visitor.innerNonNullRows()) + hasHook ? velox::iota( + numRows, + visitor.innerNonNullRows(), + visitor.numValuesBias()) : nullptr, visitor.rawValues(numRows), hasFilter ? visitor.outputRows(numRows) : nullptr, diff --git a/velox/dwio/common/tests/utils/E2EFilterTestBase.h b/velox/dwio/common/tests/utils/E2EFilterTestBase.h index 9aafbc4ac118e..d0659280b4f6b 100644 --- a/velox/dwio/common/tests/utils/E2EFilterTestBase.h +++ b/velox/dwio/common/tests/utils/E2EFilterTestBase.h @@ -120,7 +120,8 @@ class E2EFilterTestBase : public testing::Test { static bool typeKindSupportsValueHook(TypeKind kind) { return kind != TypeKind::TIMESTAMP && kind != TypeKind::ARRAY && - kind != TypeKind::ROW && kind != TypeKind::MAP; + kind != TypeKind::ROW && kind != TypeKind::MAP && + kind != TypeKind::HUGEINT; } std::vector makeDataset( @@ -257,7 +258,7 @@ class E2EFilterTestBase : public testing::Test { for (int32_t i = 0; i < 5 && i < batch->size(); ++i) { rows.push_back(i); } - for (int32_t i = 5; i < 5 && i < batch->size(); i += 2) { + for (int32_t i = 5; i < batch->size(); i += 2) { rows.push_back(i); } auto result = std::static_pointer_cast>( diff --git a/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.cpp b/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.cpp index ecbf2f47f8806..a1044c0fe7484 100644 --- a/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.cpp +++ b/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.cpp @@ -228,37 +228,10 @@ void SelectiveStringDictionaryColumnReader::read( loadStrideDictionary(); } - if (scanSpec_->keepValues()) { - if (scanSpec_->valueHook()) { - if (isDense) { - readHelper( - &alwaysTrue(), - rows, - ExtractStringDictionaryToGenericHook( - scanSpec_->valueHook(), rows, scanState_.rawState)); - } else { - readHelper( - &alwaysTrue(), - rows, - ExtractStringDictionaryToGenericHook( - scanSpec_->valueHook(), rows, scanState_.rawState)); - } - } else { - if (isDense) { - processFilter(scanSpec_->filter(), rows, ExtractToReader(this)); - } else { - processFilter(scanSpec_->filter(), rows, ExtractToReader(this)); - } - } - } else { - if (isDense) { - processFilter( - scanSpec_->filter(), rows, dwio::common::DropValues()); - } else { - processFilter( - scanSpec_->filter(), rows, dwio::common::DropValues()); - } - } + dwio::common::StringColumnReadWithVisitorHelper( + *this, rows)([&](auto visitor) { + readWithVisitor(visitor.toStringDictionaryColumnVisitor()); + }); readOffset_ += rows.back() + 1; numRowsScanned_ = readOffset_ - offset; diff --git a/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.h b/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.h index f8ee3d6705a5d..9216764aff111 100644 --- a/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.h +++ b/velox/dwio/dwrf/reader/SelectiveStringDictionaryColumnReader.h @@ -72,17 +72,7 @@ class SelectiveStringDictionaryColumnReader void makeDictionaryBaseVector(); template - void readWithVisitor(const RowSet& rows, TVisitor visitor); - - template - void - readHelper(common::Filter* filter, const RowSet& rows, ExtractValues values); - - template - void processFilter( - common::Filter* filter, - const RowSet& rows, - ExtractValues extractValues); + void readWithVisitor(TVisitor visitor); // Fills 'values' from 'data' and 'lengthDecoder'. The count of // values is in 'values.numValues'. @@ -118,9 +108,7 @@ class SelectiveStringDictionaryColumnReader }; template -void SelectiveStringDictionaryColumnReader::readWithVisitor( - const RowSet& /*rows*/, - TVisitor visitor) { +void SelectiveStringDictionaryColumnReader::readWithVisitor(TVisitor visitor) { if (version_ == velox::dwrf::RleVersion_1) { decodeWithVisitor>( dictIndex_.get(), visitor); @@ -130,64 +118,4 @@ void SelectiveStringDictionaryColumnReader::readWithVisitor( } } -template -void SelectiveStringDictionaryColumnReader::readHelper( - common::Filter* filter, - const RowSet& rows, - ExtractValues values) { - readWithVisitor( - rows, - dwio::common:: - StringDictionaryColumnVisitor( - *reinterpret_cast(filter), this, rows, values)); -} - -template -void SelectiveStringDictionaryColumnReader::processFilter( - common::Filter* filter, - const RowSet& rows, - ExtractValues extractValues) { - if (filter == nullptr) { - readHelper( - &dwio::common::alwaysTrue(), rows, extractValues); - return; - } - - switch (filter->kind()) { - case common::FilterKind::kAlwaysTrue: - readHelper(filter, rows, extractValues); - break; - case common::FilterKind::kIsNull: - filterNulls( - rows, - true, - !std::is_same_v); - break; - case common::FilterKind::kIsNotNull: - if (std::is_same_v) { - filterNulls(rows, false, false); - } else { - readHelper(filter, rows, extractValues); - } - break; - case common::FilterKind::kBytesRange: - readHelper(filter, rows, extractValues); - break; - case common::FilterKind::kNegatedBytesRange: - readHelper( - filter, rows, extractValues); - break; - case common::FilterKind::kBytesValues: - readHelper(filter, rows, extractValues); - break; - case common::FilterKind::kNegatedBytesValues: - readHelper( - filter, rows, extractValues); - break; - default: - readHelper(filter, rows, extractValues); - break; - } -} - } // namespace facebook::velox::dwrf diff --git a/velox/dwio/parquet/reader/ParquetReader.cpp b/velox/dwio/parquet/reader/ParquetReader.cpp index 81b794dc86d22..0e054c4d2a265 100644 --- a/velox/dwio/parquet/reader/ParquetReader.cpp +++ b/velox/dwio/parquet/reader/ParquetReader.cpp @@ -856,6 +856,7 @@ class ParquetRowReader::Impl { *options_.scanSpec()); columnReader_->setFillMutatedOutputRows( options_.rowNumberColumnInfo().has_value()); + columnReader_->setIsTopLevel(); filterRowGroups(); if (!rowGroupIds_.empty()) { diff --git a/velox/dwio/parquet/reader/RleBpDataDecoder.h b/velox/dwio/parquet/reader/RleBpDataDecoder.h index 9ab89a54500dc..2931e33085fd0 100644 --- a/velox/dwio/parquet/reader/RleBpDataDecoder.h +++ b/velox/dwio/parquet/reader/RleBpDataDecoder.h @@ -114,6 +114,11 @@ class RleBpDataDecoder : public facebook::velox::parquet::RleBpDecoder { visitor.setAllNull(hasFilter ? 0 : numRows); return; } + if (hasHook && visitor.numValuesBias() > 0) { + for (auto& row : *outerVector) { + row += visitor.numValuesBias(); + } + } bulkScan( folly::Range(rows, outerVector->size()), outerVector->data(), @@ -138,6 +143,11 @@ class RleBpDataDecoder : public facebook::velox::parquet::RleBpDecoder { visitor.setAllNull(hasFilter ? 0 : numRows); return; } + if (hasHook && visitor.numValuesBias() > 0) { + for (auto& row : *outerVector) { + row += visitor.numValuesBias(); + } + } bulkScan( *innerVector, outerVector->data(), visitor); skip(tailSkip, 0, nullptr); diff --git a/velox/dwio/parquet/reader/StringDecoder.h b/velox/dwio/parquet/reader/StringDecoder.h index cfbb1589ba512..740a91cd9a537 100644 --- a/velox/dwio/parquet/reader/StringDecoder.h +++ b/velox/dwio/parquet/reader/StringDecoder.h @@ -43,6 +43,7 @@ class StringDecoder { template void readWithVisitor(const uint64_t* nulls, Visitor visitor) { int32_t current = visitor.start(); + int32_t numValues = 0; skip(current, 0, nulls); int32_t toSkip; bool atEnd = false; @@ -57,6 +58,10 @@ class StringDecoder { skip(toSkip, current, nullptr); } if (atEnd) { + if constexpr (Visitor::kHasHook) { + visitor.setNumValues( + Visitor::kHasFilter ? numValues : visitor.numRows()); + } return; } } @@ -66,11 +71,16 @@ class StringDecoder { fixedLength_ > 0 ? readFixedString() : readString(), atEnd); } ++current; + ++numValues; if (toSkip) { skip(toSkip, current, nulls); current += toSkip; } if (atEnd) { + if constexpr (Visitor::kHasHook) { + visitor.setNumValues( + Visitor::kHasFilter ? numValues : visitor.numRows()); + } return; } } diff --git a/velox/dwio/parquet/tests/reader/ParquetReaderTest.cpp b/velox/dwio/parquet/tests/reader/ParquetReaderTest.cpp index 67da2935efc56..a774f83a0609b 100644 --- a/velox/dwio/parquet/tests/reader/ParquetReaderTest.cpp +++ b/velox/dwio/parquet/tests/reader/ParquetReaderTest.cpp @@ -648,8 +648,14 @@ TEST_F(ParquetReaderTest, parseIntDecimal) { rowReader->next(6, result); EXPECT_EQ(result->size(), 6ULL); auto decimals = result->as(); - auto a = decimals->childAt(0)->asFlatVector()->rawValues(); - auto b = decimals->childAt(1)->asFlatVector()->rawValues(); + auto a = decimals->childAt(0) + ->loadedVector() + ->asFlatVector() + ->rawValues(); + auto b = decimals->childAt(1) + ->loadedVector() + ->asFlatVector() + ->rawValues(); for (int i = 0; i < 3; i++) { int index = 2 * i; EXPECT_EQ(a[index], expectValues[i]); @@ -752,7 +758,8 @@ TEST_F(ParquetReaderTest, parseRowArrayTest) { ASSERT_TRUE(rowReader->next(1, result)); // data: 10, 9, , null, {9}, 2 elements starting at 0 {{9}, {10}}} - auto structArray = result->as()->childAt(5)->as(); + auto structArray = + result->as()->childAt(5)->loadedVector()->as(); auto structEle = structArray->elements() ->as() ->childAt(0) @@ -1198,8 +1205,11 @@ TEST_F(ParquetReaderTest, readVarbinaryFromFLBA) { rowReader->next(1, result); EXPECT_EQ( expected, - result->as()->childAt(0)->asFlatVector()->valueAt( - 0)); + result->as() + ->childAt(0) + ->loadedVector() + ->asFlatVector() + ->valueAt(0)); } TEST_F(ParquetReaderTest, readBinaryAsStringFromNation) { @@ -1226,10 +1236,11 @@ TEST_F(ParquetReaderTest, readBinaryAsStringFromNation) { auto expected = std::string("ALGERIA"); VectorPtr result = BaseVector::create(outputRowType, 0, &(*leafPool_)); rowReader->next(1, result); + auto nameVector = result->as()->childAt(1); + ASSERT_TRUE(isLazyNotLoaded(*nameVector)); EXPECT_EQ( expected, - result->as()->childAt(1)->asFlatVector()->valueAt( - 0)); + nameVector->loadedVector()->asFlatVector()->valueAt(0)); } TEST_F(ParquetReaderTest, readFixedLenBinaryAsStringFromUuid) { @@ -1254,10 +1265,11 @@ TEST_F(ParquetReaderTest, readFixedLenBinaryAsStringFromUuid) { auto expected = std::string("5468454a-363f-ccc8-7d0b-76072a75dfaa"); VectorPtr result = BaseVector::create(outputRowType, 0, &(*leafPool_)); rowReader->next(1, result); + auto uuidVector = result->as()->childAt(0); + ASSERT_TRUE(isLazyNotLoaded(*uuidVector)); EXPECT_EQ( expected, - result->as()->childAt(0)->asFlatVector()->valueAt( - 0)); + uuidVector->loadedVector()->asFlatVector()->valueAt(0)); } TEST_F(ParquetReaderTest, testV2PageWithZeroMaxDefRep) { @@ -1452,9 +1464,11 @@ TEST_F(ParquetReaderTest, testLzoDataPage) { VectorPtr result = BaseVector::create(outputRowType, 0, &*leafPool_); rowReader->next(23'547ULL, result); EXPECT_EQ(23'547ULL, result->size()); - auto values = result->as()->childAt(0)->as(); - auto intField = values->childAt(0)->asFlatVector(); - auto stringArray = values->childAt(1)->as(); + auto* rowVector = result->asUnchecked(); + auto* values = + rowVector->childAt(0)->loadedVector()->asUnchecked(); + auto* intField = values->childAt(0)->loadedVector()->asFlatVector(); + auto* stringArray = values->childAt(1)->loadedVector()->as(); EXPECT_EQ(intField->valueAt(0), 1); EXPECT_EQ(intField->valueAt(23'546), 13); EXPECT_EQ( diff --git a/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp b/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp index 4261ee7022490..fe1015004fd68 100644 --- a/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp +++ b/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp @@ -312,6 +312,26 @@ TEST_F(ParquetTableScanTest, basic) { "SELECT max(b), a FROM tmp WHERE a < 3 GROUP BY a"); } +TEST_F(ParquetTableScanTest, lazy) { + auto filePath = getExampleFilePath("sample.parquet"); + auto schema = ROW({"a", "b"}, {BIGINT(), DOUBLE()}); + CursorParameters params; + params.copyResult = false; + params.planNode = PlanBuilder().tableScan(schema).planNode(); + auto cursor = TaskCursor::create(params); + cursor->task()->addSplit("0", exec::Split(makeSplit(filePath))); + cursor->task()->noMoreSplits("0"); + int rows = 0; + while (cursor->moveNext()) { + auto* result = cursor->current()->asUnchecked(); + ASSERT_TRUE(result->childAt(0)->isLazy()); + ASSERT_TRUE(result->childAt(1)->isLazy()); + rows += result->size(); + } + ASSERT_EQ(rows, 20); + ASSERT_TRUE(waitForTaskCompletion(cursor->task().get())); +} + TEST_F(ParquetTableScanTest, countStar) { // sample.parquet holds two columns (a: BIGINT, b: DOUBLE) and // 20 rows. diff --git a/velox/exec/AggregationHook.h b/velox/exec/AggregationHook.h index 21444aca08a08..1bd2e42f429df 100644 --- a/velox/exec/AggregationHook.h +++ b/velox/exec/AggregationHook.h @@ -233,6 +233,7 @@ class MinMaxHook final : public AggregationHook { template void addValueImpl(vector_size_t row, T value) { auto group = findGroup(row); + LOG(INFO) << "row=" << row << " group=" << group << " value=" << value; auto* currPtr = reinterpret_cast(group + offset_); if constexpr (std::is_floating_point_v) { static const auto isGreater = diff --git a/velox/vector/LazyVector.cpp b/velox/vector/LazyVector.cpp index b836faeecc958..d0cc09c3d59e1 100644 --- a/velox/vector/LazyVector.cpp +++ b/velox/vector/LazyVector.cpp @@ -69,7 +69,7 @@ void VectorLoader::load( return; } } - std::vector positions(rows.countSelected()); + raw_vector positions(rows.countSelected()); simd::indicesOfSetBits( rows.allBits(), rows.begin(), rows.end(), positions.data()); load(positions, hook, resultSize, result);