.. _program_listing_file_include_shad_data_structures_vector.h: Program Listing for File vector.h ================================= |exhale_lsh| :ref:`Return to documentation for file ` (``include/shad/data_structures/vector.h``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp //===------------------------------------------------------------*- C++ -*-===// // // SHAD // // The Scalable High-performance Algorithms and Data Structure Library // //===----------------------------------------------------------------------===// // // Copyright 2018 Battelle Memorial Institute // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // //===----------------------------------------------------------------------===// #ifndef INCLUDE_SHAD_DATA_STRUCTURES_VECTOR_H_ #define INCLUDE_SHAD_DATA_STRUCTURES_VECTOR_H_ #include #include #include #include #include #include #include #include #include #include #include "shad/data_structures/abstract_data_structure.h" #include "shad/data_structures/buffer.h" #include "shad/runtime/runtime.h" namespace shad { template > class Vector : public AbstractDataStructure> { template class Iterator; public: using allocator_type = Allocator; using value_type = T; using difference_type = typename allocator_type::difference_type; using size_type = typename allocator_type::size_type; using iterator = Iterator; using const_iterator = Iterator; using ObjectID = typename AbstractDataStructure>::ObjectID; static_assert( (std::is_same::value), "Allocator::value_type must be the same as value_type"); size_type Size() const noexcept; size_type MaxSize() const noexcept; size_type Capacity() const noexcept; bool Empty() const noexcept; void Reserve(size_type n); void Resize(size_type n); value_type At(size_type n) const; value_type operator[](size_type n) const; value_type Front() const; value_type Back() const; void AsyncAt(rt::Handle &handle, size_type n, T *result) const; void Clear() noexcept; void PushBack(const T &value); iterator InsertAt(shad::Vector::size_type position, const shad::Vector::value_type &value); template iterator InsertAt(size_type position, InputIterator begin, InputIterator end); void AsyncInsertAt(rt::Handle &handle, shad::Vector::size_type position, const shad::Vector::value_type &value); template void AsyncInsertAt(rt::Handle &handle, size_type position, InputIterator begin, InputIterator end); void BufferedInsertAt(const size_type pos, const value_type &value); void BufferedAsyncInsertAt(rt::Handle &handle, const size_type pos, const value_type &value); void WaitForBufferedInsert() { buffers_.FlushAll(); } template void Apply(const size_type position, ApplyFunT &&function, Args &... args); template void AsyncApply(rt::Handle &handle, const size_type position, ApplyFunT &&function, Args &... args); template void ForEachInRange(const size_type first, const size_type last, ApplyFunT &&function, Args &... args); template void AsyncForEachInRange(rt::Handle &handle, const size_type first, const size_type last, ApplyFunT &&function, Args &... args); ObjectID GetGlobalID() const { return oid_; } ~Vector() { _clear(); } void BufferEntryInsert(const std::tuple entry) { auto blockOffsetPair = _blockOffsetFromPosition(std::get<0>(entry)); size_type localBlock = _globlalBlockToLocalBlock(blockOffsetPair.first); dataBlocks_.at(localBlock)[blockOffsetPair.second] = std::get<1>(entry); } protected: Vector(ObjectID oid, size_type n) : oid_(oid), dataBlocks_(), mainLocality_(static_cast(oid) % rt::numLocalities()), sizeCapacityLock_(), size_(n), capacity_(0), allocator_(), buffers_(oid) { size_t blocksToAllocate = std::max(_sizeToLocalBlocks(n, kBlockSize), 1UL); capacity_ = std::max(kBlockSize * _blockOffsetFromPosition(n).first, kBlockSize); if (n == 0 && static_cast(rt::thisLocality()) != 0) return; for (size_t i = 0; i < blocksToAllocate; ++i) dataBlocks_.emplace_back(std::allocator_traits::allocate( allocator_, kBlockSize)); } private: friend class AbstractDataStructure>; static const size_type kBlockSize; std::tuple _targetFromPosition( size_type position, size_type blockSize) const { size_t blockNumber = position / blockSize; size_t destination = blockNumber % rt::numLocalities(); size_t offset = position % blockSize; return std::make_tuple(rt::Locality(destination), blockNumber, offset); } size_t _sizeToLocalBlocks(size_type n, size_type blockSize) const { size_t fullyUsedBlocks(0); rt::Locality pivot(0); if (n == 0) return 0; std::tie(pivot, fullyUsedBlocks, std::ignore) = _targetFromPosition(n - 1, blockSize); size_t localBlocks = fullyUsedBlocks / rt::numLocalities(); if (rt::thisLocality() <= pivot) localBlocks += 1; return localBlocks; } std::pair _blockOffsetFromPosition(size_type n) const { size_type blockNumber = n / kBlockSize; size_type offset = n % kBlockSize; return std::make_pair(blockNumber, offset); } size_type _globlalBlockToLocalBlock(size_type n) const { size_type i = 0; while (static_cast(rt::thisLocality()) < n) { n -= rt::numLocalities(); ++i; } return i; } void _reserve(size_type n) { size_type currentCapacity = Capacity(); if (currentCapacity >= n) return; rt::Locality insertLocality(0); size_t lastBlock(0); std::tie(insertLocality, lastBlock, std::ignore) = _targetFromPosition(currentCapacity, kBlockSize); size_t newLastBlock = n / kBlockSize; size_type blocksToAllocate = 1; blocksToAllocate += newLastBlock - lastBlock; std::vector newBlocks(rt::numLocalities(), 0); for (size_t i = static_cast(insertLocality), j = blocksToAllocate; j > 0; ++i, --j) { newBlocks[i % rt::numLocalities()]++; } rt::Handle handle; for (size_t i = 0; i < rt::numLocalities(); ++i) { if (newBlocks[i] == 0) continue; rt::asyncExecuteAt( handle, rt::Locality(i), [](rt::Handle &, const std::pair &args) { auto This = Vector::GetPtr(args.first); for (size_t i = 0; i < args.second; ++i) { This->dataBlocks_.emplace_back( std::allocator_traits::allocate( This->allocator_, kBlockSize)); } }, std::make_pair(oid_, newBlocks[i])); } rt::waitForCompletion(handle); capacity_ += kBlockSize * blocksToAllocate; } void _clear() { for (auto block : dataBlocks_) { for (T *toDestroy = block; toDestroy < block + kBlockSize; ++toDestroy) { std::allocator_traits::destroy(allocator_, toDestroy); } std::allocator_traits::deallocate(allocator_, block, kBlockSize); } dataBlocks_.clear(); } template static void CallApplyFun(const ObjectID &oid, const size_type position, ApplyFunT function, std::tuple &args, std::index_sequence) { auto This = Vector::GetPtr(oid); auto blockOffsetPair = This->_blockOffsetFromPosition(position); size_type localBlock = This->_globlalBlockToLocalBlock(blockOffsetPair.first); value_type &element = This->dataBlocks_.at(localBlock)[blockOffsetPair.second]; function(position, element, std::get(args)...); } template static void ApplyFunWrapper(const Tuple &args) { constexpr auto Size = std::tuple_size< typename std::decay(args))>::type>::value; Tuple &tuple = const_cast(args); CallApplyFun(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::make_index_sequence{}); } template static void AsyncCallApplyFun(rt::Handle &handle, const ObjectID &oid, const size_type position, ApplyFunT function, std::tuple &args, std::index_sequence) { auto This = Vector::GetPtr(oid); auto blockOffsetPair = This->_blockOffsetFromPosition(position); size_type localBlock = This->_globlalBlockToLocalBlock(blockOffsetPair.first); value_type &element = This->dataBlocks_[localBlock][blockOffsetPair.second]; function(handle, position, element, std::get(args)...); } template static void AsyncApplyFunWrapper(rt::Handle &handle, const Tuple &args) { constexpr auto Size = std::tuple_size< typename std::decay(args))>::type>::value; Tuple &tuple = const_cast(args); AsyncCallApplyFun(handle, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::make_index_sequence{}); } template static void CallForEachInRangeFun(const size_t i, const ObjectID &oid, const size_t position, ApplyFunT function, std::tuple &args, std::index_sequence) { // Get a local instance on the remote node. auto This = Vector::GetPtr(oid); auto blockOffsetPair = This->_blockOffsetFromPosition(position + i); size_type localBlock = This->_globlalBlockToLocalBlock(blockOffsetPair.first); value_type &element = This->dataBlocks_.at(localBlock)[blockOffsetPair.second]; function(position + i, element, std::get(args)...); } template static void ForEachInRangeFunWrapper(const Tuple &args, size_t i) { constexpr auto Size = std::tuple_size< typename std::decay(args))>::type>::value; Tuple &tuple = const_cast(args); CallForEachInRangeFun(i, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::make_index_sequence{}); } template static void AsyncCallForEachInRangeFun(rt::Handle &handle, const size_t i, const ObjectID &oid, const size_t position, ApplyFunT function, std::tuple &args, std::index_sequence) { // Get a local instance on the remote node. auto This = Vector::GetPtr(oid); auto blockOffsetPair = This->_blockOffsetFromPosition(position + i); size_type localBlock = This->_globlalBlockToLocalBlock(blockOffsetPair.first); value_type &element = This->dataBlocks_.at(localBlock)[blockOffsetPair.second]; function(handle, position + i, element, std::get(args)...); } template static void AsyncForEachInRangeFunWrapper(rt::Handle &handle, const Tuple &args, size_t i) { constexpr auto Size = std::tuple_size< typename std::decay(args))>::type>::value; Tuple &tuple = const_cast(args); AsyncCallForEachInRangeFun( handle, i, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::make_index_sequence{}); } using BuffersVector = impl::BuffersVector, Vector>; friend class impl::BuffersVector, Vector>; ObjectID oid_; rt::Locality mainLocality_; std::vector dataBlocks_; rt::Lock sizeCapacityLock_; size_type size_; size_type capacity_; allocator_type allocator_; BuffersVector buffers_; }; template const typename Vector::size_type Vector::kBlockSize = constants::max((1024 << 6) / sizeof(T), 1lu); template template class Vector::Iterator : std::iterator { public: Iterator(Vector::size_type n, Vector::ObjectID oid) : position_(n), oid_(oid) {} Iterator(const Iterator &itr) = default; Iterator &operator=(const Iterator &itr) = default; explicit operator bool() const { if (position_ == Vector::MaxSize()) return false; else return true; } bool operator==(const Iterator &rhs) const { if (oid_ != rhs.oid_) return false; if (position_ != rhs.position_) return false; else return true; } bool operator!=(const Iterator &rhs) const { return !(*this == rhs); } Iterator &operator+=(const ptrdiff_t &movement) { position_ += movement; return *this; } Iterator &operator-=(const ptrdiff_t &movement) { position_ -= movement; return *this; } Iterator &operator++() { ++position_; return *this; } Iterator &operator--() { --position_; return *this; } Iterator operator++(int) { auto tmp(*this); ++position_; return tmp; } Iterator operator--(int) { auto tmp(*this); --position_; tmp; } Iterator operator+(const ptrdiff_t &movement) { Iterator tmp(*this); tmp.position_ += movement; return tmp; } Iterator operator-(const ptrdiff_t &movement) { Iterator tmp(*this); tmp.position_ -= movement; return tmp; } ptrdiff_t operator-(const Iterator &rhs) { return std::distance(rhs.position_, this->position_); } const value_type operator*() const { auto ptr = Vector::GetPtr(oid_); return ptr->At(position_); } const value_type *operator->() const { auto ptr = Vector::GetPtr(oid_); return &*(*this); } private: Vector::size_type position_; Vector::ObjectID oid_; }; template typename Vector::size_type Vector::Size() const noexcept { if (rt::thisLocality() == mainLocality_) return size_; size_type size = 0; rt::executeAtWithRet(mainLocality_, [](const ObjectID &oid, size_type *size) { auto This = Vector::GetPtr(oid); *size = This->size_; }, oid_, &size); return size; } template typename Vector::size_type Vector::MaxSize() const noexcept { return std::numeric_limits::size_type>::max() - 1; } template typename Vector::size_type Vector::Capacity() const noexcept { if (rt::thisLocality() == mainLocality_) return capacity_; size_type capacity = 0; rt::executeAtWithRet(mainLocality_, [](const ObjectID &oid, size_type *capacity) { auto ptr = Vector::GetPtr(oid); *capacity = ptr->capacity_; }, oid_, &capacity); return capacity; } template bool Vector::Empty() const noexcept { return Size() == 0; } template void Vector::Reserve(Vector::size_type n) { rt::executeAt(mainLocality_, [](const std::pair &args) { auto This = Vector::GetPtr(args.first); auto n = args.second; std::lock_guard _(This->sizeCapacityLock_); This->_reserve(n); }, std::make_pair(oid_, n)); } template void Vector::Resize(Vector::size_type n) { rt::executeAt(mainLocality_, [](const std::pair &args) { auto This = Vector::GetPtr(args.first); auto n = args.second; std::lock_guard _(This->sizeCapacityLock_); if (n <= This->size_) return; This->_reserve(n); This->size_ = n; }, std::make_pair(oid_, n)); } template typename Vector::value_type Vector::At( Vector::size_type n) const { rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(n, kBlockSize); if (target == rt::thisLocality()) { size_type localBlock = _globlalBlockToLocalBlock(blockNumber); return dataBlocks_[localBlock][offset]; } else { T value; rt::executeAtWithRet( target, [](const std::tuple &args, T *result) { auto This = Vector::GetPtr(std::get<0>(args)); size_type localBlock = This->_globlalBlockToLocalBlock(std::get<1>(args)); *result = This->dataBlocks_[localBlock][std::get<2>(args)]; }, std::make_tuple(oid_, blockNumber, offset), &value); return value; } } template typename Vector::value_type Vector::operator[]( Vector::size_type n) const { return At(n); } template typename Vector::value_type Vector::Front() const { return At(0); } template typename Vector::value_type Vector::Back() const { size_type last_position = Size() - 1; return At(last_position); } template void Vector::AsyncAt( rt::Handle &handle, Vector::size_type n, Vector::value_type *result) const { rt::Locality target(0); size_type blockNumber(0); size_type offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(n, kBlockSize); rt::asyncExecuteAtWithRet( handle, target, [](rt::Handle &handle, const std::tuple &args, T *result) { auto This = Vector::GetPtr(std::get<0>(args)); size_type localBlock = This->_globlalBlockToLocalBlock(std::get<1>(args)); *result = This->dataBlocks_[localBlock][std::get<2>(args)]; }, std::make_tuple(oid_, blockNumber, offset), result); } template void Vector::Clear() noexcept { rt::executeAt(mainLocality_, [](const ObjectID &args) { auto This = Vector::GetPtr(args); std::lock_guard _(This->sizeCapacityLock_); This->size_ = 0; This->capacity_ = 0; rt::executeOnAll( [](const ObjectID &args) { auto This = Vector::GetPtr(args); This->_clear(); }, args); }, oid_); } template void Vector::PushBack( const typename Vector::value_type &value) { size_type newSize(0); rt::executeAtWithRet(mainLocality_, [](const ObjectID &args, size_type *size) { auto This = Vector::GetPtr(args); std::lock_guard _(This->sizeCapacityLock_); *size = ++This->size_; if (This->size_ > This->capacity_) This->_reserve(This->size_); }, oid_, &newSize); size_type position = newSize - 1; rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); if (target == rt::thisLocality()) { size_type localBlock = _globlalBlockToLocalBlock(blockNumber); dataBlocks_[localBlock][offset] = value; } else { using MessageTuple = std::tuple; rt::executeAt(target, [](const MessageTuple &args) { auto This = Vector::GetPtr(std::get<0>(args)); size_type localBlock = This->_globlalBlockToLocalBlock(std::get<1>(args)); This->dataBlocks_[localBlock][std::get<2>(args)] = std::get<3>(args); }, std::make_tuple(oid_, blockNumber, offset, value)); } } template typename Vector::iterator Vector::InsertAt( Vector::size_type position, const Vector::value_type &value) { rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); if (target == rt::thisLocality()) { size_type localBlock = _globlalBlockToLocalBlock(blockNumber); dataBlocks_[localBlock][offset] = value; } else { using MessageTuple = std::tuple; rt::executeAt(target, [](const MessageTuple &args) { auto This = Vector::GetPtr(std::get<0>(args)); size_type localBlock = This->_globlalBlockToLocalBlock(std::get<1>(args)); This->dataBlocks_[localBlock][std::get<2>(args)] = std::get<3>(args); }, std::make_tuple(oid_, blockNumber, offset, value)); } return Vector::iterator(position, GetGlobalID()); } template template typename Vector::iterator Vector::InsertAt( Vector::size_type position, IteratorType begin, IteratorType end) { rt::Handle handle; AsyncInsertAt(handle, position, begin, end); rt::waitForCompletion(handle); return Vector::iterator(position, GetGlobalID()); } template void Vector::AsyncInsertAt( rt::Handle &handle, Vector::size_type position, const Vector::value_type &value) { size_type currentSize(0); rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); if (target == rt::thisLocality()) { size_type localBlock = _globlalBlockToLocalBlock(blockNumber); dataBlocks_[localBlock][offset] = value; } else { using MessageTuple = std::tuple; rt::asyncExecuteAt( handle, target, [](rt::Handle &, const MessageTuple &args) { auto This = Vector::GetPtr(std::get<0>(args)); size_type localBlock = This->_globlalBlockToLocalBlock(std::get<1>(args)); This->dataBlocks_[localBlock][std::get<2>(args)] = std::get<3>(args); }, std::make_tuple(oid_, blockNumber, offset, value)); } } template template void Vector::AsyncInsertAt( rt::Handle &handle, Vector::size_type position, IteratorType begin, IteratorType end) { size_type newElements = std::distance(begin, end); size_type newSize(0); rt::executeAtWithRet( mainLocality_, [](const std::tuple &args, size_type *size) { auto This = Vector::GetPtr(std::get<0>(args)); std::lock_guard _(This->sizeCapacityLock_); auto position = std::get<1>(args); auto newElements = std::get<2>(args); if (position > This->size_ || position + newElements <= This->size_) { *size = This->size_; return; } size_type end = position + newElements; size_type growth = This->size_ > end ? 0 : end - This->size_; This->size_ += growth; if (This->size_ > This->capacity_) This->_reserve(This->size_); *size = This->size_; }, std::make_tuple(oid_, position, newElements), &newSize); // Trying to insert in an invalid position. if (position > newSize) { throw std::out_of_range("AsyncInsertAt: position out of range"); } size_type startingPoint = newSize - newElements; constexpr size_t kNumElements = 4000 / sizeof(value_type); static_assert(kNumElements >= 1, "We can't do this due to a series of unfortunate events"); struct InsertMessage { ObjectID objID; size_type startPosition; size_type numElements; value_type elements[kNumElements]; InsertMessage() : objID(ObjectID::kNullID), startPosition(0), numElements(0) {} }; auto insertFunction = [](rt::Handle &, const InsertMessage &args) { auto This = Vector::GetPtr(args.objID); auto blockOffsetPair = This->_blockOffsetFromPosition(args.startPosition); size_type localBlock = This->_globlalBlockToLocalBlock(blockOffsetPair.first); std::copy(&args.elements[0], &args.elements[args.numElements], &This->dataBlocks_.at(localBlock)[blockOffsetPair.second]); }; InsertMessage args; size_t spaceLeftInBlock = kBlockSize - (startingPoint % kBlockSize); // first block args.objID = oid_; args.startPosition = startingPoint; args.numElements = std::min(newElements, std::min(kNumElements, spaceLeftInBlock)); std::copy(begin, begin + args.numElements, args.elements); rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); rt::asyncExecuteAt(handle, target, insertFunction, args); begin += args.numElements; newElements -= args.numElements; startingPoint += args.numElements; // inner blocks while (begin != end && newElements > 0) { std::tie(target, blockNumber, offset) = _targetFromPosition(startingPoint, kBlockSize); args.startPosition = startingPoint; spaceLeftInBlock = kBlockSize - (startingPoint % kBlockSize); args.numElements = std::min(newElements, std::min(kNumElements, spaceLeftInBlock)); std::copy(begin, begin + args.numElements, args.elements); rt::asyncExecuteAt(handle, target, insertFunction, args); begin += args.numElements; newElements -= args.numElements; startingPoint += args.numElements; } } template void Vector::BufferedInsertAt(const size_type position, const value_type &value) { rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); if (target == rt::thisLocality()) { size_type localBlock = _globlalBlockToLocalBlock(blockNumber); dataBlocks_[localBlock][offset] = value; } else { buffers_.Insert(std::make_tuple(position, value), target); } } template void Vector::BufferedAsyncInsertAt(rt::Handle &handle, const size_type position, const value_type &value) { rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); if (target == rt::thisLocality()) { size_type localBlock = _globlalBlockToLocalBlock(blockNumber); dataBlocks_[localBlock][offset] = value; } else { buffers_.AsyncInsert(handle, std::make_tuple(position, value), target); } } template template void Vector::Apply(const Vector::size_type position, ApplyFunT &&function, Args &... args) { rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); using FunctionTy = void (*)(size_type, T &, Args & ...); FunctionTy fn = std::forward(function); using ArgsTuple = std::tuple>; ArgsTuple argsTuple{oid_, position, fn, std::tuple(args...)}; rt::executeAt(target, ApplyFunWrapper, argsTuple); } template template void Vector::AsyncApply( rt::Handle &handle, const Vector::size_type position, ApplyFunT &&function, Args &... args) { rt::Locality target(0); size_t blockNumber(0); size_t offset(0); std::tie(target, blockNumber, offset) = _targetFromPosition(position, kBlockSize); using FunctionTy = void (*)(rt::Handle &, size_type, T &, Args & ...); FunctionTy fn = std::forward(function); using ArgsTuple = std::tuple>; ArgsTuple argsTuple{oid_, position, fn, std::tuple(args...)}; rt::asyncExecuteAt(handle, target, AsyncApplyFunWrapper, argsTuple); } template template void Vector::ForEachInRange(const size_type begin, const size_type end, ApplyFunT &&function, Args &... args) { using FunctionTy = void (*)(size_type, value_type &, Args & ...); FunctionTy fn = std::forward(function); using ArgsTuple = std::tuple>; size_type numElements = end - begin; size_type start = begin; // first block size_type blockSize = std::min(numElements, kBlockSize - (start % kBlockSize)); rt::Locality target(0); std::tie(target, std::ignore, std::ignore) = _targetFromPosition(start, kBlockSize); ArgsTuple argsTuple{oid_, start, fn, std::tuple(args...)}; rt::forEachAt(target, ForEachInRangeFunWrapper, argsTuple, blockSize); start += blockSize; numElements -= blockSize; // inner blocks while (start != end && numElements > 0) { std::tie(target, std::ignore, std::ignore) = _targetFromPosition(start, kBlockSize); blockSize = std::min(kBlockSize, numElements); std::get<1>(argsTuple) = start; rt::forEachAt(target, ForEachInRangeFunWrapper, argsTuple, blockSize); start += blockSize; numElements -= blockSize; } } template template void Vector::AsyncForEachInRange(rt::Handle &handle, const size_type begin, const size_type end, ApplyFunT &&function, Args &... args) { using FunctionTy = void (*)(rt::Handle &, size_type, value_type &, Args & ...); FunctionTy fn = std::forward(function); using ArgsTuple = std::tuple>; size_type numElements = end - begin; size_type start = begin; // first block size_type blockSize = std::min(numElements, kBlockSize - (start % kBlockSize)); rt::Locality target(0); std::tie(target, std::ignore, std::ignore) = _targetFromPosition(start, kBlockSize); ArgsTuple argsTuple{oid_, start, fn, std::tuple(args...)}; rt::asyncForEachAt(handle, target, AsyncForEachInRangeFunWrapper, argsTuple, blockSize); start += blockSize; numElements -= blockSize; // inner blocks while (start != end && numElements > 0) { std::tie(target, std::ignore, std::ignore) = _targetFromPosition(start, kBlockSize); blockSize = std::min(kBlockSize, numElements); std::get<1>(argsTuple) = start; rt::asyncForEachAt(handle, target, AsyncForEachInRangeFunWrapper, argsTuple, blockSize); start += blockSize; numElements -= blockSize; } } } // namespace shad #endif // INCLUDE_SHAD_DATA_STRUCTURES_VECTOR_H_