// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using vespalib::btree::BTree; using vespalib::btree::BTreeNode; using vespalib::btree::BTreeTraits; enum class ScanMethod { ITERATOR, FUNCTOR }; class ScanSpeed { template void work_loop(ScanMethod scan_method); public: int main(); }; namespace { const char *scan_method_name(ScanMethod scan_method) { switch (scan_method) { case ScanMethod::ITERATOR: return "iterator"; default: return "functor"; } } class ScanOnce { public: virtual ~ScanOnce() = default; virtual void operator()(std::vector& bv) = 0; }; template class ScanTree : public ScanOnce { protected: const Tree &_tree; int _startval; int _endval; public: ScanTree(const Tree &tree, int startval, int endval) : _tree(tree), _startval(startval), _endval(endval) { } ~ScanTree() override { } }; template class ScanWithIterator : public ScanTree { public: ScanWithIterator(const Tree &tree, int startval, int endval) : ScanTree(tree, startval, endval) { } ~ScanWithIterator() override = default; void operator()(std::vector& bv) override; }; template void ScanWithIterator::operator()(std::vector& bv) { using ConstIterator = typename Tree::ConstIterator; ConstIterator itr(BTreeNode::Ref(), this->_tree.getAllocator()); itr.lower_bound(this->_tree.getRoot(), this->_startval); while (itr.valid() && itr.getKey() < this->_endval) { bv[itr.getKey()] = true; ++itr; } } template class ScanWithFunctor : public ScanTree { public: ScanWithFunctor(const Tree &tree, int startval, int endval) : ScanTree(tree, startval, endval) { } ~ScanWithFunctor() override = default; void operator()(std::vector& bv) override; }; template void ScanWithFunctor::operator()(std::vector& bv) { using ConstIterator = typename Tree::ConstIterator; ConstIterator start(BTreeNode::Ref(), this->_tree.getAllocator()); ConstIterator end(BTreeNode::Ref(), this->_tree.getAllocator()); start.lower_bound(this->_tree.getRoot(), this->_startval); end.lower_bound(this->_tree.getRoot(), this->_endval); start.foreach_key_range(end, [&](int key) { bv[key] = true; } ); } } template void ScanSpeed::work_loop(ScanMethod scan_method) { vespalib::GenerationHandler g; using Tree = BTree, Traits>; using Builder = typename Tree::Builder; Tree tree; Builder builder(tree.getAllocator()); size_t numEntries = 1000000; size_t numInnerLoops = 1000; for (size_t i = 0; i < numEntries; ++i) { builder.insert(i, 0); } tree.assign(builder); assert(numEntries == tree.size()); assert(tree.isValid()); std::unique_ptr scan_once; if (scan_method == ScanMethod::ITERATOR) { scan_once = std::make_unique>(tree, 4, numEntries - 4); } else { scan_once = std::make_unique>(tree, 4, numEntries - 4); } auto bv = std::make_unique>(numEntries); vespalib::Timer timer; for (size_t innerl = 0; innerl < numInnerLoops; ++innerl) { (*scan_once)(*bv); } double used = vespalib::to_s(timer.elapsed()); printf("Elapsed time for scanning %ld entries is %8.5f, " "scanmethod=%s, fanout=%u,%u\n", numEntries * numInnerLoops, used, scan_method_name(scan_method), static_cast(Traits::LEAF_SLOTS), static_cast(Traits::INTERNAL_SLOTS)); fflush(stdout); } int ScanSpeed::main() { using SmallTraits = BTreeTraits<4, 4, 31, false>; using DefTraits = vespalib::btree::BTreeDefaultTraits; using LargeTraits = BTreeTraits<32, 16, 10, true>; using HugeTraits = BTreeTraits<64, 16, 10, true>; work_loop(ScanMethod::ITERATOR); work_loop(ScanMethod::ITERATOR); work_loop(ScanMethod::ITERATOR); work_loop(ScanMethod::ITERATOR); work_loop(ScanMethod::FUNCTOR); work_loop(ScanMethod::FUNCTOR); work_loop(ScanMethod::FUNCTOR); work_loop(ScanMethod::FUNCTOR); return 0; } int main(int, char **) { ScanSpeed app; return app.main(); }