diff --git a/tree/ntuple/src/RFieldSequenceContainer.cxx b/tree/ntuple/src/RFieldSequenceContainer.cxx index 7505249e14b86..a541b5ba33c5e 100644 --- a/tree/ntuple/src/RFieldSequenceContainer.cxx +++ b/tree/ntuple/src/RFieldSequenceContainer.cxx @@ -291,13 +291,21 @@ unsigned char *ROOT::RRVecField::ResizeRVec(void *rvec, std::size_t nItems, std: } } - // TODO Increment capacity by a factor rather than just enough to fit the elements. + // Increment capacity geometrically rather than just enough to fit the elements. + std::size_t newCapacity = nItems; + if (*capacityPtr > 0) { + constexpr std::size_t maxSize = static_cast(std::numeric_limits::max()); + const std::size_t oldCap = static_cast(*capacityPtr); + const std::size_t grown = oldCap <= maxSize / 2 ? oldCap * 2 : maxSize; + newCapacity = std::min(maxSize, std::max(nItems, grown)); + } + Internal::DestroyRVecWithChecks(itemField->GetAlignment(), beginPtr, capacityPtr); // We trust that malloc returns a buffer with large enough alignment. // This might not be the case if T in RVec is over-aligned. - *beginPtr = static_cast(malloc(nItems * itemSize)); + *beginPtr = static_cast(malloc(newCapacity * itemSize)); R__ASSERT(*beginPtr != nullptr); - *capacityPtr = nItems; + *capacityPtr = static_cast(newCapacity); // Placement new for elements that were already there before the resize if (needsConstruct) { diff --git a/tree/ntuple/test/rfield_vector.cxx b/tree/ntuple/test/rfield_vector.cxx index af28671b25961..daa90fbbd01d9 100644 --- a/tree/ntuple/test/rfield_vector.cxx +++ b/tree/ntuple/test/rfield_vector.cxx @@ -154,7 +154,6 @@ TEST(RNTuple, InsideCollection) // TODO: test reading of "klassVec.v1" } - TEST(RNTuple, RVec) { FileRaii fileGuard("test_ntuple_rvec.root"); @@ -304,6 +303,46 @@ TEST(RNTuple, ReadVectorAsRVec) EXPECT_TRUE(rvec->empty()); } +TEST(RNTuple, RVecGeometricGrowth) +{ + FileRaii fileGuard("test_ntuple_rvec_geometric_growth.root"); + + { + auto m = RNTupleModel::Create(); + auto rvec = m->MakeField>("v"); + auto w = RNTupleWriter::Recreate(std::move(m), "ntpl", fileGuard.GetPath()); + + *rvec = {1, 2}; + w->Fill(); + *rvec = {1, 2, 3}; + w->Fill(); + *rvec = {1, 2, 3, 4}; + w->Fill(); + *rvec = {10}; + w->Fill(); + } + + auto m = RNTupleModel::Create(); + auto rvec = m->MakeField>("v"); + auto reader = RNTupleReader::Open(std::move(m), "ntpl", fileGuard.GetPath()); + + reader->LoadEntry(0); + EXPECT_EQ(rvec->size(), 2u); + EXPECT_EQ(rvec->capacity(), 2u); + + reader->LoadEntry(1); + EXPECT_EQ(rvec->size(), 3u); + EXPECT_GE(rvec->capacity(), 4u); + + reader->LoadEntry(2); + EXPECT_EQ(rvec->size(), 4u); + EXPECT_GE(rvec->capacity(), 4u); + + reader->LoadEntry(3); + EXPECT_EQ(rvec->size(), 1u); + EXPECT_GE(rvec->capacity(), 4u); +} + TEST(RNTuple, BoolVector) { FileRaii fileGuard("test_ntuple_boolvec.root");