| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 TVM_RUNTIME_HEXAGON_RING_BUFFER_H_ |
| #define TVM_RUNTIME_HEXAGON_RING_BUFFER_H_ |
| |
| #include <functional> |
| #include <queue> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "hexagon_common.h" |
| |
| namespace tvm { |
| namespace runtime { |
| namespace hexagon { |
| |
| template <class T> |
| class RingBuffer { |
| public: |
| //! \brief Returns the number of Ts in flight |
| uint32_t InFlight() { |
| while (id_oldest_ < id_next_ && !in_flight_(GetAddr(id_oldest_))) { |
| id_oldest_++; |
| } |
| return id_next_ - id_oldest_; |
| } |
| |
| //! \brief Returns pointer to next T; null if ring buffer is full |
| T* Next() { |
| if (InFlight() == ring_buff_size_) { |
| return nullptr; |
| } |
| T* next = GetAddr(id_next_); |
| id_next_++; |
| return next; |
| } |
| |
| /*! \brief Creates a ring buffer for storage items of type T |
| * \param ring_buff_size Size of the ring buffer in number of Ts |
| * \param in_flight Function that determines whether a T is in flight |
| */ |
| RingBuffer(uint32_t ring_buff_size, std::function<bool(T*)> in_flight) |
| : ring_buff_size_(ring_buff_size), in_flight_(in_flight) { |
| TVM_FFI_ICHECK_NE(ring_buff_size, 0); |
| int ret = posix_memalign(reinterpret_cast<void**>(&ring_buff_ptr_), sizeof(T), |
| sizeof(T) * ring_buff_size_); |
| TVM_FFI_ICHECK_EQ(ret, 0); |
| TVM_FFI_ICHECK_NE(ring_buff_ptr_, nullptr); |
| } |
| |
| ~RingBuffer() { free(ring_buff_ptr_); } |
| |
| private: |
| //! \brief Returns the address of a T given its index |
| T* GetAddr(uint32_t id) const { |
| uint32_t ring_buff_index = id % ring_buff_size_; |
| return ring_buff_ptr_ + ring_buff_index; |
| } |
| |
| //! \brief Pointer to the ring buffer |
| T* ring_buff_ptr_ = nullptr; |
| |
| //! \brief Size of the ring buffer in number of Ts |
| const uint32_t ring_buff_size_ = 0; |
| |
| //! \brief Function that determines whether a T is in flight |
| const std::function<bool(T*)> in_flight_; |
| |
| //! \brief Tracks the ID of the next T to be added to the ring buffer |
| uint32_t id_next_ = 0; |
| |
| //! \brief Tracks the ID of the oldest T in flight |
| uint32_t id_oldest_ = 0; |
| }; |
| |
| //! \brief Separates a single RingBuffer into multiple virtual queues with each queue having a |
| //! unique integer ID; queues allow for indepent users of the same RingBuffer while mainting overall |
| //! FIFO ordering among all queues |
| template <class T> |
| class QueuedRingBuffer : RingBuffer<T> { |
| public: |
| QueuedRingBuffer(uint32_t max_queues, uint32_t ring_buff_size, std::function<bool(T*)> in_flight) |
| : RingBuffer<T>(ring_buff_size, in_flight), max_queues_(max_queues) { |
| queue_descriptors_.resize(max_queues_); |
| } |
| |
| //! \brief Returns pointer to next T; add the queue ID for tracking |
| T* Next(uint32_t queue_id) { |
| TVM_FFI_ICHECK_LT(queue_id, max_queues_); |
| queue_ids_.push_back(queue_id); |
| queue_descriptor* d = &queue_descriptors_[queue_id]; |
| if (d->group_started) { |
| // if we have a group started just update then pending count |
| d->pending_in_group++; |
| } else { |
| // else create group with size one |
| d->groups.push(1); |
| d->pending_total++; |
| } |
| return RingBuffer<T>::Next(); |
| } |
| |
| //! \brief Returns the number of groups of Ts in flight for a given queue ID |
| uint32_t InFlight(uint32_t queue_id) { |
| TVM_FFI_ICHECK_LT(queue_id, max_queues_); |
| queue_descriptor* d = &queue_descriptors_[queue_id]; |
| TVM_FFI_ICHECK(!d->group_started); |
| |
| uint32_t in_flight = 0; |
| // look at the queue IDs for the RingBuffer entries in flight |
| for (size_t i = queue_ids_.size() - RingBuffer<T>::InFlight(); i < queue_ids_.size(); ++i) { |
| // increment return value if in flight queue ID matches |
| if (queue_ids_[i] == queue_id) { |
| in_flight++; |
| } |
| } |
| |
| // calculate number of groups in flight |
| while (!d->groups.empty() && d->pending_total - d->groups.front() >= in_flight) { |
| d->pending_total -= d->groups.front(); |
| d->groups.pop(); |
| } |
| |
| // return the number of groups in flight |
| return d->groups.size(); |
| } |
| |
| //! \brief Start a group of Ts, if not called the deafault group size is one |
| void StartGroup(uint32_t queue_id) { |
| TVM_FFI_ICHECK_LT(queue_id, max_queues_); |
| queue_descriptor* d = &queue_descriptors_[queue_id]; |
| TVM_FFI_ICHECK(!d->group_started); |
| |
| // start group |
| d->group_started = true; |
| d->pending_in_group = 0; |
| } |
| |
| //! \brief End a group of Ts |
| void EndGroup(uint32_t queue_id) { |
| TVM_FFI_ICHECK_LT(queue_id, max_queues_); |
| queue_descriptor* d = &queue_descriptors_[queue_id]; |
| TVM_FFI_ICHECK(d->group_started); |
| TVM_FFI_ICHECK(d->pending_in_group); |
| |
| // create group |
| if (d->pending_in_group) { |
| d->groups.emplace(d->pending_in_group); |
| } |
| d->pending_total += d->pending_in_group; |
| |
| // end group |
| d->group_started = false; |
| d->pending_in_group = 0; |
| } |
| |
| private: |
| struct queue_descriptor { |
| uint32_t pending_total = 0; |
| uint32_t pending_in_group = 0; |
| bool group_started = false; |
| std::queue<int> groups; |
| }; |
| |
| const int max_queues_; |
| std::vector<int> queue_ids_; |
| std::vector<queue_descriptor> queue_descriptors_; |
| }; |
| |
| } // namespace hexagon |
| } // namespace runtime |
| } // namespace tvm |
| |
| #endif // TVM_RUNTIME_HEXAGON_RING_BUFFER_H_ |