Started porting newest version of MxTasking.

This commit is contained in:
Michael Mueller
2025-05-05 18:19:34 +02:00
parent 795b22afeb
commit d9d02c9024
118 changed files with 45165 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Jan Macheta
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,960 @@
#ifndef STATIC_VECTOR_HPP_
#define STATIC_VECTOR_HPP_
#include <algorithm>
#include <array>
#include <compare>
#include <cstdlib>
#include <iterator>
#include <memory>
#include <type_traits>
#ifdef __cpp_exceptions
#include <exception>
#define ECPP_STATIC_VECTOR_THROW(x) throw(x)
#else
#define ECPP_STATIC_VECTOR_THROW(x) std::abort();
#endif
namespace ecpp
{
/**
* @brief Implementation of standard vector with statically allocated pool
* The container uses underlying array to allocate required capacity.
* For detailed documentation see https://en.cppreference.com/w/cpp/container/vector
* The documentation below highlights only the differences.
*/
template <typename T, std::size_t N> class static_vector
{
public:
using value_type = T; ///< The type of the elements
using storage_container_type =
std::array<std::aligned_storage_t<sizeof(T), alignof(T)>,
N>; ///< Type of underlying continuous static storage
using size_type = typename storage_container_type::size_type; ///< Unsigned integer type
///< (usually std::size_t)
using pointer = T *; ///< Pointer to element type
using const_pointer = T const *; ///< Const pointer to element type
using reference = T &; ///< Reference to element type
using const_reference = T const &; ///< Const reference to element type
using iterator = pointer; ///< Iterator to value_type type that meets
///< LegacyRandomAccessIterator requirements
using const_iterator = const_pointer; ///< Iterator to const value_type type that meets
///< LegacyRandomAccessIterator requirements
using reverse_iterator = std::reverse_iterator<iterator>; ///< Reverse iterator type
using const_reverse_iterator =
std::reverse_iterator<const_iterator>; ///< Reverse const iterator type
using difference_type = decltype(std::distance(
std::declval<iterator>(),
std::declval<iterator>())); ///< Signed integer type (usually std::ptrdiff_t)
private:
storage_container_type container; ///< element storage
size_type currentSize{0}; ///< current element count
public:
/// Default constructor. Constructs an empty container with a default-constructed
/// allocator
constexpr static_vector() noexcept = default;
/**
* @brief Constructs the container with count copies of elements with value value
* @param[in] count the size of the container
* @param[in] value the value to initialize elements of the container with
* @note The method will throw LengthError if count > max_size()
*/
constexpr static_vector(size_type count, const_reference value) : currentSize(count)
{
if (count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
std::uninitialized_fill_n(begin(), count, value);
}
/**
* @brief Constructs the container with count default-inserted instances of T. No copies
* are made
* @param[in] count the size of the container
* @note The method will throw LengthError if count > max_size()
*/
constexpr explicit static_vector(size_type count)
requires(std::is_default_constructible_v<T>)
: currentSize(count)
{
if (count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
std::uninitialized_default_construct_n(begin(), count);
}
/**
* @brief Constructs the container with the contents of the range [first, last)
* @param[in] first start of the range to copy the elements from
* @param[in] last end of the range to copy the elements from
* @note The method will throw LengthError if std::distance(first, last) is negative or,
* greater than max_size()
*/
template <class InputIt> constexpr static_vector(InputIt first, InputIt last)
{
auto dist = std::distance(first, last);
auto count = size_type(dist);
if (dist < 0 || count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
currentSize = count;
std::uninitialized_copy(first, last, begin());
}
/**
* @brief Copy constructor. Constructs the container with the copy of the contents of
* other
* @param[in] other another container to be used as source to initialize the elements of
* the container with
*/
constexpr static_vector(static_vector const &other) : currentSize(other.currentSize)
{
std::uninitialized_copy(other.begin(), other.end(), begin());
}
/**
* @brief Move constructor. Constructs the container with the contents of other using
* move semantics. After the move, other is guaranteed to be empty()
* @param[in] other another container to be used as source to initialize the elements of
* the container with
*/
constexpr static_vector(static_vector &&other) noexcept
requires(std::movable<T>)
: currentSize(std::move(other.currentSize))
{
std::uninitialized_move(other.begin(), other.end(), begin());
}
/**
* @brief Constructs the container with the contents of the initializer list init
* @param[in] init initializer list to initialize the elements of the container with
* @note The method will throw LengthError if init.size() > max_size()
*/
constexpr static_vector(std::initializer_list<value_type> init)
: static_vector(init.begin(), init.end())
{
}
/**
* @brief Destructs the vector. The destructors of the elements are called and the used
* storage is deallocated.
* @note if the elements are pointers, the pointed-to objects are not destroyed.
*/
constexpr ~static_vector() noexcept
{
if constexpr (!std::is_trivially_destructible_v<value_type>) { clear(); }
}
/**
* @brief Copy assignment operator. Replaces the contents with a copy of the contents of
* other
* @param[in] other another container to use as data source
* @return *this
*/
constexpr static_vector &operator=(static_vector const &other)
{
if (&other != this) {
if (!empty()) { clear(); }
currentSize = other.currentSize;
std::uninitialized_copy(other.begin(), other.end(), begin());
}
return *this;
}
/**
* @brief Move assignment operator. Replaces the contents with those of other using move
* semantics (i.e. the data in other is moved from other into this container). other is
* in a valid but unspecified state afterwards
* @param[in] other another container to use as data source
* @return *this
*/
constexpr static_vector &operator=(static_vector &&other)
{
if (&other != this) {
if (!empty()) { clear(); }
currentSize = std::move(other.currentSize);
std::uninitialized_move(other.begin(), other.end(), begin());
}
return *this;
}
/**
* @brief Replaces the contents with those identified by initializer list ilist
* @param[in] ilist initializer list to use as data source
* @return *this
* @note The method will throw LengthError if ilist.size() > max_size()
*/
constexpr static_vector &operator=(std::initializer_list<value_type> ilist)
{
if (!empty()) { clear(); }
if (ilist.size() > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
currentSize = ilist.size();
std::uninitialized_copy(ilist.begin(), ilist.end(), begin());
return *this;
}
/**
* @brief Replaces the contents with count copies of value value
* @param[in] count the new size of the container
* @param[in] value the value to initialize elements of the container with
* @note The method will throw LengthError if count > max_size()
*/
constexpr void assign(size_type count, T const &value)
{
if (!empty()) { clear(); }
if (count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
std::uninitialized_fill_n(begin(), count, value);
currentSize = count;
}
/**
* @brief Replaces the contents with copies of those in the range [first, last)
* @param[in] first start of the range to copy the elements from
* @param[in] last end of the range to copy the elements from
* @note The behavior is undefined if either argument is an iterator into *this.
* @note The method will throw LengthError if std::distance(first, last) is negative or,
* greater than max_size()
*/
template <class InputIt> constexpr void assign(InputIt first, InputIt last)
{
if (!empty()) { clear(); }
auto dist = std::distance(first, last);
auto count = size_type(dist);
if (dist < 0 || count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
currentSize = count;
std::uninitialized_copy(first, last, begin());
}
/**
* @brief Replaces the contents with the elements from the initializer list ilist
* @param[in] ilist initializer list to copy the values from
* @note The method will throw LengthError if ilist.size() > max_size()
*/
constexpr void assign(std::initializer_list<T> ilist)
{
assign(ilist.begin(), ilist.end());
}
/**
* @brief Returns a reference to the element at specified location pos, with bounds
* checking.
* @param[in] pos position of the element to return
* @return Reference to the requested element
* @note If pos is not within the range of the container, an OutOfRangeError is thrown.
*/
constexpr reference at(size_type pos)
{
if (pos >= size()) {
ECPP_STATIC_VECTOR_THROW(std::out_of_range("Index out of bounds"));
}
return (*this)[pos];
}
/**
* @brief Returns a reference to the element at specified location pos, with bounds
* checking.
* @param[in] pos position of the element to return
* @return Reference to the requested element
* @note If pos is not within the range of the container, an OutOfRangeError is thrown.
*/
constexpr const_reference at(size_type pos) const
{
if (pos >= size()) {
ECPP_STATIC_VECTOR_THROW(std::out_of_range("Index out of bounds"));
}
return (*this)[pos];
}
/**
* @brief Returns a reference to the element at specified location pos. No bounds
* checking is performed
* @param[in] pos position of the element to return
* @return Reference to the requested element
* @note Accessing a nonexistent element through this operator is undefined behavior
*/
constexpr reference operator[](size_type pos) noexcept { return *(begin() + pos); }
/**
* @brief Returns a reference to the element at specified location pos. No bounds
* checking is performed
* @param[in] pos position of the element to return
* @return Reference to the requested element
* @note Accessing a nonexistent element through this operator is undefined behavior
*/
constexpr const_reference operator[](size_type pos) const noexcept
{
return *(begin() + pos);
}
/**
* @brief Returns a reference to the first element in the container
* @return Reference to the first element
* @note Calling front on an empty container is undefined
*/
constexpr reference front() noexcept { return *begin(); }
/**
* @brief Returns a reference to the first element in the container
* @return Reference to the first element
* @note Calling front on an empty container is undefined
*/
constexpr const_reference front() const noexcept { return *begin(); }
/**
* @brief Returns a reference to the last element in the container
* @return Reference to the last element
* @note Calling back on an empty container is undefined
*/
constexpr reference back() noexcept { return *(end() - 1); }
/**
* @brief Returns a reference to the last element in the container
* @return Reference to the last element
* @note Calling back on an empty container is undefined
*/
constexpr const_reference back() const noexcept { return *(end() - 1); }
/**
* @brief Returns pointer to the underlying array serving as element storage
* @return Pointer to the underlying element storage. For non-empty containers, the
* returned pointer compares equal to the address of the first element
* @note The pointer is such that range [data(); data() + size()) is always a valid
* range, even if the container is empty (data() is not dereferenceable in that case)
*/
constexpr pointer data() noexcept
{
return std::launder(reinterpret_cast<pointer>(container.begin()));
}
/**
* @brief Returns pointer to the underlying array serving as element storage
* @return Pointer to the underlying element storage. For non-empty containers, the
* returned pointer compares equal to the address of the first element
* @note The pointer is such that range [data(); data() + size()) is always a valid
* range, even if the container is empty (data() is not dereferenceable in that case)
*/
constexpr const_pointer data() const noexcept
{
return std::launder(reinterpret_cast<const_pointer>(container.begin()));
}
/**
* @brief Returns an iterator to the first element of the vector.
* If the vector is empty, the returned iterator will be equal to end()
* @return Iterator to the first element
*/
constexpr iterator begin() noexcept { return data(); }
/**
* @brief Returns an iterator to the first element of the vector.
* If the vector is empty, the returned iterator will be equal to end()
* @return Iterator to the first element
*/
constexpr const_iterator begin() const noexcept { return data(); }
/**
* @brief Returns an iterator to the first element of the vector.
* If the vector is empty, the returned iterator will be equal to end()
* @return Iterator to the first element
*/
constexpr const_iterator cbegin() const noexcept { return data(); }
/**
* @brief Returns an iterator to the element following the last element of the vector
* @return Iterator to the element following the last element
* @note This element acts as a placeholder; attempting to access it results in
* undefined behavior.
*/
constexpr iterator end() noexcept { return begin() + currentSize; }
/**
* @brief Returns an iterator to the element following the last element of the vector
* @return Iterator to the element following the last element
* @note This element acts as a placeholder; attempting to access it results in
* undefined behavior.
*/
constexpr const_iterator end() const noexcept { return begin() + currentSize; }
/**
* @brief Returns an iterator to the element following the last element of the vector
* @return Iterator to the element following the last element
* @note This element acts as a placeholder; attempting to access it results in
* undefined behavior.
*/
constexpr const_iterator cend() const noexcept { return begin() + currentSize; }
/**
* @brief Returns a reverse iterator to the first element of the reversed vector.
* It corresponds to the last element of the non-reversed vector. If the vector is
* empty, the returned iterator is equal to rend().
* @return Reverse iterator to the first element
*/
constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
/**
* @brief Returns a reverse iterator to the first element of the reversed vector.
* It corresponds to the last element of the non-reversed vector. If the vector is
* empty, the returned iterator is equal to rend().
* @return Reverse iterator to the first element
*/
constexpr const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
/**
* @brief Returns a reverse iterator to the first element of the reversed vector.
* It corresponds to the last element of the non-reversed vector. If the vector is
* empty, the returned iterator is equal to rend().
* @return Reverse iterator to the first element
*/
constexpr const_reverse_iterator crbegin() const noexcept
{
return const_reverse_iterator(cend());
}
/**
* @brief Returns a reverse iterator to the element following the last element of the
* reversed vector. It corresponds to the element preceding the first element of the
* non-reversed vector
* @return Reverse iterator to the element following the last element
* @note This element acts as a placeholder, attempting to access it results in
* undefined behavior
*/
constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
/**
* @brief Returns a reverse iterator to the element following the last element of the
* reversed vector. It corresponds to the element preceding the first element of the
* non-reversed vector
* @return Reverse iterator to the element following the last element
* @note This element acts as a placeholder, attempting to access it results in
* undefined behavior
*/
constexpr const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
/**
* @brief Returns a reverse iterator to the element following the last element of the
* reversed vector. It corresponds to the element preceding the first element of the
* non-reversed vector
* @return Reverse iterator to the element following the last element
* @note This element acts as a placeholder, attempting to access it results in
* undefined behavior
*/
constexpr const_reverse_iterator crend() const noexcept
{
return const_reverse_iterator(begin());
}
/**
* @brief Checks if the container has no elements, i.e. whether begin() == end()
* @return true if the container is empty, false otherwise
*/
constexpr bool empty() const noexcept { return 0 == size(); }
/**
* @brief Returns the number of elements in the container, i.e. std::distance(begin(),
* end())
* @return The number of elements in the container
*/
constexpr size_type size() const noexcept { return currentSize; }
/**
* @brief Returns the maximum number of elements the container is able to hold due to
* system or library implementation limitations i.e. N
* @return Maximum number of elements
*/
constexpr size_type max_size() const noexcept { return container.max_size(); }
/**
* @brief Increase the capacity of the vector to a value that's greater or equal to
* new_cap. Because the static_vector uses static storage, when new_cap <= max_size()
* the method does nothing.
* @param[in] new_cap new capacity of the vector
* @note For compatibility purposes, The method throws LengthError when new_cap >
* max_size
*/
constexpr void reserve(size_type new_cap)
{
if (new_cap > max_size()) {
ECPP_STATIC_VECTOR_THROW(std::length_error("Reserve exceeds max_size"));
}
}
/**
* @brief Returns the number of elements that the container has currently allocated
* space for
* @return Capacity of the currently allocated storage
*/
constexpr size_type capacity() const noexcept { return max_size(); }
/**
* @brief Requests the removal of unused capacity.
* Because the static_vector uses static storage this method does nothing
*/
constexpr void shrink_to_fit() noexcept
{ /* Do nothing, as the underlying storage cannot be shrinked */ }
/**
* @brief Erases all elements from the container. After this call, size() returns zero.
* Invalidates any references, pointers, or iterators referring to contained elements.
* Any past-the-end iterators are also invalidated. Leaves the capacity() of the vector
* unchanged
*/
constexpr void clear() noexcept
{
std::for_each(begin(), end(), [](reference x) { std::destroy_at(&x); });
currentSize = 0;
}
/**
* @brief Inserts value before pos
* @param[in] pos iterator before which the content will be inserted. pos may be the
* end() iterator
* @param[in] value element value to insert
* @return Iterator pointing to the inserted value
* @note The method will throw LengthError if size() == max_size()
*/
constexpr iterator insert(const_iterator pos, T const &value)
{
if (size() + 1 > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
auto position = begin() + std::distance(cbegin(), pos);
// Move last element right by one (end() + 1 will become new end(), so uninitialized
// memory need to be initialized)
if (position != end()) {
std::uninitialized_move(end() - 1, end(), end());
// Move [pos, end() -1) to [pos + 1, end())
std::move_backward(position, end() - 1, end());
}
std::construct_at(position, value);
++currentSize;
return position;
}
/**
* @brief Inserts value before pos
* @param[in] pos iterator before which the content will be inserted. pos may be the
* end() iterator
* @param[in] value element value to insert
* @return Iterator pointing to the inserted value
* @note The method will throw LengthError if size() == max_size()
*/
constexpr iterator insert(const_iterator pos, T &&value)
{
if (size() + 1 > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
auto position = begin() + std::distance(cbegin(), pos);
// Move last element right by one (end() + 1 will become new end(), so uninitialized
// memory need to be initialized)
if (position != end()) {
std::uninitialized_move(end() - 1, end(), end());
// Move [pos, end() -1) to [pos + 1, end())
std::move_backward(position, end() - 1, end());
}
std::construct_at(position, std::move(value));
++currentSize;
return position;
}
/**
* @brief Inserts count copies of the value before pos
* @param[in] pos iterator before which the content will be inserted. pos may be the
* end() iterator
* @param[in] count number of copies to be inserted
* @param[in] value element value to insert
* @return Iterator pointing to the first element inserted, or pos if count==0
* @note The method will throw LengthError if size() + count > max_size()
*/
constexpr iterator insert(const_iterator pos, size_type count, T const &value)
{
if (size() + count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
auto position = begin() + std::distance(cbegin(), pos);
if (count > 0) {
if (position != end()) {
auto existingElementsToMove = std::distance(
position, end()); // Negative distance in this context is UB (position
// must be in range [begin(), end()])
auto toCopyAtTheEnd = (count >= existingElementsToMove)
? (count - existingElementsToMove)
: 0;
auto toMoveAssign = (count >= existingElementsToMove)
? 0
: (existingElementsToMove - count);
// uninitialized_copy last toCopyAtTheEnd elements of input range at the
// end(), as they don't overlap with existing data
auto lastElem = std::uninitialized_fill_n(end(), toCopyAtTheEnd, value);
// Move data from [pos end()) after last element of the vector. If size of
// the input range is smaller than number of elements to move
std::uninitialized_move(position + toMoveAssign, end(), lastElem);
std::move_backward(position, position + toMoveAssign, end());
std::fill(position, position + count - toCopyAtTheEnd, value);
} else {
std::uninitialized_fill_n(position, count, value);
}
currentSize += count;
}
return position;
}
/**
* @brief Inserts elements from range [first, last) before pos
* @param[in] pos iterator before which the content will be inserted. pos may be the
* end() iterator
* @param[in] first start of the range of elements to insert, can't be iterators into
* container for which insert is called
* @param[in] last end of the range of elements to insert, can't be iterators into
* container for which insert is called
* @return Iterator pointing to the first element inserted, or pos if first==last
* @note The method will throw LengthError if size() + count > max_size()
*/
template <class InputIt>
constexpr iterator insert(const_iterator pos, InputIt first, InputIt last)
{
auto dist = std::distance(first, last);
auto count = size_type(dist);
if (dist < 0 || (size() + count > max_size())) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
auto position = begin() + std::distance(cbegin(), pos);
if (position != end()) {
auto existingElementsToMove = std::distance(
position, end()); // Negative distance in this context is UB (position must
// be in range [begin(), end()])
auto toCopyAtTheEnd =
(dist >= existingElementsToMove) ? (dist - existingElementsToMove) : 0;
auto toMoveAssign =
(dist >= existingElementsToMove) ? 0 : (existingElementsToMove - dist);
// uninitialized_copy last toCopyAtTheEnd elements of input range at the end(),
// as they don't overlap with existing data
auto lastElem = std::uninitialized_copy(last - toCopyAtTheEnd, last, end());
// Move data from [pos end()) after last element of the vector. If size of the
// input range is smaller than number of elements to move
//
std::uninitialized_move(position + toMoveAssign, end(), lastElem);
std::move_backward(position, position + toMoveAssign, end());
std::copy(first, last - toCopyAtTheEnd, position);
} else {
std::uninitialized_copy(first, last, end());
}
currentSize += count;
return position;
}
/**
* @brief Inserts elements from initializer list ilist before pos
* @param[in] pos iterator before which the content will be inserted. pos may be the
* end() iterator
* @param[in] ilist initializer list to insert the values from
* @return Iterator pointing to the first element inserted, or pos if ilist is empty
* @note The method will throw LengthError if size() + ilist.size() > max_size()
*/
constexpr iterator insert(const_iterator pos, std::initializer_list<T> ilist)
{
return insert(pos, ilist.begin(), ilist.end());
}
/**
* @brief Inserts a new element into the container directly before pos
* @param pos iterator before which the new element will be constructed
* @param args arguments to forward to the constructor of the element
* @return Iterator pointing to the emplaced element
* @note The method will throw LengthError if size() == max_size()
*/
template <class... Args> constexpr iterator emplace(const_iterator pos, Args &&...args)
{
if (size() + 1 > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
auto const position = begin() + std::distance(cbegin(), pos);
// Move last element right by one (end() + 1 will become new end(), so uninitialized
// memory need to be initialized)
if (position != end()) {
std::uninitialized_move(end() - 1, end(), end());
// Move [pos, end() -1) to [pos + 1, end())
std::move_backward(position, end() - 1, end());
} else {
std::construct_at(position, std::forward<Args>(args)...);
}
++currentSize;
return position;
}
/**
* @brief Removes the element at pos
* @param[in] pos to the element to remove
* @return Iterator following the last removed element
* @note If pos refers to the last element, then the end() iterator is returned
*/
constexpr iterator erase(const_iterator pos)
{
auto index = std::distance(cbegin(), pos);
std::move(begin() + index + 1, end(), begin() + index);
// Elements were moved left, now destroy the last element
currentSize--;
// Now, end() points to previous last element
std::destroy_at(end());
return begin() + index;
}
/**
* @brief Removes the elements in the range [first, last)
* @param[in] first start of range of elements to remove
* @param[in] last end of range of elements to remove
* @return Iterator following the last removed element
* @note If last==end() prior to removal, then the updated end() iterator is returned.
* @note If [first, last) is an empty range, then last is returned
*/
constexpr iterator erase(const_iterator first, const_iterator last)
{
auto last_ = begin() + std::distance(cbegin(), last);
if (first >= last) return last_;
auto first_ = begin() + std::distance(cbegin(), first);
auto toErase = std::distance(first, last); // Guaranteed to be > 0
auto lastValid = std::move(first_ + toErase, end(), first_);
std::for_each(lastValid, end(), [](reference x) { std::destroy_at(&x); });
currentSize -= size_type(toErase);
return first_;
}
/**
* @brief Appends the given element value to the end of the container. The new element
* is initialized as a copy of value
* @param[in] value the value of the element to append
* @note The method will throw LengthError if size() == max_size()
*/
constexpr void push_back(T const &value)
{
if (size() + 1 > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
std::construct_at(end(), value);
++currentSize;
}
/**
* @brief Appends the given element value to the end of the container; value is moved
* into the new element
* @param[in] value the value of the element to append
* @note The method will throw LengthError if size() == max_size()
*/
constexpr void push_back(T &&value)
{
if (size() + 1 > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
std::construct_at(end(), std::move(value));
++currentSize;
}
/**
* @brief Appends a new element to the end of the container. The element is constructed
* in-place.
* @param[in] args arguments to forward to the constructor of the element
* @return A reference to the inserted element
* @note The method will throw LengthError if size() == max_size()
*/
template <class... Args> constexpr reference emplace_back(Args &&...args)
{
if (size() + 1 > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
}
auto const position = end();
std::construct_at(position, std::forward<Args>(args)...);
++currentSize;
return *position;
}
/**
* @brief Removes the last element of the container
* Iterators and references to the last element, as well as the end() iterator, are
* invalidated
* @note Calling pop_back on an empty container results in undefined behavior.
*/
constexpr void pop_back()
{
currentSize--;
std::destroy_at(end());
}
/**
* @brief Resizes the container to contain count elements
* If the current size is greater than count, the container is reduced to its first
* count elements. If the current size is less than count, additional default-inserted
* elements are appended
* @param[in] count new size of the container
* @note The method will throw LengthError if count > max_size()
*/
constexpr void resize(size_type count)
{
if (count < size()) {
erase(begin() + count, end());
} else if (count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
} else {
auto toAdd = count - size();
for (size_type i = 0; i != toAdd; ++i) { emplace_back(); }
}
}
/**
* @brief Resizes the container to contain count elements
* If the current size is greater than count, the container is reduced to its first
* count elements. If the current size is less than count, additional copies of value
* are appended.
* @param[in] count new size of the container
* @param[in] value the value to initialize the new elements with
* @note The method will throw LengthError if count > max_size()
*/
constexpr void resize(size_type count, value_type const &value)
{
if (count < size()) {
erase(begin() + count, end());
} else if (count > max_size()) {
ECPP_STATIC_VECTOR_THROW(
std::length_error("Insertion would exceed static_vector capacity"));
} else {
auto toAdd = count - size();
for (size_type i = 0; i != toAdd; ++i) { push_back(value); }
}
}
/**
* Exchanges the contents of the container with those of other. Swaps content of
* underlying storage. All iterators and references are invalidated
* @param[in] other reference to static_vector instance to swap with
*/
constexpr void swap(static_vector &other) noexcept
{
container.swap(other.container);
std::swap(currentSize, other.currentSize);
}
};
/**
* @brief Checks if the contents of lhs and rhs are equal, that is, they have the same number of
* elements and each element in lhs compares equal with the element in rhs at the same position
* @param[in] lhs first of vectors whose contents to compare
* @param[in] rhs second of vectors whose contents to compare
* @return true if the contents of the vectors are equal, false otherwise
*/
template <class T, std::size_t N>
constexpr bool operator==(static_vector<T, N> const &lhs, static_vector<T, N> const &rhs)
{
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
/**
* @brief Compares the contents of lhs and rhs lexicographically.
* The comparison is performed as if by calling std::lexicographical_compare_three_way on two
* vectors with a function object performing synthesized three-way comparison. The return type
* is same as the result type of synthesized three-way comparison
* @param[in] lhs first of vectors whose contents to compare
* @param[in] rhs second of vectors whose contents to compare
* @return The relative order of the first pair of non-equivalent elements in lhs and rhs if
* there are such elements, lhs.size() <=> rhs.size() otherwise
*/
template <class T, std::size_t N>
constexpr auto operator<=>(static_vector<T, N> const &lhs, static_vector<T, N> const &rhs)
{
return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(),
rhs.end());
}
/**
* @brief Specializes the std::swap algorithm for static_vector.
* Swaps the contents of lhs and rhs. Calls lhs.swap(rhs).
* @param[in,out] lhs container whose contents to swap
* @param[in,out] rhs containers whose contents to swap
*/
template <typename T, std::size_t N>
constexpr void swap(static_vector<T, N> &lhs, static_vector<T, N> &rhs) noexcept
{
lhs.swap(rhs);
}
/**
* @brief Erases all elements that compare equal to value from the container
* @param[in, out] c container from which to erase
* @param[in] value value to be removed
* @return The number of erased elements
*/
template <typename T, std::size_t N, typename U>
constexpr typename static_vector<T, N>::size_type erase(static_vector<T, N> &c, U const &value)
{
auto oldSize = c.size();
auto end = std::remove(c.begin(), c.end(), value);
c.erase(end, c.end());
return c.size() - oldSize;
}
/**
* @brief Erases all elements that compare equal to value from the container
* @param[in, out] c container from which to erase
* @param[in] pred unary predicate which returns true if the element should be erased
* @return The number of erased elements
*/
template <typename T, std::size_t N, typename Pred>
constexpr typename static_vector<T, N>::size_type erase_if(static_vector<T, N> &c, Pred pred)
{
auto oldSize = c.size();
auto end = std::remove_if(c.begin(), c.end(), pred);
c.erase(end, c.end());
return c.size() - oldSize;
}
} // namespace ecpp
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,415 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_GROWTH_POLICY_H
#define TSL_ROBIN_GROWTH_POLICY_H
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits>
#include <ratio>
#include <stdexcept>
// A change of the major version indicates an API and/or ABI break (change of
// in-memory layout of the data structure)
#define TSL_RH_VERSION_MAJOR 1
// A change of the minor version indicates the addition of a feature without
// impact on the API/ABI
#define TSL_RH_VERSION_MINOR 4
// A change of the patch version indicates a bugfix without additional
// functionality
#define TSL_RH_VERSION_PATCH 0
#ifdef TSL_DEBUG
#define tsl_rh_assert(expr) assert(expr)
#else
#define tsl_rh_assert(expr) (static_cast<void>(0))
#endif
/**
* If exceptions are enabled, throw the exception passed in parameter, otherwise
* call std::terminate.
*/
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
(defined(_MSC_VER) && defined(_CPPUNWIND))) && \
!defined(TSL_NO_EXCEPTIONS)
#define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
#else
#define TSL_RH_NO_EXCEPTIONS
#ifdef TSL_DEBUG
#include <iostream>
#define TSL_RH_THROW_OR_TERMINATE(ex, msg) \
do { \
std::cerr << msg << std::endl; \
std::terminate(); \
} while (0)
#else
#define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
#endif
#endif
#if defined(__GNUC__) || defined(__clang__)
#define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
#else
#define TSL_RH_LIKELY(exp) (exp)
#endif
#define TSL_RH_UNUSED(x) static_cast<void>(x)
namespace tsl {
namespace rh {
/**
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a
* power of two. It allows the table to use a mask operation instead of a modulo
* operation to map a hash to a bucket.
*
* GrowthFactor must be a power of two >= 2.
*/
template <std::size_t GrowthFactor>
class power_of_two_growth_policy {
public:
/**
* Called on the hash table creation and on rehash. The number of buckets for
* the table is passed in parameter. This number is a minimum, the policy may
* update this value with a higher value if needed (but not lower).
*
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy
* creation and bucket_for_hash must always return 0 in this case.
*/
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
if (min_bucket_count_in_out > max_bucket_count()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
if (min_bucket_count_in_out > 0) {
min_bucket_count_in_out =
round_up_to_power_of_two(min_bucket_count_in_out);
m_mask = min_bucket_count_in_out - 1;
} else {
m_mask = 0;
}
}
/**
* Return the bucket [0, bucket_count()) to which the hash belongs.
* If bucket_count() is 0, it must always return 0.
*/
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash & m_mask;
}
/**
* Return the number of buckets that should be used on next growth.
*/
std::size_t next_bucket_count() const {
if ((m_mask + 1) > max_bucket_count() / GrowthFactor) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
return (m_mask + 1) * GrowthFactor;
}
/**
* Return the maximum number of buckets supported by the policy.
*/
std::size_t max_bucket_count() const {
// Largest power of two.
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
}
/**
* Reset the growth policy as if it was created with a bucket count of 0.
* After a clear, the policy must always return 0 when bucket_for_hash is
* called.
*/
void clear() noexcept { m_mask = 0; }
private:
static std::size_t round_up_to_power_of_two(std::size_t value) {
if (is_power_of_two(value)) {
return value;
}
if (value == 0) {
return 1;
}
--value;
for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
value |= value >> i;
}
return value + 1;
}
static constexpr bool is_power_of_two(std::size_t value) {
return value != 0 && (value & (value - 1)) == 0;
}
protected:
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
"GrowthFactor must be a power of two >= 2.");
std::size_t m_mask;
};
/**
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo
* to map a hash to a bucket. Slower but it can be useful if you want a slower
* growth.
*/
template <class GrowthFactor = std::ratio<3, 2>>
class mod_growth_policy {
public:
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
if (min_bucket_count_in_out > max_bucket_count()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
if (min_bucket_count_in_out > 0) {
m_mod = min_bucket_count_in_out;
} else {
m_mod = 1;
}
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash % m_mod;
}
std::size_t next_bucket_count() const {
if (m_mod == max_bucket_count()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
const double next_bucket_count =
std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
if (!std::isnormal(next_bucket_count)) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
if (next_bucket_count > double(max_bucket_count())) {
return max_bucket_count();
} else {
return std::size_t(next_bucket_count);
}
}
std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; }
void clear() noexcept { m_mod = 1; }
private:
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR =
1.0 * GrowthFactor::num / GrowthFactor::den;
static const std::size_t MAX_BUCKET_COUNT =
std::size_t(double(std::numeric_limits<std::size_t>::max() /
REHASH_SIZE_MULTIPLICATION_FACTOR));
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
"Growth factor should be >= 1.1.");
std::size_t m_mod;
};
namespace detail {
#if SIZE_MAX >= ULLONG_MAX
#define TSL_RH_NB_PRIMES 51
#elif SIZE_MAX >= ULONG_MAX
#define TSL_RH_NB_PRIMES 40
#else
#define TSL_RH_NB_PRIMES 23
#endif
inline constexpr std::array<std::size_t, TSL_RH_NB_PRIMES> PRIMES = {{
1u,
5u,
17u,
29u,
37u,
53u,
67u,
79u,
97u,
131u,
193u,
257u,
389u,
521u,
769u,
1031u,
1543u,
2053u,
3079u,
6151u,
12289u,
24593u,
49157u,
#if SIZE_MAX >= ULONG_MAX
98317ul,
196613ul,
393241ul,
786433ul,
1572869ul,
3145739ul,
6291469ul,
12582917ul,
25165843ul,
50331653ul,
100663319ul,
201326611ul,
402653189ul,
805306457ul,
1610612741ul,
3221225473ul,
4294967291ul,
#endif
#if SIZE_MAX >= ULLONG_MAX
6442450939ull,
12884901893ull,
25769803751ull,
51539607551ull,
103079215111ull,
206158430209ull,
412316860441ull,
824633720831ull,
1649267441651ull,
3298534883309ull,
6597069766657ull,
#endif
}};
template <unsigned int IPrime>
static constexpr std::size_t mod(std::size_t hash) {
return hash % PRIMES[IPrime];
}
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for
// faster modulo as the compiler can optimize the modulo code better with a
// constant known at the compilation.
inline constexpr std::array<std::size_t (*)(std::size_t), TSL_RH_NB_PRIMES>
MOD_PRIME = {{
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>,
&mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>,
&mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>,
&mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>,
#if SIZE_MAX >= ULONG_MAX
&mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>,
&mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>,
&mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>,
#endif
#if SIZE_MAX >= ULLONG_MAX
&mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>,
&mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>,
#endif
}};
} // namespace detail
/**
* Grow the hash table by using prime numbers as bucket count. Slower than
* tsl::rh::power_of_two_growth_policy in general but will probably distribute
* the values around better in the buckets with a poor hash function.
*
* To allow the compiler to optimize the modulo operation, a lookup table is
* used with constant primes numbers.
*
* With a switch the code would look like:
* \code
* switch(iprime) { // iprime is the current prime of the hash table
* case 0: hash % 5ul;
* break;
* case 1: hash % 17ul;
* break;
* case 2: hash % 29ul;
* break;
* ...
* }
* \endcode
*
* Due to the constant variable in the modulo the compiler is able to optimize
* the operation by a series of multiplications, substractions and shifts.
*
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34)
* * 5' in a 64 bits environment.
*/
class prime_growth_policy {
public:
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
auto it_prime = std::lower_bound(
detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out);
if (it_prime == detail::PRIMES.end()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
m_iprime = static_cast<unsigned int>(
std::distance(detail::PRIMES.begin(), it_prime));
if (min_bucket_count_in_out > 0) {
min_bucket_count_in_out = *it_prime;
} else {
min_bucket_count_in_out = 0;
}
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return detail::MOD_PRIME[m_iprime](hash);
}
std::size_t next_bucket_count() const {
if (m_iprime + 1 >= detail::PRIMES.size()) {
TSL_RH_THROW_OR_TERMINATE(std::length_error,
"The hash table exceeds its maximum size.");
}
return detail::PRIMES[m_iprime + 1];
}
std::size_t max_bucket_count() const { return detail::PRIMES.back(); }
void clear() noexcept { m_iprime = 0; }
private:
unsigned int m_iprime;
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >=
detail::PRIMES.size(),
"The type of m_iprime is not big enough.");
};
} // namespace rh
} // namespace tsl
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,815 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_MAP_H
#define TSL_ROBIN_MAP_H
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "robin_hash.h"
namespace tsl {
/**
* Implementation of a hash map using open-addressing and the robin hood hashing
* algorithm with backward shift deletion.
*
* For operations modifying the hash map (insert, erase, rehash, ...), the
* strong exception guarantee is only guaranteed when the expression
* `std::is_nothrow_swappable<std::pair<Key, T>>::value &&
* std::is_nothrow_move_constructible<std::pair<Key, T>>::value` is true,
* otherwise if an exception is thrown during the swap or the move, the hash map
* may end up in a undefined state. Per the standard a `Key` or `T` with a
* noexcept copy constructor and no move constructor also satisfies the
* `std::is_nothrow_move_constructible<std::pair<Key, T>>::value` criterion (and
* will thus guarantee the strong exception for the map).
*
* When `StoreHash` is true, 32 bits of the hash are stored alongside the
* values. It can improve the performance during lookups if the `KeyEqual`
* function takes time (if it engenders a cache-miss for example) as we then
* compare the stored hashes before comparing the keys. When
* `tsl::rh::power_of_two_growth_policy` is used as `GrowthPolicy`, it may also
* speed-up the rehash process as we can avoid to recalculate the hash. When it
* is detected that storing the hash will not incur any memory penalty due to
* alignment (i.e. `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType,
* true>) == sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`)
* and `tsl::rh::power_of_two_growth_policy` is used, the hash will be stored
* even if `StoreHash` is false so that we can speed-up the rehash (but it will
* not be used on lookups unless `StoreHash` is true).
*
* `GrowthPolicy` defines how the map grows and consequently how a hash value is
* mapped to a bucket. By default the map uses
* `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of
* buckets to a power of two and uses a mask to map the hash to a bucket instead
* of the slow modulo. Other growth policies are available and you may define
* your own growth policy, check `tsl::rh::power_of_two_growth_policy` for the
* interface.
*
* `std::pair<Key, T>` must be swappable.
*
* `Key` and `T` must be copy and/or move constructible.
*
* If the destructor of `Key` or `T` throws an exception, the behaviour of the
* class is undefined.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators.
* - insert, emplace, emplace_hint, operator[]: if there is an effective
* insert, invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template <class Key, class T, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
bool StoreHash = false,
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
class robin_map {
private:
template <typename U>
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(
const std::pair<Key, T>& key_value) const noexcept {
return key_value.first;
}
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.first;
}
};
class ValueSelect {
public:
using value_type = T;
const value_type& operator()(
const std::pair<Key, T>& key_value) const noexcept {
return key_value.second;
}
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
return key_value.second;
}
};
using ht = detail_robin_hash::robin_hash<std::pair<Key, T>, KeySelect,
ValueSelect, Hash, KeyEqual,
Allocator, StoreHash, GrowthPolicy>;
public:
using key_type = typename ht::key_type;
using mapped_type = T;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
public:
/*
* Constructors
*/
robin_map() : robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {}
explicit robin_map(size_type bucket_count, const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: m_ht(bucket_count, hash, equal, alloc) {}
robin_map(size_type bucket_count, const Allocator& alloc)
: robin_map(bucket_count, Hash(), KeyEqual(), alloc) {}
robin_map(size_type bucket_count, const Hash& hash, const Allocator& alloc)
: robin_map(bucket_count, hash, KeyEqual(), alloc) {}
explicit robin_map(const Allocator& alloc)
: robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {}
template <class InputIt>
robin_map(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: robin_map(bucket_count, hash, equal, alloc) {
insert(first, last);
}
template <class InputIt>
robin_map(InputIt first, InputIt last, size_type bucket_count,
const Allocator& alloc)
: robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) {}
template <class InputIt>
robin_map(InputIt first, InputIt last, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: robin_map(first, last, bucket_count, hash, KeyEqual(), alloc) {}
robin_map(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc) {}
robin_map(std::initializer_list<value_type> init, size_type bucket_count,
const Allocator& alloc)
: robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(),
alloc) {}
robin_map(std::initializer_list<value_type> init, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(),
alloc) {}
robin_map& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
template <class P, typename std::enable_if<std::is_constructible<
value_type, P&&>::value>::type* = nullptr>
std::pair<iterator, bool> insert(P&& value) {
return m_ht.emplace(std::forward<P>(value));
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert_hint(hint, value);
}
template <class P, typename std::enable_if<std::is_constructible<
value_type, P&&>::value>::type* = nullptr>
iterator insert(const_iterator hint, P&& value) {
return m_ht.emplace_hint(hint, std::forward<P>(value));
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert_hint(hint, std::move(value));
}
template <class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
template <class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
return m_ht.insert_or_assign(k, std::forward<M>(obj));
}
template <class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
}
template <class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
}
template <class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
}
/**
* Due to the way elements are stored, emplace will need to move or copy the
* key-value once. The method is equivalent to
* insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy
* the key-value once. The method is equivalent to insert(hint,
* value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
template <class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
return m_ht.try_emplace(k, std::forward<Args>(args)...);
}
template <class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template <class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
}
template <class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
return m_ht.try_emplace_hint(hint, std::move(k),
std::forward<Args>(args)...);
}
iterator erase(iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) {
return m_ht.erase(first, last);
}
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* Erase the element at position 'pos'. In contrast to the regular erase()
* function, erase_fast() does not return an iterator. This allows it to be
* faster especially in hash tables with a low load factor, where finding the
* next nonempty bucket would be costly.
*/
void erase_fast(iterator pos) { return m_ht.erase_fast(pos); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) {
return m_ht.erase(key);
}
/**
* @copydoc erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup to the value if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(robin_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
T& at(const Key& key) { return m_ht.at(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
T& at(const Key& key, std::size_t precalculated_hash) {
return m_ht.at(key, precalculated_hash);
}
const T& at(const Key& key) const { return m_ht.at(key); }
/**
* @copydoc at(const Key& key, std::size_t precalculated_hash)
*/
const T& at(const Key& key, std::size_t precalculated_hash) const {
return m_ht.at(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key) {
return m_ht.at(key);
}
/**
* @copydoc at(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
T& at(const K& key, std::size_t precalculated_hash) {
return m_ht.at(key, precalculated_hash);
}
/**
* @copydoc at(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key) const {
return m_ht.at(key);
}
/**
* @copydoc at(const K& key, std::size_t precalculated_hash)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const T& at(const K& key, std::size_t precalculated_hash) const {
return m_ht.at(key, precalculated_hash);
}
T& operator[](const Key& key) { return m_ht[key]; }
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const {
return m_ht.count(key);
}
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
/**
* @copydoc find(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
bool contains(const Key& key) const { return m_ht.contains(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
bool contains(const Key& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key) const {
return m_ht.contains(key);
}
/**
* @copydoc contains(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) {
return m_ht.equal_range(key);
}
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(
const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(
const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float min_load_factor() const { return m_ht.min_load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
/**
* Set the `min_load_factor` to `ml`. When the `load_factor` of the map goes
* below `min_load_factor` after some erase operations, the map will be
* shrunk when an insertion occurs. The erase method itself never shrinks
* the map.
*
* The default value of `min_load_factor` is 0.0f, the map never shrinks by
* default.
*/
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count_) { m_ht.rehash(count_); }
void reserve(size_type count_) { m_ht.reserve(count_); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
/**
* Serialize the map through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the
* following call:
* - `template<typename U> void operator()(const U& value);` where the types
* `std::int16_t`, `std::uint32_t`, `std::uint64_t`, `float` and
* `std::pair<Key, T>` must be supported for U.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, ...) of the types it serializes in the hands of the `Serializer`
* function object if compatibility is required.
*/
template <class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized map through the `deserializer`
* parameter.
*
* The `deserializer` parameter must be a function object that supports the
* following call:
* - `template<typename U> U operator()();` where the types `std::int16_t`,
* `std::uint32_t`, `std::uint64_t`, `float` and `std::pair<Key, T>` must be
* supported for U.
*
* If the deserialized hash map type is hash compatible with the serialized
* map, the deserialization process can be sped up by setting
* `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and
* GrowthPolicy must behave the same way than the ones used on the serialized
* map and the StoreHash must have the same value. The `std::size_t` must also
* be of the same size as the one on the platform used to serialize the map.
* If these criteria are not met, the behaviour is undefined with
* `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `Key` and `T` of the `robin_map` are
* not the same as the types used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, size of int, ...) of the types it deserializes in the hands of the
* `Deserializer` function object if compatibility is required.
*/
template <class Deserializer>
static robin_map deserialize(Deserializer& deserializer,
bool hash_compatible = false) {
robin_map map(0);
map.m_ht.deserialize(deserializer, hash_compatible);
return map;
}
friend bool operator==(const robin_map& lhs, const robin_map& rhs) {
if (lhs.size() != rhs.size()) {
return false;
}
for (const auto& element_lhs : lhs) {
const auto it_element_rhs = rhs.find(element_lhs.first);
if (it_element_rhs == rhs.cend() ||
element_lhs.second != it_element_rhs->second) {
return false;
}
}
return true;
}
friend bool operator!=(const robin_map& lhs, const robin_map& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(robin_map& lhs, robin_map& rhs) { lhs.swap(rhs); }
private:
ht m_ht;
};
/**
* Same as `tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash,
* tsl::rh::prime_growth_policy>`.
*/
template <class Key, class T, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key, T>>,
bool StoreHash = false>
using robin_pg_map = robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash,
tsl::rh::prime_growth_policy>;
} // end namespace tsl
#endif

View File

@@ -0,0 +1,668 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_SET_H
#define TSL_ROBIN_SET_H
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
#include "robin_hash.h"
namespace tsl {
/**
* Implementation of a hash set using open-addressing and the robin hood hashing
* algorithm with backward shift deletion.
*
* For operations modifying the hash set (insert, erase, rehash, ...), the
* strong exception guarantee is only guaranteed when the expression
* `std::is_nothrow_swappable<Key>::value &&
* std::is_nothrow_move_constructible<Key>::value` is true, otherwise if an
* exception is thrown during the swap or the move, the hash set may end up in a
* undefined state. Per the standard a `Key` with a noexcept copy constructor
* and no move constructor also satisfies the
* `std::is_nothrow_move_constructible<Key>::value` criterion (and will thus
* guarantee the strong exception for the set).
*
* When `StoreHash` is true, 32 bits of the hash are stored alongside the
* values. It can improve the performance during lookups if the `KeyEqual`
* function takes time (or engenders a cache-miss for example) as we then
* compare the stored hashes before comparing the keys. When
* `tsl::rh::power_of_two_growth_policy` is used as `GrowthPolicy`, it may also
* speed-up the rehash process as we can avoid to recalculate the hash. When it
* is detected that storing the hash will not incur any memory penalty due to
* alignment (i.e. `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType,
* true>) == sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`)
* and `tsl::rh::power_of_two_growth_policy` is used, the hash will be stored
* even if `StoreHash` is false so that we can speed-up the rehash (but it will
* not be used on lookups unless `StoreHash` is true).
*
* `GrowthPolicy` defines how the set grows and consequently how a hash value is
* mapped to a bucket. By default the set uses
* `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of
* buckets to a power of two and uses a mask to set the hash to a bucket instead
* of the slow modulo. Other growth policies are available and you may define
* your own growth policy, check `tsl::rh::power_of_two_growth_policy` for the
* interface.
*
* `Key` must be swappable.
*
* `Key` must be copy and/or move constructible.
*
* If the destructor of `Key` throws an exception, the behaviour of the class is
* undefined.
*
* Iterators invalidation:
* - clear, operator=, reserve, rehash: always invalidate the iterators.
* - insert, emplace, emplace_hint, operator[]: if there is an effective
* insert, invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template <class Key, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>, bool StoreHash = false,
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
class robin_set {
private:
template <typename U>
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
class KeySelect {
public:
using key_type = Key;
const key_type& operator()(const Key& key) const noexcept { return key; }
key_type& operator()(Key& key) noexcept { return key; }
};
using ht = detail_robin_hash::robin_hash<Key, KeySelect, void, Hash, KeyEqual,
Allocator, StoreHash, GrowthPolicy>;
public:
using key_type = typename ht::key_type;
using value_type = typename ht::value_type;
using size_type = typename ht::size_type;
using difference_type = typename ht::difference_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using allocator_type = typename ht::allocator_type;
using reference = typename ht::reference;
using const_reference = typename ht::const_reference;
using pointer = typename ht::pointer;
using const_pointer = typename ht::const_pointer;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
/*
* Constructors
*/
robin_set() : robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {}
explicit robin_set(size_type bucket_count, const Hash& hash = Hash(),
const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: m_ht(bucket_count, hash, equal, alloc) {}
robin_set(size_type bucket_count, const Allocator& alloc)
: robin_set(bucket_count, Hash(), KeyEqual(), alloc) {}
robin_set(size_type bucket_count, const Hash& hash, const Allocator& alloc)
: robin_set(bucket_count, hash, KeyEqual(), alloc) {}
explicit robin_set(const Allocator& alloc)
: robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {}
template <class InputIt>
robin_set(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: robin_set(bucket_count, hash, equal, alloc) {
insert(first, last);
}
template <class InputIt>
robin_set(InputIt first, InputIt last, size_type bucket_count,
const Allocator& alloc)
: robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc) {}
template <class InputIt>
robin_set(InputIt first, InputIt last, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: robin_set(first, last, bucket_count, hash, KeyEqual(), alloc) {}
robin_set(std::initializer_list<value_type> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
const Allocator& alloc = Allocator())
: robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc) {}
robin_set(std::initializer_list<value_type> init, size_type bucket_count,
const Allocator& alloc)
: robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(),
alloc) {}
robin_set(std::initializer_list<value_type> init, size_type bucket_count,
const Hash& hash, const Allocator& alloc)
: robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(),
alloc) {}
robin_set& operator=(std::initializer_list<value_type> ilist) {
m_ht.clear();
m_ht.reserve(ilist.size());
m_ht.insert(ilist.begin(), ilist.end());
return *this;
}
allocator_type get_allocator() const { return m_ht.get_allocator(); }
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert(const value_type& value) {
return m_ht.insert(value);
}
std::pair<iterator, bool> insert(value_type&& value) {
return m_ht.insert(std::move(value));
}
iterator insert(const_iterator hint, const value_type& value) {
return m_ht.insert_hint(hint, value);
}
iterator insert(const_iterator hint, value_type&& value) {
return m_ht.insert_hint(hint, std::move(value));
}
template <class InputIt>
void insert(InputIt first, InputIt last) {
m_ht.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist) {
m_ht.insert(ilist.begin(), ilist.end());
}
/**
* Due to the way elements are stored, emplace will need to move or copy the
* key-value once. The method is equivalent to
* insert(value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
return m_ht.emplace(std::forward<Args>(args)...);
}
/**
* Due to the way elements are stored, emplace_hint will need to move or copy
* the key-value once. The method is equivalent to insert(hint,
* value_type(std::forward<Args>(args)...));
*
* Mainly here for compatibility with the std::unordered_map interface.
*/
template <class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args) {
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
}
iterator erase(iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) {
return m_ht.erase(first, last);
}
size_type erase(const key_type& key) { return m_ht.erase(key); }
/**
* Erase the element at position 'pos'. In contrast to the regular erase()
* function, erase_fast() does not return an iterator. This allows it to be
* faster especially in hash sets with a low load factor, where finding the
* next nonempty bucket would be costly.
*/
void erase_fast(iterator pos) { return m_ht.erase_fast(pos); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup to the value if you already have the hash.
*/
size_type erase(const key_type& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key) {
return m_ht.erase(key);
}
/**
* @copydoc erase(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup to the value if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type erase(const K& key, std::size_t precalculated_hash) {
return m_ht.erase(key, precalculated_hash);
}
void swap(robin_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
size_type count(const Key& key) const { return m_ht.count(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
size_type count(const Key& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key) const {
return m_ht.count(key);
}
/**
* @copydoc count(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
size_type count(const K& key, std::size_t precalculated_hash) const {
return m_ht.count(key, precalculated_hash);
}
iterator find(const Key& key) { return m_ht.find(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
iterator find(const Key& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
const_iterator find(const Key& key) const { return m_ht.find(key); }
/**
* @copydoc find(const Key& key, std::size_t precalculated_hash)
*/
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key) {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
iterator find(const K& key, std::size_t precalculated_hash) {
return m_ht.find(key, precalculated_hash);
}
/**
* @copydoc find(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key) const {
return m_ht.find(key);
}
/**
* @copydoc find(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
const_iterator find(const K& key, std::size_t precalculated_hash) const {
return m_ht.find(key, precalculated_hash);
}
bool contains(const Key& key) const { return m_ht.contains(key); }
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
bool contains(const Key& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key) const {
return m_ht.contains(key);
}
/**
* @copydoc contains(const K& key) const
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
bool contains(const K& key, std::size_t precalculated_hash) const {
return m_ht.contains(key, precalculated_hash);
}
std::pair<iterator, iterator> equal_range(const Key& key) {
return m_ht.equal_range(key);
}
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
std::pair<iterator, iterator> equal_range(const Key& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(
const Key& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* This overload only participates in the overload resolution if the typedef
* KeyEqual::is_transparent exists. If so, K must be hashable and comparable
* to Key.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key) {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The
* hash value should be the same as hash_function()(key). Useful to speed-up
* the lookup if you already have the hash.
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<iterator, iterator> equal_range(const K& key,
std::size_t precalculated_hash) {
return m_ht.equal_range(key, precalculated_hash);
}
/**
* @copydoc equal_range(const K& key)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
return m_ht.equal_range(key);
}
/**
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
*/
template <
class K, class KE = KeyEqual,
typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
std::pair<const_iterator, const_iterator> equal_range(
const K& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float min_load_factor() const { return m_ht.min_load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
/**
* Set the `min_load_factor` to `ml`. When the `load_factor` of the set goes
* below `min_load_factor` after some erase operations, the set will be
* shrunk when an insertion occurs. The erase method itself never shrinks
* the set.
*
* The default value of `min_load_factor` is 0.0f, the set never shrinks by
* default.
*/
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count_) { m_ht.rehash(count_); }
void reserve(size_type count_) { m_ht.reserve(count_); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Convert a const_iterator to an iterator.
*/
iterator mutable_iterator(const_iterator pos) {
return m_ht.mutable_iterator(pos);
}
friend bool operator==(const robin_set& lhs, const robin_set& rhs) {
if (lhs.size() != rhs.size()) {
return false;
}
for (const auto& element_lhs : lhs) {
const auto it_element_rhs = rhs.find(element_lhs);
if (it_element_rhs == rhs.cend()) {
return false;
}
}
return true;
}
/**
* Serialize the set through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the
* following call:
* - `template<typename U> void operator()(const U& value);` where the types
* `std::int16_t`, `std::uint32_t`, `std::uint64_t`, `float` and `Key` must be
* supported for U.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, ...) of the types it serializes in the hands of the `Serializer`
* function object if compatibility is required.
*/
template <class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized set through the `deserializer`
* parameter.
*
* The `deserializer` parameter must be a function object that supports the
* following call:
* - `template<typename U> U operator()();` where the types `std::int16_t`,
* `std::uint32_t`, `std::uint64_t`, `float` and `Key` must be supported for
* U.
*
* If the deserialized hash set type is hash compatible with the serialized
* set, the deserialization process can be sped up by setting
* `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and
* GrowthPolicy must behave the same way than the ones used on the serialized
* set and the StoreHash must have the same value. The `std::size_t` must also
* be of the same size as the one on the platform used to serialize the set.
* If these criteria are not met, the behaviour is undefined with
* `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `Key` of the `robin_set` is not the
* same as the type used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for
* floats, size of int, ...) of the types it deserializes in the hands of the
* `Deserializer` function object if compatibility is required.
*/
template <class Deserializer>
static robin_set deserialize(Deserializer& deserializer,
bool hash_compatible = false) {
robin_set set(0);
set.m_ht.deserialize(deserializer, hash_compatible);
return set;
}
friend bool operator!=(const robin_set& lhs, const robin_set& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(robin_set& lhs, robin_set& rhs) { lhs.swap(rhs); }
private:
ht m_ht;
};
/**
* Same as `tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash,
* tsl::rh::prime_growth_policy>`.
*/
template <class Key, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key>, bool StoreHash = false>
using robin_pg_set = robin_set<Key, Hash, KeyEqual, Allocator, StoreHash,
tsl::rh::prime_growth_policy>;
} // end namespace tsl
#endif

View File

@@ -0,0 +1,28 @@
MXTASKING_DIR := $(REP_DIR)/src/lib/mx
GENODE_GCC_TOOLCHAIN_DIR := /usr/local/genode/tool/23.05
SRC_CC = $(shell find $(MXTASKING_DIR) -name "*.cpp")
vpath %.cpp $(MXTASKING_DIR)
INC_DIR += $(REP_DIR)/src/lib
INC_DIR += $(REP_DIR)/include
INC_DIR += $(REP_DIR)/include/ealanos/util
INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(call select_from_repositories,src/lib/libc)/spec/x86_64
vpath %.h ${INC_DIR}
CUSTOM_CXX = /usr/local/genode/tool/bin/clang++
CUSTOM_CC = /usr/local/genode/tool/bin/clang
CC_OPT += --target=x86_64-genode --sysroot=/does/not/exist --gcc-toolchain=$(GENODE_GCC_TOOLCHAIN_DIR) -DCLANG_CXX11_ATOMICS
CC_OPT += -std=c++20 -pedantic -Wall \
-Wno-invalid-offsetof -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization \
-Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Woverloaded-virtual \
-Wredundant-decls -Wshadow -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef \
-Wno-unused -Wold-style-cast -Wno-uninitialized -O2 -g
CC_OPT += $(addprefix -I ,$(INC_DIR))
CC_CXX_WARN_STRICT =
LIBS += base libm libc stdcxx
EXT_OBJECTS += /usr/local/genode/tool/lib/clang/14.0.5/lib/linux/libclang_rt.builtins-x86_64.a /usr/local/genode/tool/lib/libatomic.a

View File

@@ -0,0 +1,22 @@
MIT License
Copyright (C) 2025 Michael Müller
Copyright (c) 2023 Jan Mühlig
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,33 @@
# MxTasking
The *MxTasking* lib implements the task engine.
## Structure
### Memory
The memory component can be found in the [memory](memory) folder.
It implements different memory allocators (the [fixed size allocator](memory/fixed_size_allocator.h) mainly for tasks with a static size and the [dynamic size allocator](memory/dynamic_size_allocator.h) for variable sized data objects).
Further, epoch-based memory reclamation is implemented by the [epoch manager](memory/reclamation/epoch_manager.h).
### Queue
Different (task-) queues can be found in the [queue](queue) folder.
It provides
* a [non-synchronized single-core queue](queue/list.h) (for fast core-local dispatching),
* a [multi-producer single-consumer queue](queue/mpsc.h) (for dispatching tasks to remote cores),
* and a [(bound) multi-producer multi-consumer queue](queue/bound_mpmc.h) (mainly used for memory-reclamation).
### Resource
The resource component can be found in the [resource](resource) folder.
This package encapsulates
* [annotations for resources](resource/annotation.h),
* the [resource builder](resource/builder.h) which creates and schedules resources,
* a [tagged pointer](resource/ptr.h) instance that links to resources including information (synchronization method and worker id),
* and the [resource interface](resource/resource_interface.h) that has to be implemented by each to synchronized resource.
### Synchronization
The synchronization component can be found in the [synchronization](synchronization) folder.
This package provides different synchronization methods ([optimistic lock](synchronization/optimistic_lock.h), [rw lock](synchronization/rw_spinlock.h), [spinlock](synchronization/spinlock.h)) and [structures to define the required synchronization level](synchronization/synchronization.h).
### Tasking
The tasking core can be found in the [tasking](tasking) folder.
For more information see the tasking-specific [readme](tasking/README.md).

View File

@@ -0,0 +1,9 @@
#pragma once
namespace mx::io::network {
class config
{
public:
static constexpr auto max_connections() noexcept { return 64U; }
};
} // namespace mx::io::network

View File

@@ -0,0 +1,153 @@
#include "server.h"
#include <limits>
#include <mx/tasking/runtime.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace mx::io::network;
Server::Server(std::unique_ptr<MessageHandler> &&message_handler, const std::uint16_t port,
const std::uint16_t count_channels) noexcept
: _port(port), _socket(-1), _client_sockets({0U}), _message_handler(std::move(message_handler)),
_count_channels(count_channels)
{
this->_buffer.fill('\0');
}
bool Server::listen()
{
this->_socket = socket(AF_INET, SOCK_STREAM, 0);
if (this->_socket == 0)
{
return false;
}
auto option = std::int32_t{1};
if (setsockopt(this->_socket, SOL_SOCKET, SO_REUSEADDR, &option, socklen_t{sizeof(std::int32_t)}) < 0)
{
return false;
}
auto address = sockaddr_in{};
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(this->_port);
if (bind(this->_socket, reinterpret_cast<sockaddr *>(&address), sizeof(sockaddr_in)) < 0)
{
return false;
}
if (::listen(this->_socket, 3) < 0)
{
return false;
}
auto address_length = socklen_t{sizeof(sockaddr_in)};
auto socket_descriptors = fd_set{};
auto max_socket_descriptor = this->_socket;
auto client_socket = std::int32_t{-1};
while (this->_is_running)
{
FD_ZERO(&socket_descriptors); // NOLINT
FD_SET(this->_socket, &socket_descriptors);
for (auto &socket_descriptor : this->_client_sockets)
{
if (socket_descriptor > 0)
{
FD_SET(socket_descriptor, &socket_descriptors);
}
max_socket_descriptor = std::max(max_socket_descriptor, std::int32_t(socket_descriptor));
}
auto timeout = timeval{};
timeout.tv_usec = 10000;
const auto count_ready_selectors =
select(max_socket_descriptor + 1, &socket_descriptors, nullptr, nullptr, &timeout);
if (count_ready_selectors > 0)
{
if (FD_ISSET(this->_socket, &socket_descriptors))
{
if ((client_socket = accept(this->_socket, reinterpret_cast<sockaddr *>(&address), &address_length)) <
0)
{
return false;
}
this->add_client(client_socket);
}
for (auto i = 0U; i < this->_client_sockets.size(); ++i)
{
const auto client = this->_client_sockets[i];
if (FD_ISSET(client, &socket_descriptors))
{
const auto read_bytes = read(client, this->_buffer.data(), this->_buffer.size());
if (read_bytes == 0U)
{
::close(client);
this->_client_sockets[i] = 0U;
}
else
{
// Copy incoming data locally.
auto message = std::string(this->_buffer.data(), read_bytes);
// Spawn task that processes the message.
auto *task = new MessageHandlerTask(*this->_message_handler.get(), i, std::move(message));
task->annotate(std::uint16_t(this->_next_worker_id.fetch_add(1U) % this->_count_channels));
tasking::runtime::spawn(*task);
}
}
}
}
}
for (const auto client : this->_client_sockets)
{
if (client > 0)
{
::close(client);
}
}
::close(this->_socket);
return true;
}
void Server::send(const std::uint32_t client_id, std::string &&message)
{
const auto length = std::uint64_t(message.size());
auto response = std::string(length + sizeof(length), '\0');
// Write header
std::memcpy(response.data(), static_cast<const void *>(&length), sizeof(length));
// Write data
std::memmove(response.data() + sizeof(length), message.data(), length);
::send(this->_client_sockets[client_id], response.c_str(), response.length(), 0);
}
std::uint16_t Server::add_client(const std::int32_t client_socket)
{
for (auto i = 0U; i < this->_client_sockets.size(); ++i)
{
if (this->_client_sockets[i] == 0U)
{
this->_client_sockets[i] = client_socket;
return i;
}
}
return std::numeric_limits<std::uint16_t>::max();
}
void Server::stop() noexcept
{
this->_is_running = false;
}

View File

@@ -0,0 +1,662 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h \
/home/mml/genode-igb/repos/libports/include/libc/component.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/netinet/in.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sockaddr_storage.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/netinet6/in6.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/socket.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_iovec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/_align.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/_align.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h:
/home/mml/genode-igb/repos/libports/include/libc/component.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/netinet/in.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sockaddr_storage.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/netinet6/in6.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/socket.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_iovec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/_align.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/_align.h:

View File

@@ -0,0 +1,76 @@
#pragma once
#include "config.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mx/memory/fixed_size_allocator.h>
#include <mx/system/cpu.h>
#include <mx/tasking/config.h>
#include <mx/tasking/scheduler.h>
#include <mx/util/core_set.h>
#include <optional>
#include <string>
namespace mx::io::network {
class MessageHandler
{
public:
constexpr MessageHandler() noexcept = default;
virtual ~MessageHandler() noexcept = default;
virtual mx::tasking::TaskResult handle(std::uint16_t worker_id, std::uint32_t client_id, std::string &&message) = 0;
};
class MessageHandlerTask final : public tasking::TaskInterface
{
public:
MessageHandlerTask(MessageHandler &message_handler, const std::uint32_t client_id, std::string &&message) noexcept
: _message_handler(message_handler), _client_id(client_id), _message(std::move(message))
{
}
~MessageHandlerTask() noexcept override = default;
tasking::TaskResult execute(const std::uint16_t worker_id) override
{
auto result = _message_handler.handle(worker_id, _client_id, std::move(_message));
delete this;
return result;
}
private:
MessageHandler &_message_handler;
const std::uint32_t _client_id;
std::string _message;
};
class Server
{
public:
Server(std::unique_ptr<MessageHandler> &&message_handler, std::uint16_t port,
std::uint16_t count_channels) noexcept;
~Server() noexcept = default;
[[nodiscard]] std::uint16_t port() const noexcept { return _port; }
void stop() noexcept;
void send(std::uint32_t client_id, std::string &&message);
bool listen();
[[nodiscard]] bool is_running() const noexcept { return _is_running; }
private:
const std::uint16_t _port;
std::int32_t _socket;
std::array<std::uint32_t, config::max_connections()> _client_sockets;
std::array<char, 2048U> _buffer;
std::unique_ptr<MessageHandler> _message_handler;
alignas(64) bool _is_running = true;
alignas(64) std::atomic_uint64_t _next_worker_id{0U};
const std::uint16_t _count_channels;
std::uint16_t add_client(std::int32_t client_socket);
};
} // namespace mx::io::network

View File

@@ -0,0 +1,39 @@
#pragma once
#include <cstdint>
namespace mx::memory {
/**
* Helper for setting the correct size on aligned allocation:
* The allocation size has to be a multiple of the alignment.
*/
class alignment_helper
{
public:
template <typename T> static constexpr T next_multiple(const T value, const T base)
{
if (value > base)
{
const auto mod = value % base;
if (mod == 0U)
{
return value;
}
return value + base - mod;
}
return base;
}
static constexpr bool is_power_of_two(const std::uint64_t value)
{
return ((value != 0U) && ((value & (value - 1U)) == 0U));
}
static constexpr std::uint64_t next_power_of_two(const std::uint64_t value)
{
return is_power_of_two(value) ? value : 1ULL << (sizeof(std::uint64_t) * 8 - __builtin_clzll(value));
}
};
} // namespace mx::memory

View File

@@ -0,0 +1,32 @@
#pragma once
#include <chrono>
#include <iterator>
namespace mx::memory {
class config
{
public:
/**
* @return Number of maximal provided NUMA regions.
*/
static constexpr std::uint8_t max_numa_nodes() { return 8U; }
/**
* @return Cycles for prefetching a single cache line.
*/
static constexpr std::uint32_t latency_per_prefetched_cache_line() { return 290U; }
/**
* @return Interval of each epoch, if memory reclamation is used.
*/
static constexpr std::chrono::milliseconds epoch_interval() { return std::chrono::milliseconds(50U); }
/**
* @return True, if garbage is removed local.
*/
static constexpr bool local_garbage_collection() { return false; }
static constexpr std::size_t min_block_size() { return 2048; }
static constexpr std::size_t superblock_cutoff() { return 1024 * min_block_size();}
};
} // namespace mx::memory

View File

@@ -0,0 +1,331 @@
#include "dynamic_size_allocator.h"
#include "global_heap.h"
#include <algorithm>
#include <cassert>
#include <mx/system/cpu.h>
using namespace mx::memory::dynamic;
AllocationBlock::AllocationBlock(const std::uint32_t id, const std::uint8_t numa_node_id, const std::size_t size)
: _id(id), _numa_node_id(numa_node_id), _size(size), _available_size(size)
{
this->_allocated_block = GlobalHeap::allocate(numa_node_id, size);
this->_free_elements.emplace_back(FreeHeader{reinterpret_cast<std::uintptr_t>(this->_allocated_block), size});
this->_lock.unlock();
}
AllocationBlock::AllocationBlock(AllocationBlock &&other) noexcept
: _id(other._id), _numa_node_id(other._numa_node_id), _size(other._size),
_allocated_block(std::exchange(other._allocated_block, nullptr)), _free_elements(std::move(other._free_elements)),
_available_size(other._available_size)
{
this->_lock.unlock();
}
AllocationBlock &AllocationBlock::operator=(AllocationBlock &&other) noexcept
{
this->_id = other._id;
this->_numa_node_id = other._numa_node_id;
this->_size = other._size;
this->_allocated_block = std::exchange(other._allocated_block, nullptr);
this->_free_elements = std::move(other._free_elements);
this->_available_size = other._available_size;
this->_lock.unlock();
return *this;
}
AllocationBlock::~AllocationBlock()
{
if (this->_allocated_block != nullptr)
{
GlobalHeap::free(this->_allocated_block, this->_size, this->_numa_node_id);
}
}
void *AllocationBlock::allocate(const std::size_t alignment, const std::size_t size) noexcept
{
assert(alignment && (!(alignment & (alignment - 1))) && "Alignment must be > 0 and power of 2");
this->_lock.lock();
if (this->_available_size < size)
{
this->_lock.unlock();
return nullptr;
}
const auto block = this->find_block(alignment, size);
if (block.has_value() == false)
{
this->_lock.unlock();
return nullptr;
}
const auto [free_block_index, aligned_size_including_header] = block.value();
auto &free_block = this->_free_elements[free_block_index];
const auto free_block_start = free_block.start();
const auto free_block_end = free_block_start + free_block.size();
const auto remaining_size = free_block.size() - aligned_size_including_header;
auto size_before_header = std::uint16_t{0U};
if (remaining_size >= 256U)
{
free_block.contract(aligned_size_including_header);
this->_available_size -= aligned_size_including_header;
}
else
{
size_before_header = remaining_size;
this->_available_size -= free_block.size();
this->_free_elements.erase(this->_free_elements.begin() + free_block_index);
}
this->_lock.unlock();
const auto allocation_header_address = free_block_end - aligned_size_including_header;
new (reinterpret_cast<void *>(allocation_header_address)) AllocatedHeader(
aligned_size_including_header - sizeof(AllocatedHeader), size_before_header, this->_numa_node_id, this->_id);
assert((allocation_header_address + sizeof(AllocatedHeader)) % alignment == 0 && "Not aligned");
return reinterpret_cast<void *>(allocation_header_address + sizeof(AllocatedHeader));
}
void AllocationBlock::free(AllocatedHeader *allocation_header) noexcept
{
const auto allocated_size = allocation_header->size;
const auto unused_size_before_header = allocation_header->unused_size_before_header;
const auto block_address = reinterpret_cast<std::uintptr_t>(allocation_header) - unused_size_before_header;
const auto size = allocated_size + unused_size_before_header + sizeof(AllocatedHeader);
const auto free_element = FreeHeader{block_address, size};
this->_lock.lock();
if (this->_free_elements.empty())
{
this->_free_elements.push_back(free_element);
}
else
{
const auto lower_bound_iterator =
std::lower_bound(this->_free_elements.begin(), this->_free_elements.end(), free_element);
const auto index = std::distance(this->_free_elements.begin(), lower_bound_iterator);
assert(index >= 0 && "Index is negative");
const auto real_index = std::size_t(index);
// Try to merge to the right.
if (real_index < this->_free_elements.size() && free_element.borders(this->_free_elements[real_index]))
{
this->_free_elements[real_index].merge(free_element);
// Okay, we inserted the new free element as merge, we do not insert it "real".
// Try to merge the expanded right with the left.
if (real_index > 0U && this->_free_elements[real_index - 1U].borders(this->_free_elements[real_index]))
{
this->_free_elements[real_index - 1].merge(this->_free_elements[real_index]);
this->_free_elements.erase(this->_free_elements.begin() + real_index);
}
}
else if (real_index > 0U && this->_free_elements[real_index - 1U].borders(free_element))
{
// In this case, we could not merge with the right, but can we merge
// to the left? By this, we could save up the real insert.
this->_free_elements[real_index - 1U].merge(free_element);
}
else
{
// We could not merge anything. Just insert.
this->_free_elements.insert(this->_free_elements.begin() + real_index, free_element);
}
}
this->_available_size += free_element.size();
this->_lock.unlock();
}
std::optional<std::pair<std::size_t, std::size_t>> AllocationBlock::find_block(const std::size_t alignment,
const std::size_t size) const noexcept
{
/**
* Check each block of the free list for enough space to include the wanted space.
* If enough, check the alignment (starting at the end).
*
* +----------------------------+
* | 2000byte |
* +----------------------------+
* => wanted: 700byte
* => align border -> 1300 is not aligned, expand to 720byte -> 1280 is aligned
* +----------------------------+
* | 1280byte | 720byte |
* +----------------------------+
*
*/
const auto size_including_header = size + sizeof(AllocatedHeader);
for (auto index = 0U; index < this->_free_elements.size(); ++index)
{
const auto &free_element = this->_free_elements[index];
if (free_element >= size_including_header)
{
const auto start = free_element.start();
// The free block ends here.
const auto end = start + free_element.size();
// This is where we would start the memory block on allocation
// But this may be not aligned.
const auto possible_block_begin = end - size;
// This is the size we need to start the block aligned.
const auto aligned_size = size + (possible_block_begin & (alignment - 1U));
// This is the size we need aligned and for header.
const auto aligned_size_including_header = aligned_size + sizeof(AllocatedHeader);
if (free_element >= aligned_size_including_header)
{
// aligned_size_including_header
return std::make_optional(std::make_pair(index, aligned_size_including_header));
}
}
}
return std::nullopt;
}
Allocator::Allocator()
{
this->initialize_empty();
}
void *Allocator::allocate(const std::uint8_t numa_node_id, const std::size_t alignment, const std::size_t size) noexcept
{
auto &allocation_blocks = this->_numa_allocation_blocks[numa_node_id];
auto *memory = allocation_blocks.back().allocate(alignment, size);
if (memory == nullptr)
{
// This will be allocated default...
constexpr auto default_alloc_size = std::size_t(1ULL << 28U);
// ... but if the requested size is higher, allocate more.
const auto size_to_alloc = std::max(default_alloc_size, alignment_helper::next_multiple(size, 64UL));
// Try to allocate until allocation was successful.
// It is possible, that another core tries to allocate at the
// same time, therefore we capture the allocation flag (one per region)
auto &flag = this->_numa_allocation_flags[numa_node_id].value();
while (memory == nullptr)
{
allocate_new_block(numa_node_id, size_to_alloc, allocation_blocks, flag);
memory = allocation_blocks.back().allocate(alignment, size);
}
}
return memory;
}
void Allocator::allocate_new_block(const std::uint8_t numa_node_id, const std::size_t size,
std::vector<AllocationBlock> &blocks, std::atomic<bool> &flag)
{
// Acquire the allocation flag to ensure only one thread to allocate.
auto expected = false;
const auto can_allocate = flag.compare_exchange_strong(expected, true);
if (can_allocate)
{
// If that was this thread go for it...
const auto next_id = this->_next_allocation_id[numa_node_id].value().fetch_add(1U, std::memory_order_acq_rel);
blocks.emplace_back(AllocationBlock{next_id, numa_node_id, size});
// .. but release the allocation flag afterward.
flag.store(false);
}
else
{
// If that was another thread, wait until he finished.
while (flag.load())
{
system::builtin::pause();
}
}
}
void Allocator::free(void *pointer) noexcept
{
// Every allocated memory belongs to one allocation block.
// The reason is, that we can only return full blocks to
// the global heap that is managed by the operating system.
const auto address = reinterpret_cast<std::uintptr_t>(pointer);
// Access the header to identify the allocation block.
const auto header_address = address - sizeof(AllocatedHeader);
auto *allocation_header = reinterpret_cast<AllocatedHeader *>(header_address);
// Check all blocks to find the matching one.
for (auto &block : this->_numa_allocation_blocks[allocation_header->numa_node_id])
{
if (allocation_header->allocation_block_id == block.id())
{
block.free(allocation_header);
return;
}
}
}
void Allocator::defragment() noexcept
{
// Remove all blocks that are unused to free as much memory as possible.
for (auto i = 0U; i <= system::cpu::max_node_id(); ++i)
{
auto &numa_blocks = this->_numa_allocation_blocks[i];
numa_blocks.erase(
std::remove_if(numa_blocks.begin(), numa_blocks.end(), [](const auto &block) { return block.is_free(); }),
numa_blocks.end());
}
// If all memory was released, acquire new.
this->initialize_empty();
}
void Allocator::initialize_empty()
{
// For performance reasons: Each list must contain at least
// one block. This way, we do not have to check every time.
for (auto i = 0U; i <= system::cpu::max_node_id(); ++i)
{
auto &blocks = this->_numa_allocation_blocks[i];
if (blocks.empty())
{
const auto next_id = this->_next_allocation_id[i].value().fetch_add(1U, std::memory_order_relaxed);
blocks.emplace_back(AllocationBlock{next_id, std::uint8_t(i), 4096U * 4096U});
}
}
}
bool Allocator::is_free() const noexcept
{
for (auto i = 0U; i <= system::cpu::max_node_id(); ++i)
{
const auto &numa_blocks = this->_numa_allocation_blocks[i];
const auto iterator = std::find_if(numa_blocks.cbegin(), numa_blocks.cend(), [](const auto &allocation_block) {
return allocation_block.is_free() == false;
});
if (iterator != numa_blocks.cend())
{
return false;
}
}
return true;
}
void Allocator::release_allocated_memory() noexcept
{
for (auto i = 0U; i <= system::cpu::max_node_id(); ++i)
{
this->_numa_allocation_blocks[i].clear();
this->_next_allocation_id[i].value().store(0U);
}
}

View File

@@ -0,0 +1,446 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/dynamic_size_allocator.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/dynamic_size_allocator.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/dynamic_size_allocator.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:

View File

@@ -0,0 +1,186 @@
#pragma once
#include "config.h"
#include <array>
#include <cassert>
#include <cstdint>
#include <mx/synchronization/spinlock.h>
#include <mx/util/aligned_t.h>
#include <optional>
#include <utility>
#include <vector>
namespace mx::memory::dynamic {
/**
* Represents free space within an allocation block.
* Holds the start and the size of a free object.
*/
class FreeHeader
{
public:
constexpr FreeHeader(const std::uintptr_t start, const std::size_t size) noexcept : _start(start), _size(size) {}
constexpr FreeHeader(const FreeHeader &other) noexcept = default;
~FreeHeader() noexcept = default;
void contract(const std::size_t size) noexcept { _size -= size; }
[[nodiscard]] std::uintptr_t start() const noexcept { return _start; }
[[nodiscard]] std::uintptr_t size() const noexcept { return _size; }
bool operator<(const FreeHeader &other) const noexcept { return _start < other._start; }
bool operator>=(const std::size_t size) const noexcept { return _size >= size; }
[[nodiscard]] bool borders(const FreeHeader &other) const noexcept { return (_start + _size) == other._start; }
void merge(const FreeHeader &other) noexcept
{
if (other._start < _start)
{
assert(other.borders(*this) && "Can not merge: Elements are not next to each other");
_start = other._start;
_size += other._size;
}
else
{
assert(borders(other) && "Can not merge: Elements are not next to each other");
_size += other._size;
}
}
private:
std::uintptr_t _start;
std::size_t _size;
};
/**
* Header in front of allocated memory, storing the
* size, the size which is unused because of alignment,
* the ID of the NUMA node the memory is allocated on,
* and the source allocation block of this memory.
*/
struct AllocatedHeader
{
constexpr AllocatedHeader(const std::size_t size_, const std::uint16_t unused_size_before_header_,
const std::uint8_t numa_node_id_, const std::uint32_t allocation_block_id_) noexcept
: size(size_), unused_size_before_header(unused_size_before_header_), numa_node_id(numa_node_id_),
allocation_block_id(allocation_block_id_)
{
}
const std::size_t size;
const std::uint16_t unused_size_before_header;
const std::uint8_t numa_node_id;
const std::uint32_t allocation_block_id;
};
/**
* Set of on or more free tiles, that can be allocated.
*/
class AllocationBlock
{
public:
AllocationBlock(std::uint32_t id, std::uint8_t numa_node_id, std::size_t size);
AllocationBlock(const AllocationBlock &other) = delete;
AllocationBlock(AllocationBlock &&other) noexcept;
AllocationBlock &operator=(AllocationBlock &&other) noexcept;
~AllocationBlock();
/**
* Allocates memory from the allocation block.
*
* @param alignment Requested alignment.
* @param size Requested size.
* @return Pointer to the allocated memory.
*/
void *allocate(std::size_t alignment, std::size_t size) noexcept;
/**
* Frees memory.
*
* @param allocation_header Pointer to the header of the freed memory.
*/
void free(AllocatedHeader *allocation_header) noexcept;
/**
* @return Unique number of this allocation block.
*/
[[nodiscard]] std::uint32_t id() const noexcept { return _id; }
/**
* @return True, if the full block is free.
*/
[[nodiscard]] bool is_free() const noexcept
{
return _free_elements.empty() || (_free_elements.size() == 1 && _free_elements[0].size() == _size);
}
private:
alignas(64) std::uint32_t _id;
std::uint8_t _numa_node_id;
std::size_t _size;
void *_allocated_block;
std::vector<FreeHeader> _free_elements;
alignas(64) std::size_t _available_size;
synchronization::Spinlock _lock;
std::optional<std::pair<std::size_t, std::size_t>> find_block(std::size_t alignment,
std::size_t size) const noexcept;
};
/**
* Allocator which holds a set of allocation blocks separated
* for each numa node region.
*/
class Allocator
{
public:
Allocator();
~Allocator() = default;
void *allocate(std::uint8_t numa_node_id, std::size_t alignment, std::size_t size) noexcept;
void free(void *pointer) noexcept;
/**
* Frees unused allocation blocks.
*/
void defragment() noexcept;
/**
* Releases all allocated memory.
*/
void release_allocated_memory() noexcept;
/**
* Adds minimal memory to all numa node regions.
*/
void initialize_empty();
/**
* @return True, if all blocks of all numa regions are free.
*/
[[nodiscard]] bool is_free() const noexcept;
private:
// Allocation blocks per numa node region.
std::array<std::vector<AllocationBlock>, config::max_numa_nodes()> _numa_allocation_blocks;
// Allocation flags, used for synchronization when allocating, per numa node region.
std::array<util::aligned_t<std::atomic<bool>>, config::max_numa_nodes()> _numa_allocation_flags;
// Sequence for block allocation per numa node region.
std::array<util::aligned_t<std::atomic_uint32_t>, config::max_numa_nodes()> _next_allocation_id;
/**
* Allocates (thread-safe) a block of fresh memory
* @param numa_node_id
* @param size
* @param blocks
* @param flag
*/
void allocate_new_block(std::uint8_t numa_node_id, std::size_t size, std::vector<AllocationBlock> &blocks,
std::atomic<bool> &flag);
};
} // namespace mx::memory::dynamic

View File

@@ -0,0 +1,396 @@
#pragma once
#include "alignment_helper.h"
#include "config.h"
#include "global_heap.h"
#include "task_allocator_interface.h"
#include <array>
#include <atomic>
#include <cstdint>
#include <cstring>
#include <memory>
#include <mx/synchronization/spinlock.h>
#include <mx/system/cache.h>
#include <mx/system/cpu.h>
#include <mx/tasking/config.h>
#include <mx/util/core_set.h>
#include <unordered_map>
#include <utility>
#include <vector>
namespace mx::memory::fixed {
/**
* Represents a free memory object.
*/
class FreeHeader
{
public:
constexpr FreeHeader() noexcept = default;
~FreeHeader() noexcept = default;
[[nodiscard]] FreeHeader *next() const noexcept { return _next; }
void next(FreeHeader *next) noexcept { _next = next; }
void numa_node_id(const std::uint8_t numa_node_id) noexcept { _numa_node_id = numa_node_id; }
[[nodiscard]] std::uint8_t numa_node_id() const noexcept { return _numa_node_id; }
private:
FreeHeader *_next = nullptr;
std::uint8_t _numa_node_id = 0U;
};
/**
* The Chunk holds a fixed size of memory.
*/
class Chunk
{
public:
Chunk() noexcept = default;
Chunk(void *memory, const bool is_allocated) noexcept : _memory(memory), _is_allocated(is_allocated) {}
~Chunk() noexcept = default;
static constexpr auto size() { return 32U * 1024U * 1024U; /* 32mb */ }
explicit operator void *() const noexcept { return _memory; }
explicit operator std::uintptr_t() const noexcept { return reinterpret_cast<std::uintptr_t>(_memory); }
explicit operator bool() const noexcept { return _memory != nullptr && _is_allocated; }
private:
void *_memory{nullptr};
bool _is_allocated{true};
};
/**
* The ProcessorHeap holds memory for a single socket.
* All cores sitting on this socket can allocate memory.
* Internal, the ProcessorHeap bufferes allocated memory
* to minimize access to the global heap.
*/
class alignas(64) ProcessorHeap
{
public:
ProcessorHeap() noexcept = default;
explicit ProcessorHeap(const std::uint8_t numa_node_id) noexcept : _numa_node_id(numa_node_id)
{
_allocated_chunks.reserve(1024U);
fill_buffer<true>();
}
~ProcessorHeap() noexcept
{
if (_numa_node_id > mx::system::cpu::max_node_id())
return;
for (const auto allocated_chunk : _allocated_chunks)
{
GlobalHeap::free(static_cast<void *>(allocated_chunk), Chunk::size(), this->_numa_node_id);
}
for (const auto free_chunk : _free_chunk_buffer)
{
if (static_cast<bool>(free_chunk))
{
GlobalHeap::free(static_cast<void *>(free_chunk), Chunk::size(), this->_numa_node_id);
}
}
}
ProcessorHeap &operator=(ProcessorHeap &&other) noexcept
{
_numa_node_id = std::exchange(other._numa_node_id, std::numeric_limits<std::uint8_t>::max());
_free_chunk_buffer = other._free_chunk_buffer;
other._free_chunk_buffer.fill(Chunk{});
_next_free_chunk.store(other._next_free_chunk.load());
_fill_buffer_flag.store(other._fill_buffer_flag.load());
_allocated_chunks = std::move(other._allocated_chunks);
return *this;
}
/**
* @return ID of the NUMA node the memory is allocated on.
*/
[[nodiscard]] std::uint8_t numa_node_id() const noexcept { return _numa_node_id; }
/**
* Allocates a chunk of memory from the internal buffer.
* In case the buffer is empty, new Chunks from the GlobalHeap
* will be allocated.
*
* @return A chunk of allocated memory.
*/
Chunk allocate() noexcept
{
const auto next_free_chunk = _next_free_chunk.fetch_add(1U, std::memory_order_relaxed);
if (next_free_chunk < _free_chunk_buffer.size())
{
return _free_chunk_buffer[next_free_chunk];
}
auto expect = false;
const auto can_fill = _fill_buffer_flag.compare_exchange_strong(expect, true);
if (can_fill)
{
fill_buffer<false>();
_fill_buffer_flag = false;
}
else
{
while (_fill_buffer_flag)
{
system::builtin::pause();
}
}
return allocate();
}
[[nodiscard]] std::vector<std::pair<std::uintptr_t, std::uintptr_t>> allocated_chunks()
{
auto chunks = std::vector<std::pair<std::uintptr_t, std::uintptr_t>>{};
chunks.reserve(_free_chunk_buffer.size() + _allocated_chunks.size());
for (const auto chunk : _free_chunk_buffer)
{
const auto start = static_cast<std::uintptr_t>(chunk);
if (start > 0U)
{
chunks.emplace_back(std::make_pair(start, start + Chunk::size()));
}
}
for (const auto chunk : _allocated_chunks)
{
const auto start = static_cast<std::uintptr_t>(chunk);
if (start > 0U)
{
chunks.emplace_back(std::make_pair(start, start + Chunk::size()));
}
}
return chunks;
}
private:
// Size of the internal chunk buffer.
inline static constexpr auto CHUNKS = 128U;
// ID of the NUMA node of this ProcessorHeap.
std::uint8_t _numa_node_id{std::numeric_limits<std::uint8_t>::max()};
// Buffer for free chunks.
std::array<Chunk, CHUNKS> _free_chunk_buffer;
// Pointer to the next free chunk in the buffer.
alignas(64) std::atomic_uint8_t _next_free_chunk{0U};
// Flag, used for allocation from the global Heap for mutual exclusion.
std::atomic_bool _fill_buffer_flag{false};
// List of all allocated chunks, they will be freed later.
std::vector<Chunk> _allocated_chunks;
/**
* Allocates a very big chunk from the GlobalHeap and
* splits it into smaller chunks to store them in the
* internal buffer.
*/
template <bool IS_FIRST = false> void fill_buffer() noexcept
{
if constexpr (IS_FIRST == false)
{
for (const auto &chunk : _free_chunk_buffer)
{
_allocated_chunks.push_back(chunk);
}
}
auto *heap_memory = GlobalHeap::allocate(_numa_node_id, Chunk::size() * _free_chunk_buffer.size());
auto heap_memory_address = reinterpret_cast<std::uintptr_t>(heap_memory);
for (auto i = 0U; i < _free_chunk_buffer.size(); ++i)
{
_free_chunk_buffer[i] = Chunk(reinterpret_cast<void *>(heap_memory_address + (i * Chunk::size())), i == 0U);
/// Touch the page to handle page fault.
reinterpret_cast<char *>(_free_chunk_buffer[i].operator void *())[0] = '\0';
}
_next_free_chunk.store(0U);
}
};
/**
* The worker local heap represents the allocator on a single core.
* By this, allocations are latch-free.
*/
template <std::size_t S> class alignas(64) WorkerLocalHeap
{
public:
explicit WorkerLocalHeap(ProcessorHeap *processor_heap) noexcept : _processor_heap(processor_heap)
{
fill_buffer<true>();
}
WorkerLocalHeap() noexcept = default;
~WorkerLocalHeap() noexcept = default;
/**
* Allocates new memory from the worker local heap.
* When the internal buffer is empty, the worker local heap
* will allocate new chunks from the ProcessorHeap.
*
* @return Pointer to the new allocated memory.
*/
[[nodiscard]] void *allocate() noexcept
{
if (empty())
{
/// Maybe, we have an item in the overflow queue.
if (_overflow_first != nullptr)
{
auto *free_element = std::exchange(_overflow_first, _overflow_first->next());
return static_cast<void *>(free_element);
}
fill_buffer<false>();
}
return reinterpret_cast<void *>(_free_elements[_free_head++]);
}
/**
* Frees a memory object. The new available memory location
* will be placed in front of the "available"-list. By this,
* the next allocation will use the just freed object, which
* may be still in the CPU cache.
*
* @param pointer Pointer to the memory object to be freed.
*/
void free(void *pointer) noexcept
{
if (_free_head > 0U)
{
_free_elements[--_free_head] = std::uintptr_t(pointer);
}
else
{
auto *free_object = reinterpret_cast<FreeHeader *>(pointer);
free_object->next(_overflow_first);
_overflow_first = free_object;
}
}
/**
* Fills the buffer by asking the ProcessorHeap for more memory.
* This is latch-free since just a single core calls this method.
*/
template <bool IS_FIRST> void fill_buffer()
{
auto chunk = _processor_heap->allocate();
const auto chunk_address = static_cast<std::uintptr_t>(chunk);
constexpr auto count_objects = std::uint64_t{Chunk::size() / S};
_free_head = 0U;
for (auto i = 0U; i < count_objects; ++i)
{
_free_elements[i] = chunk_address + i * S;
if (IS_FIRST)
{
reinterpret_cast<FreeHeader *>(_free_elements[i])->next(nullptr);
}
}
}
private:
// Processor heap to allocate new chunks.
ProcessorHeap *_processor_heap{nullptr};
std::uint64_t _free_head{Chunk::size() / S};
// Free elements
std::array<std::uintptr_t, Chunk::size() / S> _free_elements;
// First element of the list of free memory objects.
FreeHeader *_overflow_first{nullptr};
/**
* @return True, when the buffer is empty.
*/
[[nodiscard]] bool empty() const noexcept { return _free_head == _free_elements.max_size(); }
};
/**
* The Allocator is the interface to the internal worker local heaps.
*/
template <std::size_t S> class Allocator final : public TaskAllocatorInterface
{
public:
explicit Allocator(const util::core_set &core_set)
{
for (auto node_id = std::uint8_t(0U); node_id <= system::cpu::max_node_id(); ++node_id)
{
if (core_set.has_core_of_numa_node(node_id))
{
_processor_heaps[node_id] = ProcessorHeap{node_id};
}
}
for (auto worker_id = 0U; worker_id < core_set.count_cores(); ++worker_id)
{
const auto node_id = system::cpu::node_id(core_set[worker_id]);
_worker_local_heaps[worker_id] = WorkerLocalHeap<S>{&_processor_heaps[node_id]};
}
}
explicit Allocator(util::core_set &&core_set) : Allocator(core_set) {}
~Allocator() override = default;
/**
* Allocates memory from the given worker local heap.
*
* @param worker_id ID of the worker.
* @return Allocated memory object.
*/
[[nodiscard]] void *allocate(const std::uint16_t worker_id) override
{
return _worker_local_heaps[worker_id].allocate();
}
/**
* Frees memory.
*
* @param worker_id ID of the worker to place the free object in.
* @param address Pointer to the memory object.
*/
void free(const std::uint16_t worker_id, void *address) noexcept override
{
_worker_local_heaps[worker_id].free(address);
}
[[nodiscard]] std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>
allocated_chunks() override
{
auto tags = std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>{};
auto chunks = _processor_heaps.front().allocated_chunks();
for (auto i = 1U; i < _processor_heaps.max_size(); ++i)
{
auto processor_chunks = _processor_heaps[i].allocated_chunks();
std::move(processor_chunks.begin(), processor_chunks.end(), std::back_inserter(chunks));
}
tags.insert(std::make_pair("tasks", std::move(chunks)));
return tags;
}
private:
// Heap for every processor socket/NUMA region.
std::array<ProcessorHeap, config::max_numa_nodes()> _processor_heaps;
// Map from core_id to core-local allocator.
std::array<WorkerLocalHeap<S>, tasking::config::max_cores()> _worker_local_heaps;
};
} // namespace mx::memory::fixed

View File

@@ -0,0 +1,60 @@
#pragma once
#include "alignment_helper.h"
#include "config.h"
#include "ealanos/util/json.hpp"
#include <cstdint>
#include <cstdlib>
#include <mx/system/cache.h>
#include <ealanos/memory/hamstraaja.h>
namespace mx::memory {
/**
* The global heap represents the heap, provided by the OS.
*/
class GlobalHeap
{
public:
static Ealan::Memory::Hamstraaja<config::min_block_size(), config::superblock_cutoff()> *_heap;
static bool initialized() { return _heap != nullptr; }
static void init(
Ealan::Memory::Hamstraaja<config::min_block_size(), config::superblock_cutoff()> *_alloc)
{
_heap = _alloc;
}
/**
* Allocates the given size on the given NUMA node.
*
* @param numa_node_id ID of the NUMA node, the memory should allocated on.
* @param size Size of the memory to be allocated.
* @return Pointer to allocated memory.
*/
static void *allocate(const std::uint8_t numa_node_id, const std::size_t size)
{
return _heap->alloc(size, numa_node_id); //numa_alloc_onnode(size, numa_node_id);
}
/**
* Allocates the given memory aligned to the cache line
* with a multiple of the alignment as a size.
* The allocated memory is not NUMA aware.
* @param size Size to be allocated.
* @return Allocated memory
*/
static void *allocate_cache_line_aligned(const std::size_t size)
{
return _heap->alloc(alignment_helper::next_multiple(size, 64UL));//std::aligned_alloc(mx::system::cache::line_size(), alignment_helper::next_multiple(size, 64UL));
}
/**
* Frees the given memory.
*
* @param memory Pointer to memory.
* @param size Size of the allocated object.
*/
static void free(void *memory, const std::size_t size, [[maybe_unused]] const std::uint8_t numa_node_id) { _heap->free(memory); }
};
} // namespace mx::memory

View File

@@ -0,0 +1,155 @@
#include "epoch_manager.h"
#include <mx/queue/list.h>
#include <mx/system/cpu.h>
#include <mx/tasking/runtime.h>
#include <thread>
using namespace mx::memory::reclamation;
void EpochManager::enter_epoch_periodically()
{
// Wait until the scheduler starts the system.
while (this->_is_running == false)
{
system::builtin::pause();
}
// Enter new epochs and collect garbage periodically
// while the system is running.
while (this->_is_running)
{
// Enter new epoch.
this->_global_epoch.fetch_add(1U);
if constexpr (config::local_garbage_collection())
{
// Collect local garbage.
// TODO: This might be buggy (even with cpu id, since threads could be interrupted within allocation)!
const auto core_id = mx::system::cpu::core_id();
for (auto worker_id = std::uint16_t(0U); worker_id < this->_count_channels; ++worker_id)
{
auto *garbage_task =
mx::tasking::runtime::new_task<ReclaimEpochGarbageTask>(core_id, *this, this->_allocator);
garbage_task->annotate(worker_id);
mx::tasking::runtime::spawn(*garbage_task);
}
}
else
{
// Collect global garbage of finished epochs.
this->reclaim_epoch_garbage();
}
// Wait some time until next epoch.
std::this_thread::sleep_until(std::chrono::system_clock::now() + config::epoch_interval());
}
}
void EpochManager::reclaim_epoch_garbage() noexcept
{
// Items logically removed in an epoch leq than
// this epoch can be removed physically.
const auto min_epoch = this->min_local_epoch();
// Items that could not be physically removed in this epoch
// and therefore have to be scheduled to the next one.
queue::List<resource::ResourceInterface> deferred_resources{};
resource::ResourceInterface *resource;
while ((resource = reinterpret_cast<resource::ResourceInterface *>(this->_global_garbage_queue.pop_front())) !=
nullptr)
{
if (resource->remove_epoch() < min_epoch)
{
resource->on_reclaim();
this->_allocator.free(static_cast<void *>(resource));
}
else
{
deferred_resources.push_back(resource);
}
}
// Resources that could not be deleted physically
// need to be deleted in next epochs.
if (deferred_resources.empty() == false)
{
this->_global_garbage_queue.push_back(deferred_resources.begin(), deferred_resources.end());
}
}
void EpochManager::reclaim_all() noexcept
{
if constexpr (config::local_garbage_collection())
{
for (auto worker_id = 0U; worker_id < this->_count_channels; ++worker_id)
{
resource::ResourceInterface *resource;
while ((resource = reinterpret_cast<resource::ResourceInterface *>(
this->_local_garbage_queues[worker_id].value().pop_front())) != nullptr)
{
resource->on_reclaim();
this->_allocator.free(static_cast<void *>(resource));
}
}
}
else
{
resource::ResourceInterface *resource;
while ((resource = reinterpret_cast<resource::ResourceInterface *>(this->_global_garbage_queue.pop_front())) !=
nullptr)
{
resource->on_reclaim();
this->_allocator.free(static_cast<void *>(resource));
}
}
}
void EpochManager::reset() noexcept
{
if (this->_allocator.is_free())
{
this->_global_epoch.store(0U);
for (auto worker_id = 0U; worker_id < tasking::config::max_cores(); ++worker_id)
{
_local_epochs[worker_id] = std::numeric_limits<epoch_t>::max();
}
}
}
mx::tasking::TaskResult ReclaimEpochGarbageTask::execute(const std::uint16_t worker_id)
{
// Items logically removed in an epoch leq than
// this epoch can be removed physically.
const auto min_epoch = this->_epoch_manager.min_local_epoch();
// Items that could not be physically removed in this epoch
// and therefore have to be scheduled to the next one.
queue::List<resource::ResourceInterface> deferred_resources{};
// Queue with channel-local garbage.
auto &garbage_queue = this->_epoch_manager.local_garbage(worker_id);
resource::ResourceInterface *resource;
while ((resource = reinterpret_cast<resource::ResourceInterface *>(garbage_queue.pop_front())) != nullptr)
{
if (resource->remove_epoch() < min_epoch)
{
resource->on_reclaim();
this->_allocator.free(static_cast<void *>(resource));
}
else
{
deferred_resources.push_back(resource);
}
}
// Resources that could not be deleted physically
// need to be deleted in next epochs.
if (deferred_resources.empty() == false)
{
garbage_queue.push_back(deferred_resources.begin(), deferred_resources.end());
}
return tasking::TaskResult::make_remove();
}

View File

@@ -0,0 +1,641 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h \
/home/mml/genode-igb/repos/libports/include/libc/component.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h:
/home/mml/genode-igb/repos/libports/include/libc/component.h:

View File

@@ -0,0 +1,183 @@
#pragma once
#include "epoch_t.h"
#include <array>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <mx/memory/config.h>
#include <mx/memory/worker_local_dynamic_size_allocator.h>
#include <mx/queue/mpsc.h>
#include <mx/resource/resource_interface.h>
#include <mx/system/builtin.h>
#include <mx/tasking/config.h>
#include <mx/tasking/task.h>
#include <mx/util/aligned_t.h>
#include <mx/util/core_set.h>
#include <mx/util/maybe_atomic.h>
#include <thread>
namespace mx::memory::reclamation {
class alignas(64) LocalEpoch
{
public:
constexpr LocalEpoch() noexcept = default;
~LocalEpoch() noexcept = default;
LocalEpoch &operator=(const epoch_t epoch) noexcept
{
_epoch = epoch;
return *this;
}
void enter(const std::atomic<epoch_t> &global_epoch) noexcept
{
_epoch.store(global_epoch.load(std::memory_order_seq_cst), std::memory_order_seq_cst);
}
void leave() noexcept { _epoch.store(std::numeric_limits<epoch_t>::max()); }
[[nodiscard]] epoch_t operator()() const noexcept { return _epoch.load(std::memory_order_seq_cst); }
private:
std::atomic<epoch_t> _epoch{std::numeric_limits<epoch_t>::max()};
};
/**
* The Epoch Manager manages periodic epochs which
* are used to protect reads against concurrent
* delete operations. Therefore, a global epoch
* will be incremented every 50ms (configurable).
* Read operations, on the other hand, will update
* their local epoch every time before reading an
* optimistic resource.
* When (logically) deleting an optimistic resource,
* the resource will be deleted physically, when
* every local epoch is greater than the epoch
* when the resource is deleted.
*/
class EpochManager
{
public:
EpochManager(const std::uint16_t count_channels, dynamic::local::Allocator &allocator,
util::maybe_atomic<bool> &is_running) noexcept
: _count_channels(count_channels), _is_running(is_running), _allocator(allocator)
{
}
EpochManager(const EpochManager &) = delete;
~EpochManager() = default;
LocalEpoch &operator[](const std::uint16_t worker_id) noexcept { return _local_epochs[worker_id]; }
/**
* @return Access to read to global epoch.
*/
[[nodiscard]] const std::atomic<epoch_t> &global_epoch() const noexcept { return _global_epoch; }
/**
* @return The minimal epoch of all channels.
*/
[[nodiscard]] epoch_t min_local_epoch() const noexcept
{
auto min_epoch = _local_epochs[0U]();
for (auto worker_id = 1U; worker_id < _count_channels; ++worker_id)
{
min_epoch = std::min(min_epoch, _local_epochs[worker_id]());
}
return min_epoch;
}
/**
* Adds an optimistic resource to garbage collection.
* @param resource Resource to logically delete.
*/
void add_to_garbage_collection(resource::ResourceInterface *resource,
[[maybe_unused]] const std::uint16_t owning_worker_id) noexcept
{
resource->remove_epoch(_global_epoch.load());
if constexpr (config::local_garbage_collection())
{
_local_garbage_queues[owning_worker_id].value().push_back(resource);
}
else
{
_global_garbage_queue.push_back(resource);
}
}
/**
* Called periodically by a separate thread.
*/
void enter_epoch_periodically();
/**
* Reclaims all garbage, mainly right before shut down tasking.
*/
void reclaim_all() noexcept;
/**
* Grants access to the local garbage queue of a specific channel.
*
* @param worker_id Channel Id.
* @return Local garbage queue.
*/
[[nodiscard]] queue::MPSC<resource::ResourceInterface> &local_garbage(const std::uint16_t worker_id) noexcept
{
return _local_garbage_queues[worker_id].value();
}
/**
* Reset all local and the global epoch to initial values
* if no memory is in use.
*/
void reset() noexcept;
private:
// Number of used channels; important for min-calculation.
const std::uint16_t _count_channels;
// Flag of the scheduler indicating the state of the system.
util::maybe_atomic<bool> &_is_running;
// Allocator to free collected resources.
dynamic::local::Allocator &_allocator;
// Global epoch, incremented periodically.
std::atomic<epoch_t> _global_epoch{0U};
// Local epochs, one for every channel.
alignas(64) std::array<LocalEpoch, tasking::config::max_cores()> _local_epochs;
// Queue that holds all logically deleted objects in a global space.
alignas(64) queue::MPSC<resource::ResourceInterface> _global_garbage_queue;
// Queues for every worker thread. Logically deleted objects are stored here
// whenever local garbage collection is used.
alignas(64) std::array<util::aligned_t<queue::MPSC<resource::ResourceInterface>>,
tasking::config::max_cores()> _local_garbage_queues;
/**
* Reclaims resources with regard to the epoch.
*/
void reclaim_epoch_garbage() noexcept;
};
class ReclaimEpochGarbageTask final : public tasking::TaskInterface
{
public:
constexpr ReclaimEpochGarbageTask(EpochManager &epoch_manager, dynamic::local::Allocator &allocator) noexcept
: _epoch_manager(epoch_manager), _allocator(allocator)
{
}
~ReclaimEpochGarbageTask() noexcept override = default;
tasking::TaskResult execute(std::uint16_t worker_id) override;
private:
EpochManager &_epoch_manager;
dynamic::local::Allocator &_allocator;
};
} // namespace mx::memory::reclamation

View File

@@ -0,0 +1,5 @@
#pragma once
#include <cstdint>
namespace mx::memory::reclamation {
using epoch_t = std::uint32_t;
}

View File

@@ -0,0 +1,103 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <functional>
namespace mx::memory {
/**
* Holds the memory address of an instance of the class T
* and decodes a 16bit core address within the memory address.
* The size of the tagged_ptr<T> is equal to T*.
*/
template <class T, typename I> class tagged_ptr
{
public:
constexpr tagged_ptr() noexcept
{
static_assert(sizeof(I) == 2U);
static_assert(sizeof(tagged_ptr) == 8U);
}
constexpr explicit tagged_ptr(T *pointer) noexcept : _object_pointer(std::uintptr_t(pointer)) {}
constexpr explicit tagged_ptr(T *pointer, const I information) noexcept
: _object_pointer(std::uintptr_t(pointer)), _information(information)
{
}
~tagged_ptr() noexcept = default;
/**
* @return The decoded info.
*/
inline I info() const noexcept { return _information; }
/**
* @return The memory address without the info.
*/
template <typename S = T> inline S *get() const noexcept { return reinterpret_cast<S *>(_object_pointer); }
/**
* Decodes the given info within the pointer.
*
* @param info Info to store in the tagged pointer.
*/
inline void reset(const I information) noexcept { _information = information; }
/**
* Replaces the internal pointer by a new one.
*
* @param new_pointer Pointer to the new memory object.
*/
inline void reset(T *new_pointer = nullptr) noexcept { _object_pointer = std::uintptr_t(new_pointer); }
T *operator->() const noexcept { return get(); }
explicit operator T *() const noexcept { return get(); }
explicit operator bool() const noexcept { return _object_pointer != 0U; }
explicit operator std::uintptr_t() const noexcept { return _object_pointer; }
tagged_ptr<T, I> &operator=(const tagged_ptr<T, I> &other) noexcept = default;
bool operator==(const tagged_ptr<T, I> &other) const noexcept { return other._object_pointer == _object_pointer; }
bool operator==(const T *other) const noexcept { return other == get(); }
bool operator==(std::nullptr_t) const noexcept { return _object_pointer == 0U; }
bool operator!=(const tagged_ptr<T, I> &other) const noexcept { return other.get() != get(); }
bool operator!=(std::nullptr_t) const noexcept { return _object_pointer != 0U; }
bool operator<(const tagged_ptr<T, I> &other) noexcept { return other.get() < get(); }
bool operator<=(const tagged_ptr<T, I> &other) noexcept { return other.get() <= get(); }
bool operator>(const tagged_ptr<T, I> &other) noexcept { return other.get() > get(); }
bool operator>=(const tagged_ptr<T, I> &other) noexcept { return other.get() >= get(); }
private:
/**
* Pointer to the instance of T, only 48bit are used.
*/
std::uintptr_t _object_pointer : 48 {0U};
/**
* Information stored within this pointer, remaining 16bit are used.
*/
I _information{};
} __attribute__((packed));
} // namespace mx::memory
namespace std {
template <class T, typename I> struct hash<mx::memory::tagged_ptr<T, I>>
{
std::size_t operator()(const mx::memory::tagged_ptr<T, I> &ptr) const noexcept
{
return std::hash<T *>().operator()(ptr.get());
}
};
} // namespace std

View File

@@ -0,0 +1,70 @@
#pragma once
#include "mx/memory/global_heap.h"
#include <cstdint>
#include <cstdlib>
#include <mx/system/cache.h>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace mx::memory {
/**
* Interface for task allocators (e.g. using systems malloc
* or the internal allocator).
*/
class TaskAllocatorInterface
{
public:
constexpr TaskAllocatorInterface() noexcept = default;
virtual ~TaskAllocatorInterface() noexcept = default;
/**
* Allocates memory for the given core.
* @param worker_id Worker to allocate memory for.
* @return Allocated memory.
*/
[[nodiscard]] virtual void *allocate(std::uint16_t worker_id) = 0;
/**
* Frees the memory at the given core.
* @param worker_id Worker to store free memory.
* @param address Address to free.
*/
virtual void free(std::uint16_t worker_id, void *address) noexcept = 0;
[[nodiscard]] virtual std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>
allocated_chunks() = 0;
};
/**
* Task allocator using the systems (aligned_)malloc/free interface.
*/
template <std::size_t S> class SystemTaskAllocator final : public TaskAllocatorInterface
{
public:
constexpr SystemTaskAllocator() noexcept = default;
~SystemTaskAllocator() noexcept override = default;
/**
* @return Allocated memory using systems malloc (but aligned).
*/
[[nodiscard]] void *allocate(const std::uint16_t /*worker_id*/) override
{
return memory::GlobalHeap::allocate_cache_line_aligned(S);
}
/**
* Frees the given memory using systems free.
* @param address Memory to free.
*/
void free(const std::uint16_t /*worker_id*/, void *address) noexcept override { std::free(address); }
[[nodiscard]] std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>
allocated_chunks() override
{
return std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>{};
}
};
} // namespace mx::memory

View File

@@ -0,0 +1,433 @@
#include "worker_local_dynamic_size_allocator.h"
#include "alignment_helper.h"
#include "global_heap.h"
#include <cassert>
using namespace mx::memory::dynamic::local;
std::tuple<std::set<AllocatedBlock::FreeHeaderDescriptor>::iterator, bool, std::size_t, std::size_t> AllocatedBlock::
find_free_header(const std::size_t alignment, std::size_t size) const
{
/// This is the minimal size we need to allocate,
/// if and only if the address is perfectly aligned.
/// However, we can filter out all free blocks with a
/// size lower than that.
const auto size_including_header = size + sizeof(AllocationHeader);
for (auto iterator = this->_free_header.begin(); iterator != this->_free_header.end(); ++iterator)
{
const auto &descriptor = *iterator;
if (descriptor.size() >= size_including_header)
{
const auto free_block_start_address = std::uintptr_t(descriptor.header());
/// Calculate the number of bytes needed to fullfill the wanted aligntment for
/// the given free block.
/// The real allocation block will look like:
/// | (additional_size_to_fulfill_alignment[=0])(AllocationHeader)(size [+rest if free header has too
/// less left]) |
const auto allocation_start_address = free_block_start_address + sizeof(AllocationHeader);
const auto additional_size_to_fulfill_alignment = alignment - allocation_start_address % alignment;
/// Check if the block has enough space to fullfill the alignment.
const auto size_to_fulfill_alignment = size_including_header + additional_size_to_fulfill_alignment;
if (descriptor.size() >= size_to_fulfill_alignment)
{
/// Check if we should split the block.
const auto remaining_size = descriptor.size() - size_to_fulfill_alignment;
/// The header will be take totally because the rest would be too small.
/// We will increase the size (full free size - size for header - size before header for alignment).
return std::make_tuple(iterator, remaining_size > 256U, size_including_header,
additional_size_to_fulfill_alignment);
}
}
}
return std::make_tuple(this->_free_header.end(), false, 0U, 0U);
}
void *AllocatedBlock::allocate(const std::uint16_t worker_id, const std::uint8_t numa_node_id,
const std::size_t alignment, std::size_t size)
{
const auto [iterator, is_split, size_including_header, additional_size_to_fulfill_alignment] =
this->find_free_header(alignment, size);
if (iterator == this->_free_header.end())
{
return nullptr;
}
auto *free_header = iterator->header();
const auto free_header_size = iterator->size();
auto next_iterator = this->_free_header.erase(iterator);
const auto size_to_fulfill_alignment = size_including_header + additional_size_to_fulfill_alignment;
if (is_split)
{
const auto remaining_size = free_header->size() - size_to_fulfill_alignment;
auto *new_free_header = new (reinterpret_cast<void *>(std::uintptr_t(free_header) + size_to_fulfill_alignment))
FreeHeader(remaining_size, numa_node_id, this->_id);
this->_free_header.insert(next_iterator, FreeHeaderDescriptor{new_free_header, remaining_size});
}
else
{
size = free_header_size - sizeof(AllocationHeader) - additional_size_to_fulfill_alignment;
}
const auto header_address = std::uintptr_t(free_header) + additional_size_to_fulfill_alignment;
auto *allocation_header = new (reinterpret_cast<void *>(header_address))
AllocationHeader(size, additional_size_to_fulfill_alignment, worker_id, numa_node_id, this->_id);
return allocation_header + 1U;
}
void AllocatedBlock::free(AllocationHeader *allocation_header)
{
assert(this->_id == allocation_header->block_id());
const auto ptr = std::uintptr_t(allocation_header) - allocation_header->unused_size_before_header();
auto *free_header = new (reinterpret_cast<void *>(ptr)) FreeHeader(
allocation_header->unused_size_before_header() + sizeof(AllocationHeader) + allocation_header->size(),
allocation_header->numa_node_id(), this->_id);
this->refund(free_header);
}
void AllocatedBlock::refund(FreeHeader *free_header)
{
assert(std::uintptr_t(free_header) >= std::uintptr_t(this->_data));
assert((std::uintptr_t(free_header) + free_header->size()) <= (std::uintptr_t(this->_data) + this->_size));
auto descriptor = FreeHeaderDescriptor{free_header, free_header->size()};
/// Try to merge with the descriptor next to the free one.
auto next_descriptor = this->_free_header.upper_bound(descriptor);
if (next_descriptor != this->_free_header.end())
{
if (free_header->is_right_neighbour(next_descriptor->header()))
{
descriptor.grow(next_descriptor->size());
next_descriptor = this->_free_header.erase(next_descriptor);
}
}
auto iterator = this->_free_header.insert(next_descriptor, descriptor);
if (iterator != this->_free_header.begin())
{
auto inserted_iterator = iterator--;
if (iterator->header()->is_right_neighbour(
free_header)) /// Iterator is now the free header before inserted_iterator.
{
auto new_descriptor = *iterator;
new_descriptor.grow(inserted_iterator->size());
/// Remove the i-1 and i.
this->_free_header.erase(inserted_iterator);
auto next_iterator = this->_free_header.erase(iterator);
/// Re-insert as a grown header.
this->_free_header.insert(next_iterator, new_descriptor);
}
}
}
WorkerHeap::WorkerHeap(std::uint16_t worker_id, std::uint8_t numa_node_id)
: _worker_id(worker_id), _numa_node_id(numa_node_id)
{
}
WorkerHeap::WorkerHeap(mx::memory::dynamic::local::WorkerHeap &&other) noexcept
: _worker_id(other._worker_id), _numa_node_id(other._numa_node_id), _next_block_id(other._next_block_id)
{
for (auto i = 0U; i < config::max_numa_nodes(); ++i)
{
this->_allocated_blocks[i] = std::move(other._allocated_blocks[i]);
this->_allocated_block_indices[i] = std::move(other._allocated_block_indices[i]);
FreeHeader *free_header;
while ((free_header = other._remote_free_lists[i].pop_front()) != nullptr)
{
this->_remote_free_lists[i].push_back(free_header);
}
}
}
void *WorkerHeap::allocate(const std::uint8_t numa_node_id, const std::size_t alignment, const std::size_t size)
{
/// (1) Check all blocks for free memory.
auto &numa_blocks = this->_allocated_blocks[numa_node_id];
for (auto i = std::int64_t(numa_blocks.size() - 1U); i >= 0; --i)
{
auto *allocated_block = numa_blocks[i].allocate(this->_worker_id, numa_node_id, alignment, size);
if (allocated_block != nullptr)
{
return allocated_block;
}
}
/// (2) Check free list from other cores for free memory.
FreeHeader *header;
while ((header = this->_remote_free_lists[numa_node_id].pop_front()) != nullptr)
{
header->next(nullptr);
const auto qualifies = header->size() >= size;
auto &index = this->_allocated_block_indices[header->numa_node_id()];
if (auto iterator = index.find(header->block_id()); iterator != index.end())
{
auto &block = this->_allocated_blocks[header->numa_node_id()][iterator->second];
assert(block.id() == header->block_id());
block.refund(header);
if (qualifies)
{
auto *allocation = block.allocate(this->_worker_id, numa_node_id, alignment, size);
if (allocation != nullptr)
{
return allocation;
}
}
}
}
/// (3) Allocate a new block.
const auto size_to_alloc_from_global_heap = std::max<std::size_t>(
AllocatedBlock::DEFAULT_SIZE_IN_BYTES, alignment_helper::next_multiple(size + sizeof(AllocationHeader), 64UL));
auto *data = GlobalHeap::allocate(numa_node_id, size_to_alloc_from_global_heap);
auto &allocated_block = this->_allocated_blocks[numa_node_id].emplace_back(this->_next_block_id++, numa_node_id,
size_to_alloc_from_global_heap, data);
/// Update the index.
this->_allocated_block_indices[numa_node_id].insert(
std::make_pair(allocated_block.id(), this->_allocated_blocks[numa_node_id].size() - 1U));
return allocated_block.allocate(this->_worker_id, numa_node_id, alignment, size);
}
void WorkerHeap::free(AllocationHeader *allocated_block)
{
auto &index = this->_allocated_block_indices[allocated_block->numa_node_id()];
if (auto iterator = index.find(allocated_block->block_id()); iterator != index.end())
{
this->_allocated_blocks[allocated_block->numa_node_id()][iterator->second].free(allocated_block);
return;
}
}
void WorkerHeap::release_free_memory()
{
this->refund_remote_freed_memory();
for (auto numa_node_id = 0U; numa_node_id < this->_allocated_blocks.size(); ++numa_node_id)
{
auto &allocated_blocks = this->_allocated_blocks[numa_node_id];
allocated_blocks.erase(std::remove_if(allocated_blocks.begin(), allocated_blocks.end(),
[](const auto &block) { return block.is_free(); }),
allocated_blocks.end());
auto &index = this->_allocated_block_indices[numa_node_id];
index.clear();
for (auto i = 0UL; i < allocated_blocks.size(); ++i)
{
index.insert(std::make_pair(allocated_blocks[i].id(), i));
}
}
}
void WorkerHeap::release_all_memory()
{
for (auto &allocated_blocks : this->_allocated_blocks)
{
allocated_blocks.clear();
}
for (auto &index : this->_allocated_block_indices)
{
index.clear();
}
}
void WorkerHeap::refund_remote_freed_memory()
{
for (auto i = 0U; i < this->_remote_free_lists.max_size(); ++i)
{
/// Check local numa region first.
const auto nid = (this->_numa_node_id + i) & (this->_remote_free_lists.max_size() - 1U);
FreeHeader *header;
while ((header = this->_remote_free_lists[nid].pop_front()) != nullptr)
{
header->next(nullptr);
auto &index = this->_allocated_block_indices[header->numa_node_id()];
if (auto iterator = index.find(header->block_id()); iterator != index.end())
{
auto &block = this->_allocated_blocks[header->numa_node_id()][iterator->second];
assert(block.id() == header->block_id());
block.refund(header);
}
}
}
}
void WorkerHeap::initialize(const std::uint8_t numa_nodes)
{
for (auto numa_node_id = 0U; numa_node_id < numa_nodes; ++numa_node_id)
{
auto &index = this->_allocated_block_indices[numa_node_id];
if (index.max_size() < 1024U)
{
index.reserve(1024U);
}
if (this->_allocated_blocks[numa_node_id].empty())
{
const auto size = AllocatedBlock::DEFAULT_SIZE_IN_BYTES *
(1U + (static_cast<std::uint8_t>(numa_node_id == this->_numa_node_id) * 3U));
auto *data = GlobalHeap::allocate(numa_node_id, size);
auto &block = this->_allocated_blocks[numa_node_id].emplace_back(this->_next_block_id++, numa_node_id, size, data);
index.insert(std::make_pair(block.id(), this->_allocated_blocks[numa_node_id].size() - 1U));
}
}
}
bool WorkerHeap::is_free() const noexcept
{
for (const auto &allocated_blocks : this->_allocated_blocks)
{
for (const auto &block : allocated_blocks)
{
if (block.is_free() == false)
{
return false;
}
}
}
return true;
}
Allocator::Allocator(const util::core_set &cores) : _count_workers(cores.count_cores())
{
this->_worker_local_heaps = reinterpret_cast<WorkerHeap *>(
GlobalHeap::allocate_cache_line_aligned(sizeof(WorkerHeap) * cores.count_cores()));
for (auto i = std::uint16_t(0U); i < cores.count_cores(); ++i)
{
auto numa_node_id = cores.numa_node_id(i);
new (reinterpret_cast<void *>(&this->_worker_local_heaps[i])) WorkerHeap(i, numa_node_id);
this->_numa_node_ids[i] = numa_node_id;
}
}
void Allocator::initialize_heap(const std::uint16_t worker_id, const std::uint8_t count_numa_nodes)
{
this->_worker_local_heaps[worker_id].initialize(count_numa_nodes);
}
Allocator::~Allocator()
{
for (auto i = 0U; i < this->_count_workers; ++i)
{
this->_worker_local_heaps[i].~WorkerHeap();
}
std::free(this->_worker_local_heaps);
}
void *Allocator::allocate(const std::uint16_t worker_id, const std::uint8_t numa_node_id, const std::size_t alignment,
const std::size_t size)
{
return this->_worker_local_heaps[worker_id].allocate(numa_node_id, alignment, size);
}
void Allocator::free(const std::uint16_t calling_worker_id, void *pointer)
{
auto *allocation_header = reinterpret_cast<AllocationHeader *>(std::uintptr_t(pointer) - sizeof(AllocationHeader));
auto &heap = this->_worker_local_heaps[allocation_header->worker_id()];
if (allocation_header->worker_id() == calling_worker_id)
{
heap.free(allocation_header);
}
else
{
heap.free(this->_numa_node_ids[calling_worker_id], allocation_header);
}
}
void Allocator::free(void *pointer)
{
auto *allocation_header = reinterpret_cast<AllocationHeader *>(std::uintptr_t(pointer) - sizeof(AllocationHeader));
auto &heap = _worker_local_heaps[allocation_header->worker_id()];
heap.free(system::cpu::node_id(), allocation_header);
}
void Allocator::reset(const util::core_set &cores, bool force_free_memory)
{
if (force_free_memory)
{
for (auto i = 0U; i < this->_count_workers; ++i)
{
this->_worker_local_heaps[i].release_all_memory();
}
}
else
{
for (auto i = 0U; i < this->_count_workers; ++i)
{
this->_worker_local_heaps[i].release_free_memory();
}
}
if (this->_count_workers < cores.count_cores())
{
const auto old_count_workers = std::exchange(this->_count_workers, cores.count_cores());
auto *old_local_worker_heaps = this->_worker_local_heaps;
auto *new_local_worker_heaps = reinterpret_cast<WorkerHeap *>(
GlobalHeap::allocate_cache_line_aligned(sizeof(WorkerHeap) * cores.count_cores()));
/// Re-initialize old workers on new storage.
auto worker_id = 0U;
for (; worker_id < old_count_workers; ++worker_id)
{
const auto numa_node_id = cores.numa_node_id(worker_id);
new (reinterpret_cast<void *>(&new_local_worker_heaps[worker_id]))
WorkerHeap(std::move(old_local_worker_heaps[worker_id]));
this->_numa_node_ids[worker_id] = numa_node_id;
}
/// Create new workers on new storage.
for (; worker_id < cores.count_cores(); ++worker_id)
{
const auto numa_node_id = cores.numa_node_id(worker_id);
new (reinterpret_cast<void *>(&new_local_worker_heaps[worker_id])) WorkerHeap(worker_id, numa_node_id);
this->_numa_node_ids[worker_id] = numa_node_id;
}
/// Free old storage.
std::free(old_local_worker_heaps);
this->_worker_local_heaps = new_local_worker_heaps;
}
}
bool Allocator::is_free() const noexcept
{
for (auto i = 0U; i < this->_count_workers; ++i)
{
if (this->_worker_local_heaps[i].is_free() == false)
{
return false;
}
}
return true;
}

View File

@@ -0,0 +1,521 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:

View File

@@ -0,0 +1,345 @@
#pragma once
#include "config.h"
#include "global_heap.h"
#include <array>
#include <cassert>
#include <cstdint>
#include <mx/queue/mpsc.h>
#include <mx/tasking/config.h>
#include <mx/tasking/task.h>
#include <mx/util/core_set.h>
#include <set>
namespace mx::memory::dynamic::local {
/**
* Header of a free element.
*/
class FreeHeader
{
public:
constexpr FreeHeader(const std::size_t size, const std::uint8_t numa_node_id, const std::uint32_t block_id) noexcept
: _size(size), _numa_node_id(numa_node_id), _block_id(block_id)
{
}
~FreeHeader() noexcept = default;
[[nodiscard]] std::size_t size() const noexcept { return _size; }
[[nodiscard]] std::uint8_t numa_node_id() const noexcept { return _numa_node_id; }
[[nodiscard]] std::uint32_t block_id() const noexcept { return _block_id; }
[[nodiscard]] FreeHeader *next() const noexcept { return _next; }
void next(FreeHeader *next) noexcept
{
assert(next != this);
_next = next;
}
[[nodiscard]] bool is_right_neighbour(FreeHeader *possible_right_neighbour)
{
assert(this->_block_id == possible_right_neighbour->_block_id);
return (std::uintptr_t(this) + _size) == std::uintptr_t(possible_right_neighbour);
}
void append(FreeHeader *other) { grow(other->_size); }
void grow(const std::size_t size) { _size += size; }
private:
/// Size of the full block, including the header size.
/// We include the header size because that is the size that is usable for allocation.
std::size_t _size;
/// Id of the numa node the block was allocated in. Needed when returning
/// remote free memory.
const std::uint8_t _numa_node_id;
/// Id of the block within the worker heap.
const std::uint32_t _block_id;
/// Will be used for allocation within allocation block
/// and for queue in remote free list.
FreeHeader *_next{nullptr};
};
/**
* Header of a block that is allocated.
*/
class AllocationHeader
{
public:
constexpr AllocationHeader(const std::size_t size, const std::uint16_t unused_size_before_header,
const std::uint16_t worker_id, const std::uint8_t numa_node_id,
const std::uint32_t block_id) noexcept
: _size(size), _unused_size_before_header(unused_size_before_header), _worker_id(worker_id),
_numa_node_id(numa_node_id), _block_id(block_id)
{
}
~AllocationHeader() noexcept = default;
[[nodiscard]] std::size_t size() const noexcept { return _size; }
[[nodiscard]] std::uint16_t unused_size_before_header() const noexcept { return _unused_size_before_header; }
[[nodiscard]] std::uint16_t worker_id() const noexcept { return _worker_id; }
[[nodiscard]] std::uint8_t numa_node_id() const noexcept { return _numa_node_id; }
[[nodiscard]] std::uint32_t block_id() const noexcept { return _block_id; }
[[nodiscard]] FreeHeader *to_free_header() const noexcept
{
const auto size = _size + _unused_size_before_header + sizeof(AllocationHeader);
return new (reinterpret_cast<void *>(std::uintptr_t(this) - _unused_size_before_header))
FreeHeader(size, _numa_node_id, _block_id);
}
private:
/// Size of the block after the header.
const std::size_t _size;
/// Size in front of the header that is not used but needed for alignment.
const std::uint16_t _unused_size_before_header;
/// Id of the worker.
const std::uint16_t _worker_id;
/// Numa region the block was allocated in.
std::uint8_t _numa_node_id;
/// Id of the block within the worker heap.
const std::uint32_t _block_id;
};
/**
* A block allocated from global memory into a worker-local heap.
*/
class AllocatedBlock
{
private:
class FreeHeaderDescriptor
{
public:
constexpr FreeHeaderDescriptor(FreeHeader *header, const std::size_t size) noexcept
: _header(header), _size(size)
{
}
~FreeHeaderDescriptor() noexcept = default;
[[nodiscard]] FreeHeader *header() const noexcept { return _header; }
[[nodiscard]] std::size_t size() const noexcept { return _size; }
void grow(const std::size_t size) noexcept
{
_size += size;
_header->grow(size);
}
bool operator<(const FreeHeaderDescriptor other) const noexcept
{
return std::uintptr_t(_header) < std::uintptr_t(other._header);
}
private:
FreeHeader *_header;
std::size_t _size;
};
public:
static inline constexpr auto DEFAULT_SIZE_IN_BYTES = 1024UL * 1024UL * 128U;
AllocatedBlock(const std::uint32_t id, const std::uint8_t numa_node_id, const std::size_t size, void *data) noexcept
: _id(id), _numa_node_id(numa_node_id), _size(size), _data(data)
{
_free_header.insert(FreeHeaderDescriptor{new (_data) FreeHeader(size, 0U, id), size});
}
AllocatedBlock(AllocatedBlock &&other) noexcept
: _id(other._id), _numa_node_id(other._numa_node_id), _size(other._size), _data(std::exchange(other._data, nullptr)),
_free_header(std::move(other._free_header))
{
}
~AllocatedBlock()
{
if (_data != nullptr)
{
GlobalHeap::free(std::exchange(_data, nullptr), _size, _numa_node_id);
}
}
AllocatedBlock &operator=(AllocatedBlock &&other) noexcept
{
_id = other._id;
_size = other._size;
_data = std::exchange(other._data, nullptr);
_free_header = std::move(other._free_header);
return *this;
}
[[nodiscard]] std::uint32_t id() const noexcept { return _id; }
[[nodiscard]] void *data() const noexcept { return _data; }
[[nodiscard]] std::size_t size() const noexcept { return _size; }
[[nodiscard]] void *allocate(std::uint16_t worker_id, std::uint8_t numa_node_id, std::size_t alignment,
std::size_t size);
void free(AllocationHeader *allocation_header);
void refund(FreeHeader *free_header);
[[nodiscard]] bool is_free() const noexcept
{
return _free_header.size() == 1U && _free_header.begin()->size() == _size;
}
[[nodiscard]] std::tuple<std::set<FreeHeaderDescriptor>::iterator, bool, std::size_t, std::size_t> find_free_header(
std::size_t alignment, std::size_t size) const;
private:
std::uint32_t _id;
std::uint8_t _numa_node_id;
std::size_t _size;
void *_data;
std::set<FreeHeaderDescriptor> _free_header;
};
class alignas(64) WorkerHeap
{
public:
WorkerHeap(std::uint16_t worker_id, std::uint8_t numa_node_id);
WorkerHeap(WorkerHeap &&other) noexcept;
~WorkerHeap() = default;
/**
* Allocates memory from the list of allocated blocks,
* or, when no memory available, from the remote free list.
* If, however, no memory is available, a new block is allocated.
*
* @param numa_node_id NUMA node id to allocate memory for.
* @param alignment Alignment of the allocated address.
* @param size Size to allocate.
* @return Pointer to the allocated memory.
*/
void *allocate(std::uint8_t numa_node_id, std::size_t alignment, std::size_t size);
/**
* Frees memory from a remote worker.
* The memory will be freed lazy, using
* a list that will be used for allocation.
*
* @param calling_numa_id NUMA node id of the freeing worker.
* @param allocated_item Header to the allocation.
*/
void free(const std::uint8_t calling_numa_id, AllocationHeader *allocated_item)
{
auto *free_header = allocated_item->to_free_header();
_remote_free_lists[calling_numa_id].push_back(free_header);
}
/**
* Frees a local-worker allocated block.
*
* @param allocated_block Header to the allocation.
*/
void free(AllocationHeader *allocated_block);
/**
* Releases all free blocks.
*/
void release_free_memory();
/**
* Releases all blocks.
*/
void release_all_memory();
/**
* Refunds memory from the remote list.
*/
void refund_remote_freed_memory();
void initialize(std::uint8_t numa_nodes);
[[nodiscard]] bool is_free() const noexcept;
private:
const std::uint16_t _worker_id;
const std::uint8_t _numa_node_id;
std::uint32_t _next_block_id{0U};
/// Every worker can allocate blocks for every numa region.
std::array<std::vector<AllocatedBlock>, config::max_numa_nodes()> _allocated_blocks;
/// Index for every numa node that points from block_id to index in _allocated_blocks.
std::array<std::unordered_map<std::uint32_t, std::uint64_t>, config::max_numa_nodes()> _allocated_block_indices;
std::array<queue::MPSC<FreeHeader>, config::max_numa_nodes()> _remote_free_lists;
};
class Allocator
{
public:
Allocator(const util::core_set &cores);
~Allocator();
void initialize_heap(std::uint16_t worker_id, std::uint8_t count_numa_nodes);
void *allocate(std::uint16_t worker_id, std::uint8_t numa_node_id, std::size_t alignment, std::size_t size);
void free(std::uint16_t calling_worker_id, void *pointer);
/**
* Frees the memory always as a "remote" caller.
* For performance reason, this should be used carefully!
*
* @param pointer Pointer to the data.
*/
void free(void *pointer);
/**
* Resets all worker-local allocators.
*
* @param cores New core set (may change in contrast to the current).
* @param force_free_memory If set to true, memory should be freed.
*/
void reset(const util::core_set &cores, bool force_free_memory);
/**
* Cleans the remote free'd memory of a worker-local heap.
*
* @param worker_id Worker id to clean up.
*/
void clean_up_remote_freed_memory(const std::uint16_t worker_id)
{
_worker_local_heaps[worker_id].refund_remote_freed_memory();
}
/**
* @return True, if all blocks of all numa regions are free.
*/
[[nodiscard]] bool is_free() const noexcept;
private:
/// Map from worker id to numa node id.
std::array<std::uint8_t, tasking::config::max_cores()> _numa_node_ids;
/// One heap for every worker.
WorkerHeap *_worker_local_heaps;
std::uint16_t _count_workers;
};
class CleanUpMemoryTask final : public tasking::TaskInterface
{
public:
constexpr CleanUpMemoryTask(Allocator &allocator) noexcept : _allocator(allocator) {}
~CleanUpMemoryTask() noexcept override = default;
tasking::TaskResult execute(const std::uint16_t worker_id) override
{
_allocator.clean_up_remote_freed_memory(worker_id);
return tasking::TaskResult::make_remove();
}
private:
Allocator &_allocator;
};
} // namespace mx::memory::dynamic::local

View File

@@ -0,0 +1,179 @@
#pragma once
#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <mx/memory/global_heap.h>
#include <mx/system/builtin.h>
namespace mx::queue {
/**
* Multi producer, multi consumer queue with a fixed number of slots.
* Every thread can push and pop values into the queue without using latches.
*
* Inspired by http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
*/
template <typename T> class BoundMPMC
{
public:
explicit BoundMPMC(const std::uint64_t capacity) noexcept : _capacity(capacity)
{
_storage =
new (memory::GlobalHeap::allocate_cache_line_aligned(sizeof(std::pair<std::atomic_uint64_t, T>) * capacity))
std::pair<std::atomic_uint64_t, T>[capacity];
std::memset(static_cast<void *>(_storage), 0, sizeof(std::pair<std::atomic_uint64_t, T>) * capacity);
for (auto i = 0U; i < capacity; ++i)
{
std::get<0>(_storage[i]).store(i, std::memory_order_relaxed);
}
}
~BoundMPMC() noexcept { std::free(_storage); }
// BoundMPMC(const BoundMPMCQueue<T> &) = delete;
// BoundMPMC(BoundMPMCQueue<T> &&) = delete;
//
// BoundMPMC<T> &operator=(const BoundMPMCQueue<T> &) = delete;
// BoundMPMC<T> &operator=(BoundMPMCQueue<T> &&) = delete;
[[nodiscard]] bool empty() const noexcept { return _head.load() == _tail.load(); }
/**
* Inserts the given value.
* May block until a slot is available.
*
* @param item Data to insert.
*/
void push_back(const T &item) noexcept
{
while (try_push_back(item) == false)
{
system::builtin::pause();
}
}
/**
* Takes out the next value.
* May block until data is available.
*
* @return The popped value.
*/
T pop_front() noexcept
{
T item;
while (try_pop_front(item) == false)
{
system::builtin::pause();
}
return item;
}
/**
* Tries to take out the next value or the given default value,
* if no data is available.
*
* @param default_value Data that will be returned if no data is available.
* @return Popped data or default value.
*/
T pop_front_or(const T &default_value) noexcept
{
T item;
if (try_pop_front(item))
{
return item;
}
return default_value;
}
/**
* Tries to insert value into the queue.
*
* @param item Item to insert.
* @return True, when successful inserted; false if no slot was available.
*/
bool try_push_back(const T &item) noexcept
{
auto pos = _head.load(std::memory_order_relaxed);
std::uint64_t slot;
for (;;)
{
slot = pos % _capacity;
const auto sequence = std::get<0>(_storage[slot]).load(std::memory_order_acquire);
const auto difference = std::int64_t(sequence) - std::int64_t(pos);
if (difference == 0)
{
if (_head.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
{
break;
}
}
else if (difference < 0)
{
return false;
}
else
{
pos = _head.load(std::memory_order_relaxed);
}
}
std::get<1>(_storage[slot]) = item;
std::get<0>(_storage[slot]).store(pos + 1, std::memory_order_release);
return true;
}
/**
* Tries to take the next value.
*
* @param return_item Item where the next value will be stored.
* @return True, when pop was successful; false if no data was available.
*/
bool try_pop_front(T &return_item) noexcept
{
auto pos = _tail.load(std::memory_order_relaxed);
std::uint64_t slot;
for (;;)
{
slot = pos % _capacity;
const auto sequence = std::get<0>(_storage[slot]).load(std::memory_order_acquire);
const auto difference = std::int64_t(sequence) - std::int64_t(pos + 1);
if (difference == 0)
{
if (_tail.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
{
break;
}
}
else if (difference < 0)
{
return false;
}
else
{
pos = _tail.load(std::memory_order_relaxed);
}
}
return_item = std::get<1>(_storage[slot]);
std::get<0>(_storage[slot]).store(pos + _capacity, std::memory_order_release);
return true;
}
private:
// Capacity of the queue.
const std::uint64_t _capacity;
// Array of status flags and data slots.
std::pair<std::atomic_uint64_t, T> *_storage;
// Index of the head.
alignas(64) std::atomic_uint64_t _head{0U};
// Index of the tail.
alignas(64) std::atomic_uint64_t _tail{0U};
};
} // namespace mx::queue

View File

@@ -0,0 +1,39 @@
#pragma once
#include <cstdint>
#include <cstdlib>
namespace mx::queue
{
template<typename T>
class DynamicRingpuffer
{
public:
DynamicRingpuffer()
{
_data = std::aligned_alloc(64U, sizeof(T*) * _capacity);
}
~DynamicRingpuffer()
{
std::free(_data);
}
void push_back(T* item)
{
const auto index = (_head++) & _capacity;
if (index == _tail)
{
/// TODO: Reallocate
}
_data[index] = item;
}
private:
T **_data;
std::uint64_t _capacity {1024U};
std::uint64_t _head {0U};
std::uint64_t _tail {0U};
};
}

View File

@@ -0,0 +1,149 @@
#pragma once
#include <utility>
namespace mx::queue {
/**
* Single producer and consumer queue. This queue is not thread safe.
*/
template <class T> class alignas(64) List
{
public:
constexpr List() noexcept = default;
~List() noexcept = default;
/**
* Inserts an item into the queue.
* @param item Item to be inserted.
*/
void push_back(T *item) noexcept
{
item->next(nullptr);
if (_tail != nullptr) [[likely]]
{
_tail->next(item);
_tail = item;
}
else
{
_head = _tail = item;
}
}
/**
* Inserts a list of items into the queue.
* The items have to be concatenated.
*
* @param first First item to be inserted.
* @param last Last item to be inserted.
*/
void push_back(T *first, T *last) noexcept
{
last->next(nullptr);
if (_tail != nullptr) [[likely]]
{
_tail->next(first);
_tail = last;
}
else
{
_head = first;
_tail = last;
}
}
/**
* @return Begin of the queue.
*/
[[nodiscard]] T *begin() noexcept { return _head; }
/**
* @return End of the queue.
*/
[[nodiscard]] const T *end() const noexcept { return _tail; }
/**
* @return End of the queue.
*/
[[nodiscard]] T *end() noexcept { return _tail; }
/**
* @return True, when the queue is empty.
*/
[[nodiscard]] bool empty() const noexcept { return _head == nullptr; }
/**
* @return Takes and removes the first item from the queue.
*/
T *pop_front() noexcept
{
if (_head == nullptr) [[unlikely]]
{
return nullptr;
}
auto *head = _head;
auto *new_head = head->next();
if (new_head == nullptr) [[unlikely]]
{
_tail = nullptr;
}
_head = new_head;
return head;
}
std::pair<T *, std::uint16_t> pop_front(const std::uint16_t limit) noexcept
{
auto count = 0U;
auto *head = _head;
auto *current = _head;
do
{
current = current->next();
++count;
} while (count < limit && current != nullptr);
_head = current;
if (current == nullptr) [[unlikely]]
{
_tail = nullptr;
}
return std::make_pair(head, count);
}
/**
* Pops all items from the list. The items will be concatenated.
*
* @return Pair of first and last task
*/
[[nodiscard]] std::pair<T *, T *> pop() noexcept
{
if (_head == nullptr)
{
return std::make_pair(nullptr, nullptr);
}
if (_head == _tail)
{
auto *head = _head;
_head = _tail = nullptr;
return std::make_pair(head, nullptr);
}
auto *head = std::exchange(_head, nullptr);
auto *tail = std::exchange(_tail, nullptr);
return std::make_pair(head, tail);
}
private:
// Pointer to the head.
T *_head{nullptr};
// Pointer to the tail.
T *_tail{nullptr};
};
} // namespace mx::queue

View File

@@ -0,0 +1,165 @@
#pragma once
#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <mx/system/cache.h>
#include <utility>
namespace mx::queue {
/**
* Multi producer, single consumer queue with unlimited slots.
* Every thread can push values into the queue without using latches.
*
* Inspired by http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue
*/
template <class T> class MPSC
{
public:
constexpr MPSC() noexcept
: _head(reinterpret_cast<T *>(_stub.data())), _tail(reinterpret_cast<T *>(_stub.data())),
_end(reinterpret_cast<T *>(_stub.data()))
{
}
~MPSC() noexcept = default;
/**
* Inserts the given item into the queue.
* @param item Item to insert.
*/
void push_back(T *item) noexcept
{
item->next(nullptr);
auto *prev = __atomic_exchange_n(&_head, item, __ATOMIC_RELAXED);
prev->next(item);
}
/**
* Inserts all items between begin and end into the queue.
* Items must be linked among themselves.
* @param begin First item to insert.
* @param end Last item to insert.
*/
void push_back(T *begin, T *end) noexcept
{
end->next(nullptr);
auto *old_head = __atomic_exchange_n(&_head, end, __ATOMIC_RELAXED);
old_head->next(begin);
}
/**
* @return End of the queue.
*/
[[nodiscard]] const T *end() const noexcept { return _end; }
/**
* @return True, when the queue is empty.
*/
[[nodiscard]] bool empty() const noexcept
{
return _tail == _end && reinterpret_cast<T const &>(_stub).next() == nullptr;
}
/**
* @return Takes and removes the first item from the queue.
*/
[[nodiscard]] T *pop_front() noexcept;
/**
* Pops all items from the list. The items will be concatenated.
* This operation is NOT thread safe.
*
* @return Pair of first and last task
*/
[[nodiscard]] std::pair<T *, T *> pop() noexcept;
private:
// Head of the queue (accessed by every producer).
alignas(64) T *_head;
// Tail of the queue (accessed by the consumer and producers if queue is empty)-
alignas(64) T *_tail;
// Pointer to the end.
alignas(16) T *const _end;
// Dummy item for empty queue.
alignas(64) std::array<std::byte, sizeof(T)> _stub = {};
};
template <class T> T *MPSC<T>::pop_front() noexcept
{
auto *tail = this->_tail;
auto *next = tail->next();
if (tail == this->_end)
{
if (next == nullptr)
{
return nullptr;
}
this->_tail = next;
tail = next;
next = next->next();
}
if (next != nullptr)
{
this->_tail = next;
return tail;
}
const auto *head = this->_head;
if (tail != head)
{
return nullptr;
}
this->push_back(this->_end);
next = tail->next();
if (next != nullptr)
{
this->_tail = next;
return tail;
}
return nullptr;
}
template <class T> std::pair<T *, T *> MPSC<T>::pop() noexcept
{
T *head = nullptr;
/// Head and tail are interchanged, head is tail and tail is head.
if (this->_tail != nullptr)
{
if (this->_tail != this->_end)
{
head = this->_tail;
}
else
{
head = this->_tail->next();
}
}
if (head == nullptr)
{
return std::make_pair(nullptr, nullptr);
}
if (this->_head == nullptr || this->_head == this->_end)
{
head->next(nullptr);
return std::make_pair(head, nullptr);
}
auto *tail = std::exchange(this->_head, this->_end);
tail->next(nullptr);
this->_tail = this->_end;
return std::make_pair(head, tail);
}
} // namespace mx::queue

View File

@@ -0,0 +1,49 @@
#pragma once
#include <array>
#include <cassert>
#include <mx/tasking/priority.h>
namespace mx::queue {
template <class Q, tasking::priority MIN_PRIORITY, tasking::priority MAX_PRIORITY> class PriorityQueue
{
public:
PriorityQueue() = default;
~PriorityQueue() = default;
template <tasking::priority P> Q &get() noexcept
{
static_assert(static_cast<std::uint8_t>(P) >= static_cast<std::uint8_t>(MIN_PRIORITY));
static_assert(static_cast<std::uint8_t>(P) <= static_cast<std::uint8_t>(MAX_PRIORITY));
return _queues[static_cast<std::uint8_t>(P) - static_cast<std::uint8_t>(MIN_PRIORITY)];
}
template <tasking::priority P> const Q &get() const noexcept
{
static_assert(static_cast<std::uint8_t>(P) >= static_cast<std::uint8_t>(MIN_PRIORITY));
static_assert(static_cast<std::uint8_t>(P) <= static_cast<std::uint8_t>(MAX_PRIORITY));
return _queues[static_cast<std::uint8_t>(P) - static_cast<std::uint8_t>(MIN_PRIORITY)];
}
Q &get(const tasking::priority priority) noexcept
{
assert(priority >= static_cast<std::uint8_t>(MIN_PRIORITY));
assert(priority <= static_cast<std::uint8_t>(MAX_PRIORITY));
return _queues[static_cast<std::uint8_t>(priority) - static_cast<std::uint8_t>(MIN_PRIORITY)];
}
const Q &get(const tasking::priority priority) const noexcept
{
assert(priority >= static_cast<std::uint8_t>(MIN_PRIORITY));
assert(priority <= static_cast<std::uint8_t>(MAX_PRIORITY));
return _queues[static_cast<std::uint8_t>(priority) - static_cast<std::uint8_t>(MIN_PRIORITY)];
}
private:
std::array<Q, static_cast<std::uint8_t>(MAX_PRIORITY) - static_cast<std::uint8_t>(MIN_PRIORITY) + 1U> _queues{};
};
} // namespace mx::queue

View File

@@ -0,0 +1,146 @@
#pragma once
#include <cstdint>
#include <mx/synchronization/synchronization.h>
#include <variant>
namespace mx::resource {
enum expected_access_frequency : std::uint8_t
{
excessive = 0U,
high = 1U,
normal = 2U,
unused = 3U,
};
enum expected_read_write_ratio : std::uint8_t
{
heavy_read = 0U,
mostly_read = 1U,
balanced = 2U,
mostly_written = 3U,
heavy_written = 4U
};
class annotation
{
public:
constexpr annotation() noexcept = default;
constexpr explicit annotation(const std::uint8_t node_id) noexcept : _target(node_id) {}
constexpr explicit annotation(const std::uint16_t worker_id) noexcept : _target(worker_id) {}
constexpr explicit annotation(const synchronization::isolation_level isolation_level) noexcept
: _isolation_level(isolation_level)
{
}
constexpr explicit annotation(const expected_access_frequency access_frequency) noexcept
: _access_frequency(access_frequency)
{
}
constexpr annotation(const std::uint16_t worker_id, const synchronization::isolation_level isolation_level) noexcept
: _target(worker_id), _isolation_level(isolation_level)
{
}
constexpr annotation(const std::uint8_t node_id, const synchronization::isolation_level isolation_level) noexcept
: _target(node_id), _isolation_level(isolation_level)
{
}
constexpr annotation(const std::uint8_t node_id, const synchronization::isolation_level isolation_level,
const synchronization::protocol preferred_protocol) noexcept
: _target(node_id), _isolation_level(isolation_level), _preferred_protocol(preferred_protocol)
{
}
constexpr annotation(const std::uint16_t worker_id, const synchronization::isolation_level isolation_level,
const synchronization::protocol preferred_protocol) noexcept
: _target(worker_id), _isolation_level(isolation_level), _preferred_protocol(preferred_protocol)
{
}
constexpr annotation(const std::uint8_t node_id, const expected_access_frequency access_frequency) noexcept
: _target(node_id), _access_frequency(access_frequency)
{
}
constexpr annotation(const synchronization::isolation_level isolation_level,
const expected_access_frequency access_frequency) noexcept
: _access_frequency(access_frequency), _isolation_level(isolation_level)
{
}
constexpr annotation(const synchronization::isolation_level isolation_level,
const synchronization::protocol preferred_protocol,
const expected_access_frequency access_frequency) noexcept
: _access_frequency(access_frequency), _isolation_level(isolation_level),
_preferred_protocol(preferred_protocol)
{
}
constexpr annotation(const synchronization::isolation_level isolation_level,
const synchronization::protocol preferred_protocol,
const expected_access_frequency access_frequency,
const expected_read_write_ratio read_write_ratio) noexcept
: _access_frequency(access_frequency), _read_write_ratio(read_write_ratio), _isolation_level(isolation_level),
_preferred_protocol(preferred_protocol)
{
}
constexpr annotation(const std::uint8_t node_id, const synchronization::isolation_level isolation_level,
const expected_access_frequency access_frequency) noexcept
: _target(node_id), _access_frequency(access_frequency), _isolation_level(isolation_level)
{
}
constexpr annotation(const std::uint8_t node_id, const synchronization::isolation_level isolation_level,
const synchronization::protocol preferred_protocol,
const expected_access_frequency access_frequency) noexcept
: _target(node_id), _access_frequency(access_frequency), _isolation_level(isolation_level),
_preferred_protocol(preferred_protocol)
{
}
constexpr annotation(annotation &&) noexcept = default;
constexpr annotation(const annotation &) noexcept = default;
~annotation() = default;
annotation &operator=(annotation &&) noexcept = default;
annotation &operator=(const annotation &) noexcept = default;
[[nodiscard]] bool has_numa_node_id() const noexcept { return std::holds_alternative<std::uint8_t>(_target); }
[[nodiscard]] std::uint8_t numa_node_id() const noexcept { return std::get<std::uint8_t>(_target); }
[[nodiscard]] bool has_worker_id() const noexcept { return std::holds_alternative<std::uint16_t>(_target); }
[[nodiscard]] std::uint16_t worker_id() const noexcept { return std::get<std::uint16_t>(_target); }
[[nodiscard]] expected_access_frequency access_frequency() const noexcept { return _access_frequency; }
[[nodiscard]] expected_read_write_ratio read_write_ratio() const noexcept { return _read_write_ratio; }
[[nodiscard]] synchronization::isolation_level isolation_level() const noexcept { return _isolation_level; }
[[nodiscard]] synchronization::protocol preferred_protocol() const noexcept { return _preferred_protocol; }
bool operator==(const synchronization::isolation_level isolation_level) const noexcept
{
return _isolation_level == isolation_level;
}
bool operator!=(const synchronization::isolation_level isolation_level) const noexcept
{
return _isolation_level != isolation_level;
}
bool operator==(const synchronization::protocol protocol) const noexcept { return _preferred_protocol == protocol; }
bool operator!=(const synchronization::protocol protocol) const noexcept { return _preferred_protocol != protocol; }
private:
// Preferred NUMA region or CPU core (if any).
std::variant<std::uint8_t, std::uint16_t, std::monostate> _target{std::monostate{}};
// Expected access frequency; normal by default.
enum expected_access_frequency _access_frequency
{
expected_access_frequency::normal
};
// Expected read/write ratio; normal by default.
expected_read_write_ratio _read_write_ratio{expected_read_write_ratio::balanced};
// Preferred isolation level; no synchronization by default.
synchronization::isolation_level _isolation_level{synchronization::isolation_level::None};
// Preferred synchronization protocol (queue, latch, ...); no synchronization by default.
synchronization::protocol _preferred_protocol{synchronization::protocol::None};
};
} // namespace mx::resource

View File

@@ -0,0 +1,80 @@
#include "builder.h"
#include <mx/synchronization/primitive_matrix.h>
using namespace mx::resource;
std::pair<std::uint16_t, std::uint8_t> Builder::schedule(const resource::annotation &annotation)
{
// Scheduling was done by the hint.
if (annotation.has_worker_id())
{
this->_scheduler.predict_usage(annotation.worker_id(), annotation.access_frequency());
return std::make_pair(annotation.worker_id(), this->_scheduler.numa_node_id(annotation.worker_id()));
}
// Schedule resources round robin to the channels.
const auto count_worker = this->_scheduler.count_cores();
auto worker_id = this->_round_robin_worker_id.fetch_add(1U, std::memory_order_relaxed) % count_worker;
// If the chosen channel contains an excessive accessed resource, get another.
if (count_worker > 2U && annotation.isolation_level() == synchronization::isolation_level::Exclusive &&
this->_scheduler.has_excessive_usage_prediction(worker_id))
{
worker_id = this->_round_robin_worker_id.fetch_add(1U, std::memory_order_relaxed) % count_worker;
}
this->_scheduler.predict_usage(worker_id, annotation.access_frequency());
// TODO: NUMA NODE ID is for worker, not channel.
const auto numa_node_id =
annotation.has_numa_node_id() ? annotation.numa_node_id() : this->_scheduler.numa_node_id(worker_id);
return std::make_pair(worker_id, numa_node_id);
}
mx::synchronization::primitive Builder::isolation_level_to_synchronization_primitive(
const annotation &annotation) noexcept
{
// The developer did not define any fixed protocol for
// synchronization; we choose one depending on the hints.
if (annotation == synchronization::protocol::None)
{
return synchronization::PrimitiveMatrix::select_primitive(
annotation.isolation_level(), annotation.access_frequency(), annotation.read_write_ratio());
}
// The developer hinted a specific protocol (latched, queued, ...)
// and a relaxed isolation level.
if (annotation == synchronization::isolation_level::ExclusiveWriter)
{
switch (annotation.preferred_protocol())
{
case synchronization::protocol::Latch:
return synchronization::primitive::ReaderWriterLatch;
case synchronization::protocol::OLFIT:
return synchronization::primitive::OLFIT;
case synchronization::protocol::RestrictedTransactionalMemory:
return synchronization::primitive::RestrictedTransactionalMemory;
default:
return synchronization::primitive::ScheduleWriter;
}
}
// The developer hinted a specific protocol (latched, queued, ...)
// and a strict isolation level.
if (annotation == synchronization::isolation_level::Exclusive)
{
switch (annotation.preferred_protocol())
{
case synchronization::protocol::Latch:
return synchronization::primitive::ExclusiveLatch;
case synchronization::protocol::Batched:
return synchronization::primitive::Batched;
case synchronization::protocol::RestrictedTransactionalMemory:
return synchronization::primitive::RestrictedTransactionalMemory;
default:
return synchronization::primitive::ScheduleAll;
}
}
return mx::synchronization::primitive::None;
}

View File

@@ -0,0 +1,617 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/primitive_matrix.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/primitive_matrix.h:

View File

@@ -0,0 +1,175 @@
#pragma once
#include "annotation.h"
#include "ptr.h"
#include <array>
#include <atomic>
#include <cstdint>
#include <mx/memory/global_heap.h>
#include <mx/memory/worker_local_dynamic_size_allocator.h>
#include <mx/tasking/config.h>
#include <mx/tasking/scheduler.h>
#include <mx/util/aligned_t.h>
#include <type_traits>
#include <utility>
namespace mx::resource {
/**
* The Builder constructs and deletes data objects.
* Besides, the Builder schedules data objects to
* channels.
*/
class Builder
{
public:
Builder(tasking::Scheduler &scheduler, memory::dynamic::local::Allocator &allocator) noexcept
: _allocator(allocator), _scheduler(scheduler)
{
}
~Builder() noexcept = default;
/**
* Build a data object of given type with given
* size and arguments. The hint defines the synchronization
* requirements and affects scheduling.
*
* @param calling_worker_id Id of the calling worker for local allocation.
* @param size Size of the data object.
* @param hint Hint for scheduling and synchronization.
* @param arguments Arguments to the constructor.
* @return Tagged pointer holding the synchronization, assigned channel and pointer.
*/
template <typename T, typename... Args>
ptr build(const std::uint16_t calling_worker_id, const std::size_t size, annotation &&annotation,
Args &&...arguments) noexcept
{
#ifndef NDEBUG
if (annotation != synchronization::isolation_level::None &&
(annotation != synchronization::isolation_level::Exclusive ||
annotation != synchronization::protocol::Queue) &&
(annotation != synchronization::isolation_level::Exclusive &&
annotation != synchronization::protocol::Batched))
{
if constexpr (std::is_base_of<ResourceInterface, T>::value == false)
{
assert(false && "Type must be inherited from mx::resource::ResourceInterface");
}
}
#endif
const auto synchronization_method = Builder::isolation_level_to_synchronization_primitive(annotation);
const auto [mapped_worker_id, numa_node_id] = schedule(annotation);
auto *resource = new (_allocator.allocate(calling_worker_id, numa_node_id, system::cache::line_size(), size))
T(std::forward<Args>(arguments)...);
if constexpr (std::is_base_of<ResourceInterface, T>::value)
{
switch (synchronization_method)
{
case synchronization::primitive::ExclusiveLatch:
case synchronization::primitive::RestrictedTransactionalMemory:
resource->initialize(ResourceInterface::SynchronizationType::Exclusive);
break;
case synchronization::primitive::ReaderWriterLatch:
resource->initialize(ResourceInterface::SynchronizationType::SharedWrite);
break;
case synchronization::primitive::OLFIT:
case synchronization::primitive::ScheduleWriter:
resource->initialize(ResourceInterface::SynchronizationType::OLFIT);
break;
default:
break;
}
}
const auto resource_information = information{mapped_worker_id, synchronization_method};
return ptr{resource, resource_information};
}
/**
* Builds data resourced from an existing pointer.
* The hint defines the synchronization
* requirements and affects scheduling.
* @param object
* @param annotation Hint for scheduling and synchronization.
* @return Tagged pointer holding the synchronization, assigned channel and pointer.
*/
template <typename T> ptr build(T *object, annotation &&annotation) noexcept
{
#ifndef NDEBUG
if (annotation != synchronization::isolation_level::None &&
(annotation != synchronization::isolation_level::Exclusive ||
annotation != synchronization::protocol::Queue) &&
(annotation != synchronization::isolation_level::Exclusive &&
annotation != synchronization::protocol::Batched))
{
if constexpr (std::is_base_of<ResourceInterface, T>::value == false)
{
assert(false && "Type must be inherited from mx::resource::ResourceInterface");
}
}
#endif
const auto synchronization_method = Builder::isolation_level_to_synchronization_primitive(annotation);
const auto [worker_id, _] = schedule(annotation);
return ptr{object, information{worker_id, synchronization_method}};
}
/**
* Destroys the given data object.
* @param calling_worker_id Worker calling destroy for local free.
* @param resource Tagged pointer to the data object.
*/
template <typename T> void destroy(const std::uint16_t calling_worker_id, const ptr resource)
{
// TODO: Revoke usage prediction?
if (resource != nullptr)
{
if constexpr (tasking::config::memory_reclamation() != tasking::config::None)
{
if (synchronization::is_optimistic(resource.synchronization_primitive()))
{
_scheduler.epoch_manager().add_to_garbage_collection(resource.get<ResourceInterface>(),
resource.worker_id());
return;
}
}
// No need to reclaim memory.
resource.get<T>()->~T();
_allocator.free(calling_worker_id, resource.get<void>());
}
}
private:
// Internal allocator for dynamic sized allocation.
memory::dynamic::local::Allocator &_allocator;
// Scheduler of MxTasking to get access to channels.
tasking::Scheduler &_scheduler;
// Next channel id for round-robin scheduling.
alignas(64) std::atomic_uint16_t _round_robin_worker_id{0U};
/**
* Schedules the resource to a channel, affected by the given hint.
*
* @param annotation Hint for scheduling.
* @return Pair of Channel and NUMA node IDs.
*/
std::pair<std::uint16_t, std::uint8_t> schedule(const annotation &annotation);
/**
* Determines the best synchronization method based on
* synchronization requirement.
*
* @param annotation Hint for choosing the primitive.
* @return Chosen synchronization method.
*/
static synchronization::primitive isolation_level_to_synchronization_primitive(
const annotation &annotation) noexcept;
};
} // namespace mx::resource

View File

@@ -0,0 +1,99 @@
#pragma once
#include "resource_interface.h"
#include <cassert>
#include <cstdint>
#include <mx/memory/alignment_helper.h>
#include <mx/memory/tagged_ptr.h>
#include <mx/synchronization/synchronization.h>
#include <mx/util/random.h>
#include <new>
namespace mx::resource {
/**
* Information of a resource, stored within
* the pointer to the resource.
*/
class information
{
public:
constexpr information() noexcept = default;
explicit information(const std::uint16_t worker_id,
const synchronization::primitive synchronization_primitive) noexcept
: _worker_id(worker_id), _synchronization_primitive(static_cast<std::uint16_t>(synchronization_primitive))
{
}
~information() = default;
[[nodiscard]] std::uint16_t worker_id() const noexcept { return _worker_id; }
[[nodiscard]] synchronization::primitive synchronization_primitive() const noexcept
{
return static_cast<synchronization::primitive>(_synchronization_primitive);
}
void worker_id(const std::uint16_t worker_id) noexcept { _worker_id = worker_id; }
void synchronization_primitive(const synchronization::primitive primitive) noexcept
{
_synchronization_primitive = static_cast<std::uint16_t>(primitive);
}
information &operator=(const information &other) = default;
private:
std::uint16_t _worker_id : 12 {0U};
std::uint16_t _synchronization_primitive : 4 {0U};
} __attribute__((packed));
/**
* Pointer to a resource, stores information about
* that resource.
*/
class ptr final : public memory::tagged_ptr<void, information>
{
public:
constexpr ptr() noexcept = default;
constexpr ptr(const std::nullptr_t /*nullptr*/) noexcept : memory::tagged_ptr<void, information>(nullptr) {}
constexpr explicit ptr(void *ptr_, const information info = {}) noexcept
: memory::tagged_ptr<void, information>(ptr_, info)
{
}
~ptr() = default;
ptr &operator=(const ptr &other) noexcept = default;
ptr &operator=(std::nullptr_t) noexcept
{
reset(nullptr);
return *this;
}
[[nodiscard]] std::uint16_t worker_id() const noexcept { return info().worker_id(); }
[[nodiscard]] synchronization::primitive synchronization_primitive() const noexcept
{
return info().synchronization_primitive();
}
} __attribute__((packed));
/**
* Casts the internal pointer of the resource pointer
* to a pointer typed by the given template parameter.
*
* @param resource Resource to cast.
* @return Pointer to the requested type.
*/
template <typename S> static auto *ptr_cast(const ptr resource) noexcept
{
return resource.template get<S>();
}
} // namespace mx::resource
namespace std {
template <> struct hash<mx::resource::ptr>
{
std::size_t operator()(const mx::resource::ptr ptr) const noexcept
{
return std::hash<void *>().operator()(ptr.get());
}
};
} // namespace std

View File

@@ -0,0 +1,187 @@
#pragma once
#include <atomic>
#include <cstdint>
#include <mx/memory/reclamation/epoch_t.h>
#include <mx/synchronization/memory_transaction.h>
#include <mx/synchronization/optimistic_lock.h>
#include <mx/synchronization/rw_spinlock.h>
#include <mx/synchronization/spinlock.h>
namespace mx::resource {
/**
* The resource interface represents resources that
* needs to be synchronized by the tasking engine.
* Supported synchronizations are:
* - Latches (Spinlock, R/W-lock)
* - Optimistic latches + memory reclamation
*/
class ResourceInterface
{
public:
enum SynchronizationType : std::uint8_t
{
Exclusive,
SharedRead,
SharedWrite,
Optimistic,
OLFIT,
RestrictedTransactionalMemory,
};
constexpr ResourceInterface() noexcept = default;
ResourceInterface(const ResourceInterface &) = delete;
ResourceInterface(ResourceInterface &&) = delete;
virtual ~ResourceInterface() = default;
void initialize(const SynchronizationType type) noexcept
{
switch (type)
{
case Exclusive:
case RestrictedTransactionalMemory:
_exclusive_latch.unlock();
break;
case SharedRead:
case SharedWrite:
_rw_latch.initialize();
break;
case Optimistic:
case OLFIT:
_optimistic_latch.initialize();
break;
}
}
/**
* Called by the epoch manager on safe reclaiming this resource.
*/
virtual void on_reclaim() = 0;
/**
* Set the next resource in garbage list.
* @param next Next resource in garbage list.
*/
void next(ResourceInterface *next) noexcept { _next_garbage = next; }
/**
* @return Next resource in garbage list.
*/
[[nodiscard]] ResourceInterface *next() const noexcept { return _next_garbage; }
/**
* @return The current version of the resource.
*/
[[nodiscard]] synchronization::OptimisticLock::version_t version() const noexcept
{
return _optimistic_latch.read_valid();
}
/**
* Checks whether the given version is still valid.
*
* @param version Version to check.
* @return True, when the version is valid.
*/
[[nodiscard]] bool is_version_valid(const synchronization::OptimisticLock::version_t version) const noexcept
{
return _optimistic_latch.is_valid(version);
}
/**
* Tries to acquire the optimistic latch.
* @return True, when latch was acquired.
*/
[[nodiscard]] bool try_acquire_optimistic_latch() noexcept { return _optimistic_latch.try_lock(); }
/**
* Set the epoch-timestamp this resource was removed.
* @param epoch Epoch where this resource was removed.
*/
void remove_epoch(const memory::reclamation::epoch_t epoch) noexcept { _remove_epoch = epoch; }
/**
* @return The epoch this resource was removed.
*/
[[nodiscard]] memory::reclamation::epoch_t remove_epoch() const noexcept { return _remove_epoch; }
template <SynchronizationType T> class scoped_latch
{
public:
constexpr inline explicit scoped_latch(ResourceInterface *resource) noexcept : _resource(resource)
{
if constexpr (T == SynchronizationType::Exclusive)
{
_resource->_exclusive_latch.lock();
}
else if constexpr (T == SynchronizationType::SharedRead)
{
_resource->_rw_latch.lock_shared();
}
else if constexpr (T == SynchronizationType::SharedWrite)
{
_resource->_rw_latch.lock();
}
else if constexpr (T == SynchronizationType::Optimistic)
{
_resource->_optimistic_latch.lock<true>();
}
else if constexpr (T == SynchronizationType::OLFIT)
{
_resource->_optimistic_latch.lock<false>();
}
else if constexpr (T == SynchronizationType::RestrictedTransactionalMemory)
{
_is_transaction_used_latch = synchronization::MemoryTransaction::begin(_resource->_exclusive_latch);
}
}
inline ~scoped_latch() noexcept
{
if constexpr (T == SynchronizationType::Exclusive)
{
_resource->_exclusive_latch.unlock();
}
else if constexpr (T == SynchronizationType::SharedRead)
{
_resource->_rw_latch.unlock_shared();
}
else if constexpr (T == SynchronizationType::SharedWrite)
{
_resource->_rw_latch.unlock();
}
else if constexpr (T == SynchronizationType::Optimistic || T == SynchronizationType::OLFIT)
{
_resource->_optimistic_latch.unlock();
}
else if constexpr (T == SynchronizationType::RestrictedTransactionalMemory)
{
synchronization::MemoryTransaction::end(_resource->_exclusive_latch, _is_transaction_used_latch);
}
}
private:
ResourceInterface *_resource;
bool _is_transaction_used_latch;
};
using scoped_exclusive_latch = scoped_latch<SynchronizationType::Exclusive>;
using scoped_optimistic_latch = scoped_latch<SynchronizationType::Optimistic>;
using scoped_olfit_latch = scoped_latch<SynchronizationType::OLFIT>;
template <bool WRITER>
using scoped_rw_latch = scoped_latch<WRITER ? SynchronizationType::SharedWrite : SynchronizationType::SharedRead>;
using scoped_transaction = scoped_latch<SynchronizationType::RestrictedTransactionalMemory>;
private:
// Encapsulated synchronization primitives.
union {
synchronization::Spinlock _exclusive_latch;
synchronization::RWSpinLock _rw_latch;
synchronization::OptimisticLock _optimistic_latch;
};
// Epoch and Garbage management.
memory::reclamation::epoch_t _remove_epoch{0U};
ResourceInterface *_next_garbage{nullptr};
};
} // namespace mx::resource

View File

@@ -0,0 +1,81 @@
#pragma once
#include "spinlock.h"
#ifdef USE_RTM
#include <immintrin.h>
#endif
namespace mx::synchronization {
class MemoryTransaction
{
private:
[[nodiscard]] static constexpr auto max_tries() { return 10U; }
[[nodiscard]] static constexpr auto abort_because_locked_code() { return 0xFF; }
public:
[[nodiscard]] static bool begin(Spinlock &latch) noexcept
{
#ifdef USE_RTM
auto retries = 0U;
do
{
const auto status = _xbegin();
if (status == _XBEGIN_STARTED)
{
if (latch.is_locked() == false)
{
/// Transaction was started successfully
/// and the latch was not acquired by another thread.
return false;
}
/// Transaction was started, but lock was acquired from another thread.
_xabort(abort_because_locked_code());
}
else if (status & _XABORT_EXPLICIT)
{
if (_XABORT_CODE(status) == abort_because_locked_code() && (status & _XABORT_NESTED) == false)
{
/// The transaction was aborted because another thread
/// holds the lock. Wait until the thread releases
/// the lock.
while (latch.is_locked())
{
mx::system::builtin::pause();
}
}
else if ((status & _XABORT_RETRY) == false)
{
/// The system tells us, that we should not retry.
/// Hence, acquire the latch.
goto acquire_fallback_lock;
}
}
} while (++retries <= max_tries());
acquire_fallback_lock:
latch.lock();
return true;
#else
latch.lock();
return true;
#endif
}
static void end(Spinlock &latch, [[maybe_unused]] const bool has_locked) noexcept
{
#ifdef USE_RTM
if (has_locked == false)
{
_xend();
}
else
{
latch.unlock();
}
#else
latch.unlock();
#endif
}
};
} // namespace mx::synchronization

View File

@@ -0,0 +1,92 @@
#pragma once
#include <atomic>
#include <cstdint>
#include <limits>
#include <mx/system/builtin.h>
#include <mx/tasking/config.h>
namespace mx::synchronization {
class OptimisticLock
{
public:
using version_t = std::uint32_t;
constexpr OptimisticLock() = default;
~OptimisticLock() = default;
void initialize() { _version = 0b100; }
/**
* Guarantees to read a valid version by blocking until
* the version is not locked.
* @return The current version.
*/
[[nodiscard]] version_t read_valid() const noexcept
{
auto version = __atomic_load_n(&_version, __ATOMIC_SEQ_CST);
while (OptimisticLock::is_locked(version))
{
system::builtin::pause();
version = __atomic_load_n(&_version, __ATOMIC_SEQ_CST);
}
return version;
}
/**
* Validates the version.
*
* @param version The version to validate.
* @return True, if the version is valid.
*/
[[nodiscard]] bool is_valid(const version_t version) const noexcept
{
return version == __atomic_load_n(&_version, __ATOMIC_SEQ_CST);
}
/**
* Tries to acquire the lock.
* @return True, when lock was acquired.
*/
[[nodiscard]] bool try_lock() noexcept
{
auto version = read_valid();
return __atomic_compare_exchange_n(&_version, &version, version + 0b10, false, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
}
/**
* Waits until the lock is successfully acquired.
*/
template <bool SINGLE_WRITER> void lock() noexcept
{
if constexpr (SINGLE_WRITER)
{
__atomic_fetch_add(&_version, 0b10, __ATOMIC_SEQ_CST);
}
else
{
auto tries = std::uint64_t{1U};
while (this->try_lock() == false)
{
const auto wait = tries++;
for (auto i = 0U; i < wait * 32U; ++i)
{
system::builtin::pause();
std::atomic_thread_fence(std::memory_order_seq_cst);
}
}
}
}
/**
* Unlocks the version lock.
*/
void unlock() noexcept { __atomic_fetch_add(&_version, 0b10, __ATOMIC_SEQ_CST); }
private:
version_t _version;
[[nodiscard]] static bool is_locked(const version_t version) noexcept { return (version & 0b10) == 0b10; }
};
} // namespace mx::synchronization

View File

@@ -0,0 +1,68 @@
#pragma once
#include "synchronization.h"
#include <algorithm>
#include <cstdint>
#include <mx/resource/ptr.h>
namespace mx::synchronization {
class PrimitiveMatrix
{
public:
static primitive select_primitive(const isolation_level isolation_level,
const resource::expected_access_frequency access_frequency,
const resource::expected_read_write_ratio read_write_ratio) noexcept
{
return isolation_level != isolation_level::None
? matrix()[static_cast<std::uint8_t>(isolation_level)][static_cast<std::uint8_t>(read_write_ratio)]
[static_cast<std::uint8_t>(access_frequency)]
: primitive::None;
}
private:
constexpr static std::array<std::array<std::array<primitive, 4>, 5>, 2> matrix() noexcept
{
return {{// For isolation_level::ExclusiveWriter
{{
// For predicted_read_write_ratio::heavy_read
{{primitive::ScheduleWriter, primitive::ScheduleWriter, primitive::ScheduleWriter,
primitive::ScheduleWriter}},
// For predicted_read_write_ratio::mostly_read
{{primitive::ScheduleWriter, primitive::ScheduleWriter, primitive::OLFIT, primitive::OLFIT}},
// For predicted_read_write_ratio::balanced
{{primitive::OLFIT, primitive::OLFIT, primitive::OLFIT, primitive::OLFIT}},
// For predicted_read_write_ratio::mostly_written
{{primitive::OLFIT, primitive::OLFIT, primitive::ReaderWriterLatch, primitive::ReaderWriterLatch}},
// For predicted_read_write_ratio::heavy_written
{{primitive::ScheduleAll, primitive::ScheduleAll, primitive::ReaderWriterLatch,
primitive::ReaderWriterLatch}},
}},
// For isolation_level::Exclusive
{{
// For predicted_read_write_ratio::heavy_read
{{primitive::ScheduleAll, primitive::ScheduleAll, primitive::ExclusiveLatch,
primitive::ExclusiveLatch}},
// For predicted_read_write_ratio::mostly_read
{{primitive::ScheduleAll, primitive::ScheduleAll, primitive::ExclusiveLatch,
primitive::ExclusiveLatch}},
// For predicted_read_write_ratio::balanced
{{primitive::ScheduleAll, primitive::ScheduleAll, primitive::ExclusiveLatch,
primitive::ExclusiveLatch}},
// For predicted_read_write_ratio::mostly_written
{{primitive::ScheduleAll, primitive::ScheduleAll, primitive::ExclusiveLatch,
primitive::ExclusiveLatch}},
// For predicted_read_write_ratio::heavy_written
{{primitive::ScheduleAll, primitive::ScheduleAll, primitive::ExclusiveLatch,
primitive::ExclusiveLatch}},
}}}};
}
};
} // namespace mx::synchronization

View File

@@ -0,0 +1,294 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
/*
* N.B. You most likely do _not_ want to use RWSpinLock or any other
* kind of spinlock. Use SharedMutex instead.
*
* In short, spinlocks in preemptive multi-tasking operating systems
* have serious problems and fast mutexes like SharedMutex are almost
* certainly the better choice, because letting the OS scheduler put a
* thread to sleep is better for system responsiveness and throughput
* than wasting a timeslice repeatedly querying a lock held by a
* thread that's blocked, and you can't prevent userspace
* programs blocking.
*
* Spinlocks in an operating system kernel make much more sense than
* they do in userspace.
*
* -------------------------------------------------------------------
*
* Two Read-Write spin lock implementations.
*
* Ref: http://locklessinc.com/articles/locks
*
* Both locks here are faster than pthread_rwlock and have very low
* overhead (usually 20-30ns). They don't use any system mutexes and
* are very compact (4/8 bytes), so are suitable for per-instance
* based locking, particularly when contention is not expected.
*
* For a spinlock, RWSpinLock is a reasonable choice. (See the note
* about for why a spin lock is frequently a bad idea generally.)
* RWSpinLock has minimal overhead, and comparable contention
* performance when the number of competing threads is less than or
* equal to the number of logical CPUs. Even as the number of
* threads gets larger, RWSpinLock can still be very competitive in
* READ, although it is slower on WRITE, and also inherently unfair
* to writers.
*
* RWTicketSpinLock shows more balanced READ/WRITE performance. If
* your application really needs a lot more threads, and a
* higher-priority writer, prefer one of the RWTicketSpinLock locks.
*
* Caveats:
*
* RWTicketSpinLock locks can only be used with GCC on x86/x86-64
* based systems.
*
* RWTicketSpinLock<32> only allows up to 2^8 - 1 concurrent
* readers and writers.
*
* RWTicketSpinLock<64> only allows up to 2^16 - 1 concurrent
* readers and writers.
*
* RWTicketSpinLock<..., true> (kFavorWriter = true, that is, strict
* writer priority) is NOT reentrant, even for lock_shared().
*
* The lock will not grant any new shared (read) accesses while a thread
* attempting to acquire the lock in write mode is blocked. (That is,
* if the lock is held in shared mode by N threads, and a thread attempts
* to acquire it in write mode, no one else can acquire it in shared mode
* until these N threads release the lock and then the blocked thread
* acquires and releases the exclusive lock.) This also applies for
* attempts to reacquire the lock in shared mode by threads that already
* hold it in shared mode, making the lock non-reentrant.
*
* RWSpinLock handles 2^30 - 1 concurrent readers.
*
* @author Xin Liu <xliux@fb.com>
*/
#pragma once
/*
========================================================================
Benchmark on (Intel(R) Xeon(R) CPU L5630 @ 2.13GHz) 8 cores(16 HTs)
========================================================================
------------------------------------------------------------------------------
1. Single thread benchmark (read/write lock + unlock overhead)
Benchmark Iters Total t t/iter iter/sec
-------------------------------------------------------------------------------
* BM_RWSpinLockRead 100000 1.786 ms 17.86 ns 53.4M
+30.5% BM_RWSpinLockWrite 100000 2.331 ms 23.31 ns 40.91M
+85.7% BM_RWTicketSpinLock32Read 100000 3.317 ms 33.17 ns 28.75M
+96.0% BM_RWTicketSpinLock32Write 100000 3.5 ms 35 ns 27.25M
+85.6% BM_RWTicketSpinLock64Read 100000 3.315 ms 33.15 ns 28.77M
+96.0% BM_RWTicketSpinLock64Write 100000 3.5 ms 35 ns 27.25M
+85.7% BM_RWTicketSpinLock32FavorWriterRead 100000 3.317 ms 33.17 ns 28.75M
+29.7% BM_RWTicketSpinLock32FavorWriterWrite 100000 2.316 ms 23.16 ns 41.18M
+85.3% BM_RWTicketSpinLock64FavorWriterRead 100000 3.309 ms 33.09 ns 28.82M
+30.2% BM_RWTicketSpinLock64FavorWriterWrite 100000 2.325 ms 23.25 ns 41.02M
+ 175% BM_PThreadRWMutexRead 100000 4.917 ms 49.17 ns 19.4M
+ 166% BM_PThreadRWMutexWrite 100000 4.757 ms 47.57 ns 20.05M
------------------------------------------------------------------------------
2. Contention Benchmark 90% read 10% write
Benchmark hits average min max sigma
------------------------------------------------------------------------------
---------- 8 threads ------------
RWSpinLock Write 142666 220ns 78ns 40.8us 269ns
RWSpinLock Read 1282297 222ns 80ns 37.7us 248ns
RWTicketSpinLock Write 85692 209ns 71ns 17.9us 252ns
RWTicketSpinLock Read 769571 215ns 78ns 33.4us 251ns
pthread_rwlock_t Write 84248 2.48us 99ns 269us 8.19us
pthread_rwlock_t Read 761646 933ns 101ns 374us 3.25us
---------- 16 threads ------------
RWSpinLock Write 124236 237ns 78ns 261us 801ns
RWSpinLock Read 1115807 236ns 78ns 2.27ms 2.17us
RWTicketSpinLock Write 81781 231ns 71ns 31.4us 351ns
RWTicketSpinLock Read 734518 238ns 78ns 73.6us 379ns
pthread_rwlock_t Write 83363 7.12us 99ns 785us 28.1us
pthread_rwlock_t Read 754978 2.18us 101ns 1.02ms 14.3us
---------- 50 threads ------------
RWSpinLock Write 131142 1.37us 82ns 7.53ms 68.2us
RWSpinLock Read 1181240 262ns 78ns 6.62ms 12.7us
RWTicketSpinLock Write 83045 397ns 73ns 7.01ms 31.5us
RWTicketSpinLock Read 744133 386ns 78ns 11ms 31.4us
pthread_rwlock_t Write 80849 112us 103ns 4.52ms 263us
pthread_rwlock_t Read 728698 24us 101ns 7.28ms 194us
*/
#include <algorithm>
#include <atomic>
#include <mx/system/builtin.h>
#include <thread>
namespace mx::synchronization {
/*
* A simple, small (4-bytes), but unfair rwlock. Use it when you want
* a nice writer and don't expect a lot of write/read contention, or
* when you need small rwlocks since you are creating a large number
* of them.
*
* Note that the unfairness here is extreme: if the lock is
* continually accessed for read, writers will never get a chance. If
* the lock can be that highly contended this class is probably not an
* ideal choice anyway.
*
* It currently implements most of the Lockable, SharedLockable and
* UpgradeLockable concepts except the TimedLockable related locking/unlocking
* interfaces.
*/
class RWSpinLock
{
enum : int32_t
{
READER = 4,
UPGRADED = 2,
WRITER = 1
};
public:
constexpr RWSpinLock() noexcept = default;
RWSpinLock(RWSpinLock const &) = delete;
RWSpinLock &operator=(RWSpinLock const &) = delete;
void initialize() { _bits = 0; }
// Lockable Concept
void lock() noexcept
{
while (!try_lock())
{
mx::system::builtin::pause();
}
}
// Writer is responsible for clearing up both the UPGRADED and WRITER bits.
void unlock() noexcept
{
static_assert(READER > WRITER + UPGRADED, "wrong bits!");
__atomic_fetch_and(&_bits, ~(WRITER | UPGRADED), __ATOMIC_RELEASE);
}
// SharedLockable Concept
void lock_shared() noexcept
{
while (!try_lock_shared())
{
mx::system::builtin::pause();
}
}
void unlock_shared() noexcept { __atomic_fetch_add(&_bits, -READER, __ATOMIC_RELEASE); }
// Downgrade the lock from writer status to reader status.
void unlock_and_lock_shared() noexcept
{
__atomic_fetch_add(&_bits, READER, __ATOMIC_ACQUIRE);
unlock();
}
// UpgradeLockable Concept
void lock_upgrade() noexcept
{
while (!try_lock_upgrade())
{
system::builtin::pause();
}
}
void unlock_upgrade() noexcept { __atomic_fetch_add(&_bits, -UPGRADED, __ATOMIC_ACQ_REL); }
// unlock upgrade and try to acquire write lock
void unlock_upgrade_and_lock() noexcept
{
while (!try_unlock_upgrade_and_lock())
{
system::builtin::pause();
}
}
// unlock upgrade and read lock atomically
void unlock_upgrade_and_lock_shared() noexcept { __atomic_fetch_add(&_bits, READER - UPGRADED, __ATOMIC_ACQ_REL); }
// write unlock and upgrade lock atomically
void unlock_and_lock_upgrade() noexcept
{
// need to do it in two steps here -- as the UPGRADED bit might be OR-ed at
// the same time when other threads are trying do try_lock_upgrade().
__atomic_fetch_or(&_bits, UPGRADED, __ATOMIC_ACQUIRE);
__atomic_fetch_add(&_bits, -WRITER, __ATOMIC_RELEASE);
}
// Attempt to acquire writer permission. Return false if we didn't get it.
bool try_lock() noexcept
{
auto expect = std::int32_t{0};
return __atomic_compare_exchange_n(&_bits, &expect, WRITER, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQ_REL);
}
// Try to get reader permission on the lock. This can fail if we
// find out someone is a writer or upgrader.
// Setting the UPGRADED bit would allow a writer-to-be to indicate
// its intention to write and block any new readers while waiting
// for existing readers to finish and release their read locks. This
// helps avoid starving writers (promoted from upgraders).
bool try_lock_shared() noexcept
{
// fetch_add is considerably (100%) faster than compare_exchange,
// so here we are optimizing for the common (lock success) case.
const auto value = __atomic_fetch_add(&_bits, READER, __ATOMIC_ACQUIRE);
if (static_cast<bool>(value & (WRITER | UPGRADED)))
{
__atomic_fetch_add(&_bits, -READER, __ATOMIC_RELEASE);
return false;
}
return true;
}
// try to unlock upgrade and write lock atomically
bool try_unlock_upgrade_and_lock() noexcept
{
auto expect = std::int32_t{UPGRADED};
return __atomic_compare_exchange_n(&_bits, &expect, WRITER, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQ_REL);
}
// try to acquire an upgradable lock.
bool try_lock_upgrade() noexcept
{
const auto value = __atomic_fetch_or(&_bits, UPGRADED, __ATOMIC_ACQUIRE);
// Note: when failed, we cannot flip the UPGRADED bit back,
// as in this case there is either another upgrade lock or a write lock.
// If it's a write lock, the bit will get cleared up when that lock's done
// with unlock().
return ((value & (UPGRADED | WRITER)) == 0);
}
// mainly for debugging purposes.
[[nodiscard]] int32_t bits() const noexcept { return __atomic_load_n(&_bits, __ATOMIC_ACQUIRE); }
private:
std::int32_t _bits;
};
} // namespace mx::synchronization

View File

@@ -0,0 +1,59 @@
#pragma once
#include <atomic>
#include <cstdint>
#include <mx/system/builtin.h>
namespace mx::synchronization {
/**
* Simple spinlock for mutual exclusion.
*/
class Spinlock
{
public:
constexpr Spinlock() noexcept = default;
~Spinlock() = default;
/**
* Locks the spinlock by spinning until it is lockable.
*/
void lock() noexcept
{
do
{
while (_flag)
{
system::builtin::pause();
}
if (try_lock())
{
return;
}
} while (true);
}
/**
* Try to lock the lock.
* @return True, when successfully locked.
*/
bool try_lock() noexcept
{
bool expected = false;
return __atomic_compare_exchange_n(&_flag, &expected, true, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
}
/**
* Unlocks the spinlock.
*/
void unlock() noexcept { __atomic_store_n(&_flag, false, __ATOMIC_SEQ_CST); }
/**
* @return True, if the lock is in use.
*/
[[nodiscard]] bool is_locked() const noexcept { return __atomic_load_n(&_flag, __ATOMIC_RELAXED); }
private:
bool _flag;
};
} // namespace mx::synchronization

View File

@@ -0,0 +1,60 @@
#pragma once
#include <cstdint>
namespace mx::synchronization {
/**
* Desired isolation level of a resource.
*/
enum class isolation_level : std::uint8_t
{
ExclusiveWriter = 0U, // Reads can be parallel, writes will be synchronized
Exclusive = 1U, // All accesses will be synchronized
None = 2U, // Nothing will be synchronized
};
/**
* Desired protocol of synchronization.
*/
enum class protocol : std::uint8_t
{
None = 0U, // System is free to choose
Queue = 1U, // Choose primitive with queues with respect to isolation level
Latch = 2U, // Choose primitive with latches with respect to isolation level
OLFIT = 3U, // Try to choose olfit
RestrictedTransactionalMemory = 4U, // Try to choose transactional memory
Batched = 5U, // Tasks are batched
};
/**
* Real method, based on the isolation level
* and decision by the tasking layer.
*
* Attention: Even if the primitive is 8bit long,
* it is stored within the tagged_ptr as
* using only 4bit! Therefore, the max.
* value can be 15.
*/
enum class primitive : std::uint8_t
{
None = 0U, // Nothing will be synchronized
ExclusiveLatch = 1U, // All accesses will use a spinlock
ScheduleAll = 2U, // All accesses will be scheduled to the mapped channel
ReaderWriterLatch = 3U, // Use a reader/writer latch to enable parallel reads
ScheduleWriter = 4U, // Reads can perform anywhere, writes are scheduled to the mapped channel
OLFIT = 5U, // Read/write anywhere but use a latch for writers
RestrictedTransactionalMemory = 6U, /// Read/write transactional
Batched = 7U // Tasks are batched by using task squads
};
/**
* Checks whether the given primitive is kind of optimistic synchronization
* or not.
* @param primitive_ Primitive to check.
* @return True, if the given primitive is optimistic.
*/
static inline bool is_optimistic(const primitive primitive_) noexcept
{
return primitive_ == primitive::ScheduleWriter || primitive_ == primitive::OLFIT;
}
} // namespace mx::synchronization

View File

@@ -0,0 +1,28 @@
#pragma once
#include <cstdint>
#include <iostream>
namespace mx::system {
/**
* Encapsulates compiler builtins.
*/
class builtin
{
public:
/**
* Generates a pause/yield cpu instruction, independently
* of the hardware.
*/
static void pause() noexcept
{
#if defined(__x86_64__) || defined(__amd64__)
__builtin_ia32_pause();
#elif defined(__arm__)
asm("YIELD");
#endif
}
[[nodiscard]] static std::uint32_t clz(const std::uint32_t number) noexcept { return __builtin_clz(number); }
[[nodiscard]] static std::uint64_t clz(const std::uint64_t number) noexcept { return __builtin_clzll(number); }
};
} // namespace mx::system

View File

@@ -0,0 +1,203 @@
#pragma once
#include <array>
#include <cassert>
#include <cstdint>
#include <functional>
#include <unistd.h>
namespace mx::system {
/**
* Encapsulates cache operations like prefetching.
*
* Further documentation on Intel: https://www.felixcloutier.com/x86/prefetchh
*/
class cache
{
public:
[[nodiscard]] static constexpr auto line_size() noexcept { return 64U; }
enum level : std::uint8_t
{
L1 = 3U,
ALL = 3U,
L2 = 2U,
L3 = 1U,
NTA = 0U
};
enum access : std::uint8_t
{
read = 0U,
write = 1U
};
/**
* Prefetches a single cache line into a given prefetch level.
*
* @tparam L Wanted cache level.
* @tparam A Access to the cache line whether read or write.
* @param address Address of the memory which should be prefetched.
*/
template <level L, access A = access::read, std::uint8_t C>
static void prefetch(const std::int64_t *address) noexcept
{
constexpr auto items_per_cacheline = line_size() / sizeof(std::int64_t);
for (auto i = 0U; i < C * items_per_cacheline; i += items_per_cacheline)
{
__builtin_prefetch(&address[i], static_cast<std::uint8_t>(A), static_cast<std::uint8_t>(L));
}
}
template <level L> [[nodiscard]] static std::uint64_t size()
{
/* TODO: Get cache sizes, maybe with CPUID? */
if constexpr (L == level::L1)
{
if (_cache_size_cache[0U] == 0U)
{
//_cache_size_cache[0U] = sysconf(_SC_LEVEL1_DCACHE_SIZE);
}
return _cache_size_cache[0U];
}
else if constexpr (L == level::L2)
{
if (_cache_size_cache[1U] == 0U)
{
//_cache_size_cache[1U] = sysconf(_SC_LEVEL2_CACHE_SIZE);
}
return _cache_size_cache[1U];
}
else if constexpr (L == level::L3)
{
if (_cache_size_cache[2U] == 0U)
{
//_cache_size_cache[2U] = sysconf(_SC_LEVEL3_CACHE_SIZE);
}
return _cache_size_cache[2U];
}
else
{
return 0U;
}
}
template <level L, access A = access::read, std::uint32_t S>
static void prefetch_range(const std::int64_t *address) noexcept
{
if constexpr (S <= 64U)
{
prefetch<L, A, 1U>(address);
}
else if constexpr (S == 128U)
{
prefetch<L, A, 2U>(address);
}
else if constexpr (S == 192U)
{
prefetch<L, A, 3U>(address);
}
else if constexpr (S == 256U)
{
prefetch<L, A, 4U>(address);
}
else if constexpr (S == 256U)
{
prefetch<L, A, 4U>(address);
}
else if constexpr (S == 320U)
{
prefetch<L, A, 5U>(address);
}
else if constexpr (S == 384U)
{
prefetch<L, A, 6U>(address);
}
else if constexpr (S == 448U)
{
prefetch<L, A, 7U>(address);
}
else if constexpr (S == 512U)
{
prefetch<L, A, 8U>(address);
}
else if constexpr (S == 1024U)
{
prefetch<L, A, 16U>(address);
}
else
{
prefetch_range<L, A>(address, S);
}
}
/**
* Prefetches a range of cache lines into the given cache level.
*
* @tparam L Wanted cache level.
* @tparam A Access to the cache line whether read or write.
* @param address Address of the memory which should be prefetched.
* @param size Size of the accessed memory.
*/
template <level L, access A = access::read>
static void prefetch_range(const std::int64_t *address, const std::uint32_t size) noexcept
{
const auto cache_lines_to_prefetch = size / cache::line_size();
switch (cache_lines_to_prefetch)
{
case 4:
prefetch<L, A, 4U>(address);
break;
case 2:
prefetch<L, A, 2U>(address);
break;
case 1:
prefetch<L, A, 1U>(address);
break;
case 8:
prefetch<L, A, 8U>(address);
break;
case 3:
prefetch<L, A, 3U>(address);
break;
case 12:
prefetch<L, A, 12U>(address);
break;
case 16:
prefetch<L, A, 16U>(address);
break;
case 5:
prefetch<L, A, 5U>(address);
break;
case 6:
prefetch<L, A, 6U>(address);
break;
case 7:
prefetch<L, A, 7U>(address);
break;
case 9:
prefetch<L, A, 9U>(address);
break;
case 10:
prefetch<L, A, 10U>(address);
break;
case 11:
prefetch<L, A, 11U>(address);
break;
case 13:
prefetch<L, A, 13U>(address);
break;
case 14:
prefetch<L, A, 14U>(address);
break;
case 15:
prefetch<L, A, 15U>(address);
break;
default:
break;
}
}
private:
static inline std::array<std::uint64_t, 3U> _cache_size_cache{{0U, 0U, 0U}};
};
} // namespace mx::system

View File

@@ -0,0 +1,51 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <mx/tasking/config.h>
#include <unordered_map>
#include <tukija/syscall-generic.h>
namespace mx::system {
/**
* Encapsulates methods for retrieving information
* about the hardware landscape.
*/
class cpu
{
public:
/**
* @return Core where the caller is running.
*/
[[nodiscard]] static std::uint16_t core_id() { return Tukija::Cip::cip()->get_cpu_index(); }
/**
* Reads the NUMA region identifier of the given core.
*
* @param core_id Id of the core.
* @return Id of the NUMA region the core stays in.
*/
[[nodiscard]] static std::uint8_t node_id(const std::uint16_t core_id)
{
return Tukija::Tip::tip()->domain_of_idx(core_id);
}
/**
* Reads the NUMA region identifier of the current core.
*
* @return Id of the NUMA region the core stays in.
*/
[[nodiscard]] static std::uint8_t node_id() { return Tukija::Tip::tip()->domain_of_loc(Genode::Thread::myself()->affinity()); }
/**
* @return The greatest NUMA region identifier.
*/
[[nodiscard]] static std::uint8_t max_node_id() { return Tukija::Tip::tip()->num_domains(); }
/**
* @return Number of available cores.
*/
[[nodiscard]] static std::uint16_t count_cores() { return std::uint16_t(Tukija::Cip::cip()->habitat_affinity.total()); }
private:
};
} // namespace mx::system

View File

@@ -0,0 +1,27 @@
#pragma once
#include <cstdint>
namespace mx::system {
/**
* Encapsulates methods for checking features
* of the system by calling cpuid instruction.
*/
class cpuid
{
public:
/**
* @return True, when restricted transactional memory
* is enabled.
*/
static bool is_rtm_provided()
{
std::uint32_t eax = 0x7;
std::uint32_t ebx;
std::uint32_t ecx = 0x0;
asm volatile("cpuid" : "=b"(ebx) : "a"(eax), "c"(ecx));
return ebx & 0b100000000000;
}
};
} // namespace mx::system

View File

@@ -0,0 +1,38 @@
#pragma once
#include <fstream>
namespace mx::system {
/**
* Encapsulates functionality of the (Linux) system.
*/
class Environment
{
public:
/**
* @return True, if NUMA balancing is enabled by the system.
*/
static bool is_numa_balancing_enabled()
{
return false; /* EalánOS has no NUMA balancing, yet.*/
}
static constexpr auto is_sse2()
{
#ifdef USE_SSE2
return true;
#else
return false;
#endif // USE_SSE2
}
static constexpr auto is_debug()
{
#ifdef NDEBUG
return false;
#else
return true;
#endif // NDEBUG
}
};
} // namespace mx::system

View File

@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
namespace mx::system {
class RDTSCP
{
public:
[[nodiscard]] inline static std::uint64_t begin() noexcept
{
std::uint32_t high, low;
asm volatile("CPUID\n\t"
"RDTSC\n\t"
"mov %%edx, %0\n\t"
"mov %%eax, %1\n\t"
: "=r"(high), "=r"(low)::"%rax", "%rbx", "%rcx", "%rdx");
return (std::uint64_t(high) << 32) | low;
}
[[nodiscard]] inline static std::uint64_t end() noexcept
{
std::uint32_t high, low;
asm volatile("RDTSCP\n\t"
"mov %%edx, %0\n\t"
"mov %%eax, %1\n\t"
"CPUID\n\t"
: "=r"(high), "=r"(low)::"%rax", "%rbx", "%rcx", "%rdx");
return (std::uint64_t(high) << 32) | low;
}
};
} // namespace mx::system

View File

@@ -0,0 +1,18 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <iostream>
#include <pthread.h>
#include <string>
#include <thread>
namespace mx::system {
/**
* Encapsulates methods for thread access.
*/
class thread
{
public:
};
} // namespace mx::system

View File

@@ -0,0 +1,43 @@
# Tasking
## Task
Each task has to implement the [task interface](task.h) which inherits annotation to subclasses.
#### Annotation
Annotations provide the possibility to express runtime information about the behavior of a task to the scheduling and execution layer.
Such information include
* the [priority](priority.h) a task should run with,
* the target (a specific worker id, a [resource](../resource/resource_interface.h) that needs synchronized access, `local` or `anywhere`) the task should execute on,
* and the [resource](../resource/ptr.h) a task is accessing that could be load from memory into cache before the task executes.
## Worker
Upon start, *MxTasking* will launch one [worker](worker.h) thread on each (specified) logical hardware thread.
The worker will fetch tasks from the task pool and executes them.
#### Task Pool
Every worker has its own [task pool](task_pool.h) which has different backend-queues (a queue for normal priority and local dispatches, a queue for normal priority and remote dispatches from the same numa region, a queue for normal priority and dispatches from remote numa regions, a queue for low priority ...).
Tasks will be fetched from the task pool and stored in a buffer before executed.
#### Task Buffer
The [task buffer](task_buffer.h) is a buffer for a specific amount of tasks (i.e., `64` tasks).
Whenever the buffer becomes empty, the worker will contact the task pool and fill up the buffer with tasks from the pool.
The main reason for using a buffer is to get a precise view of future-tasks.
Tasks in the pool are stored in linked lists which are very costly to iterate whereas the buffer can be accessed like an array.
This is important to *prefetch* tasks that will be executed in the near future.
#### Task Stack
The [task stack](task_stack.h) is used to persist the state of a task before executing the task optimistically.
Executing a task optimistically may fail and the state of the task has to be reset to the state before executing (to execute again at a later time).
## Scheduler
The [scheduler](scheduler.h) dispatches tasks to task pools on different worker threads.
## Configuration
The configuration is specified by a [config file](config.h).
* `max_cores`: Specifies the maximal number of cores the tasking will spawn worker threads on.
* `task_size`: The size that will be allocated for every task.
* `task_buffer_size`: The size allocated in the worker-local [task buffer](task_buffer.h) which is filled by the task pools.
* `is_use_task_counter`: If enabled, *MxTasking* will collect information about the number of executed and disptached tasks. *Should be disabled for measurements.*
* `is_collect_task_traces`: If enabled, *MxTasking* will collect information about which task executed at what times at which worker thread. *Should be disabled for measurements.*
* `memory_reclamation`: Specifies if reclamation should be done periodically, after every task execution, or never.
* `worker_mode`: When running in `PowerSave` mode, every worker will sleep for a small amount of time to reduce power. *Should be `Performance` for measurements.*

View File

@@ -0,0 +1,139 @@
#pragma once
#include "prefetch_descriptor.h"
#include "priority.h"
#include <cstdint>
#include <mx/resource/ptr.h>
#include <variant>
namespace mx::tasking {
class TaskSquad;
/**
* Container for metadata that can be annotated to every task.
* The execution engine will use the annotation for scheduling
* and synchronization of concurrent accesses to the same data
* object, done by tasks.
*/
class annotation
{
public:
enum execution_destination : std::uint8_t
{
anywhere = 0U,
local = 1U
};
enum access_intention : bool
{
readonly = true,
write = false
};
enum resource_boundness : std::uint8_t
{
memory = 0U,
compute = 1U,
mixed = 2U
};
constexpr annotation() noexcept = default;
explicit constexpr annotation(const std::uint16_t worker_id) noexcept : _destination(worker_id) {}
explicit constexpr annotation(const execution_destination destination) noexcept : _destination(destination) {}
constexpr annotation(const enum access_intention access_intention, const resource::ptr resource) noexcept
: _access_intention(access_intention), _destination(resource)
{
}
constexpr annotation(const enum access_intention access_intention, const resource::ptr resource,
const PrefetchDescriptor prefetch_descriptor) noexcept
: _access_intention(access_intention), _destination(resource),
_prefetch_hint(PrefetchHint{prefetch_descriptor, resource})
{
}
constexpr annotation(const annotation &) noexcept = default;
constexpr annotation(annotation &&) noexcept = default;
~annotation() = default;
annotation &operator=(const annotation &) noexcept = default;
annotation &operator=(annotation &&) noexcept = default;
[[nodiscard]] bool is_readonly() const noexcept { return _access_intention == access_intention::readonly; }
[[nodiscard]] priority priority() const noexcept { return _priority; }
[[nodiscard]] enum resource_boundness resource_boundness() const noexcept { return _resource_boundness; }
[[nodiscard]] std::uint16_t worker_id() const noexcept { return std::get<std::uint16_t>(_destination); }
[[nodiscard]] std::uint8_t numa_node_id() const noexcept { return std::get<std::uint8_t>(_destination); }
[[nodiscard]] resource::ptr resource() const noexcept { return std::get<resource::ptr>(_destination); }
[[nodiscard]] bool has_worker_id() const noexcept { return std::holds_alternative<std::uint16_t>(_destination); }
[[nodiscard]] bool has_numa_node_id() const noexcept { return std::holds_alternative<std::uint8_t>(_destination); }
[[nodiscard]] bool has_resource() const noexcept { return std::holds_alternative<resource::ptr>(_destination); }
[[nodiscard]] bool is_locally() const noexcept
{
return std::holds_alternative<enum execution_destination>(_destination) &&
std::get<enum execution_destination>(_destination) == execution_destination::local;
}
[[nodiscard]] bool is_anywhere() const noexcept
{
return std::holds_alternative<enum execution_destination>(_destination) &&
std::get<enum execution_destination>(_destination) == execution_destination::anywhere;
}
[[nodiscard]] bool has_prefetch_hint() const noexcept { return _prefetch_hint.empty() == false; }
[[nodiscard]] PrefetchHint prefetch_hint() const noexcept { return _prefetch_hint; }
[[nodiscard]] PrefetchHint &prefetch_hint() noexcept { return _prefetch_hint; }
[[nodiscard]] std::uint16_t cycles() const noexcept { return _cycles; }
void set(const enum access_intention access_intention) noexcept { _access_intention = access_intention; }
void set(const enum priority priority) noexcept { _priority = priority; }
void set(const enum resource_boundness resource_boundness) noexcept { _resource_boundness = resource_boundness; }
void set(const std::uint16_t worker_id) noexcept { _destination = worker_id; }
void set(const std::uint8_t numa_id) noexcept { _destination = numa_id; }
void set(const resource::ptr resource) noexcept { _destination = resource; }
void set(const enum execution_destination execution_destination) noexcept { _destination = execution_destination; }
void set(const PrefetchDescriptor prefetch_descriptor, const resource::ptr object) noexcept
{
_prefetch_hint = PrefetchHint{prefetch_descriptor, object};
}
void set(const PrefetchHint prefetch_hint) noexcept { _prefetch_hint = prefetch_hint; }
void cycles(const std::uint16_t cycles) noexcept { _cycles = cycles; }
bool operator==(const annotation &other) const noexcept
{
return _access_intention == other._access_intention && _priority == other._priority &&
_destination == other._destination && _prefetch_hint == other._prefetch_hint;
}
private:
/// Access intention: Reading or writing the object?
/// Per default, a task is annotated as a "writer".
enum access_intention _access_intention
{
access_intention::write
};
/// Priority of a task. Low priority tasks will only be
/// executed, whenever a worker has no higher-priorized
/// tasks in his pool.
enum priority _priority
{
priority::normal
};
enum resource_boundness _resource_boundness
{
resource_boundness::mixed
};
/// Cycles used for execution of this task.
std::uint16_t _cycles{500U};
/// Target the task will run on.
/// The target can be a specific worker id, a NUMA node id,
/// a specific data object (that may have a worker_id) or
/// a hint like "local" or "anywhere".
std::variant<std::uint16_t, std::uint8_t, resource::ptr, execution_destination> _destination{
execution_destination::local};
/// The prefetch hint is a data object that will be accessed
/// by the task and a mask that identifies the cache lines,
/// which should be prefetched.
PrefetchHint _prefetch_hint{};
} __attribute__((packed));
} // namespace mx::tasking

View File

@@ -0,0 +1,73 @@
#pragma once
namespace mx::tasking {
class config
{
public:
enum queue_backend
{
Single, /// Each worker has a single queue.
NUMALocal, /// Each worker has a queue for each NUMA domain and a local queue.
WorkerLocal /// Each worker has a queue for each worker.
};
enum memory_reclamation_scheme
{
None = 0U, /// No memory reclamation at all.
UpdateEpochOnRead = 1U, /// End the epoch after every reading task.
UpdateEpochPeriodically = 2U /// End the epoch after a static amount of time.
};
enum worker_mode
{
Performance = 0U, /// The worker contact the task pool when no task was found.
PowerSave = 1U /// The worker will sleep a static amount of time when no task was found.
};
/// Maximal number of supported cores.
static constexpr auto max_cores() { return 64U; }
/// Backend of the queues.
static constexpr auto queue() { return queue_backend::NUMALocal; }
/// Maximal number of supported simultaneous multithreading threads.
static constexpr auto max_smt_threads() { return 2U; }
/// Maximal size for a single task, will be used for task allocation.
static constexpr auto task_size() { return 128U; }
/// The task buffer will hold a set of tasks, fetched from
/// queues. This is the size of the buffer.
static constexpr auto task_buffer_size() { return 64U; }
/// If enabled, the worker will sample task cycles during execution
/// and use that stats for approximating the prefetch distance for each
/// task.
/// If disabled, automatic prefetching will fall back to task annotations.
static constexpr auto is_monitor_task_cycles_for_prefetching() { return false; }
/// If enabled, will record the number of execute tasks,
/// scheduled tasks, reader and writer per core and more.
static constexpr auto is_use_task_counter() { return false; }
/// If enabled, the runtime of each task will be recorded.
static constexpr auto is_collect_task_traces() { return false; }
/// If enabled, the dataflow graph will collect statistics which node
/// emitted which amount of data.
static constexpr auto is_count_graph_emits() { return false; }
/// If enabled, the dataflow graph will collect start times of
/// pipelines and finish times of nodes.
static constexpr auto is_record_graph_times() { return false; }
/// If enabled, memory will be reclaimed while using optimistic
/// synchronization by epoch-based reclamation. Otherwise, freeing
/// memory is unsafe.
static constexpr auto memory_reclamation() { return memory_reclamation_scheme::None; }
/// Switch between performance and power saving mode.
/// Set to 'worker_mode::Performance' for measurements.
static constexpr auto worker_mode() { return worker_mode::Performance; }
};
} // namespace mx::tasking

View File

@@ -0,0 +1,97 @@
#pragma once
#include "token.h"
#include "token_generator.h"
#include <cstdint>
#include <mx/tasking/annotation.h>
#include <variant>
#include <vector>
namespace mx::tasking::dataflow {
template <typename T> class annotation
{
public:
using value_type = T;
enum FinalizationType : std::uint8_t
{
sequential,
parallel,
reduce,
none
};
class CompletionCallbackInterface
{
public:
constexpr CompletionCallbackInterface() noexcept = default;
constexpr virtual ~CompletionCallbackInterface() noexcept = default;
[[nodiscard]] virtual bool is_complete() noexcept = 0;
};
annotation() noexcept = default;
annotation(annotation &&) noexcept = default;
~annotation() noexcept = default;
annotation &operator=(annotation &&) noexcept = default;
void is_parallel(const bool is_parallel) noexcept { _is_parallel = is_parallel; }
void produces(std::unique_ptr<TokenGenerator<T>> &&generator) noexcept { _token_generator = std::move(generator); }
void resource_boundness(const enum tasking::annotation::resource_boundness resource_boundness) noexcept
{
_resource_boundness = resource_boundness;
}
void finalization_type(const FinalizationType type) noexcept { _finalization_type = type; }
void finalizes(std::vector<mx::resource::ptr> &&data) { _finalized_data = std::move(data); }
void is_finalizes_pipeline(const bool is_finalizes_pipeline) noexcept
{
_is_finalizes_pipeline = is_finalizes_pipeline;
}
void completion_callback(std::unique_ptr<CompletionCallbackInterface> &&callback) noexcept
{
_complection_callback = std::move(callback);
}
[[nodiscard]] bool is_parallel() const noexcept { return _is_parallel; }
[[nodiscard]] std::unique_ptr<TokenGenerator<T>> &token_generator() noexcept { return _token_generator; }
[[nodiscard]] enum tasking::annotation::resource_boundness resource_boundness() const noexcept
{
return _resource_boundness;
}
[[nodiscard]] bool is_producing() const noexcept { return _token_generator != nullptr; }
[[nodiscard]] FinalizationType finalization_type() const noexcept { return _finalization_type; }
[[nodiscard]] const std::vector<mx::resource::ptr> &finalize_sequence() const noexcept { return _finalized_data; }
[[nodiscard]] bool is_finalizes_pipeline() const noexcept { return _is_finalizes_pipeline; }
[[nodiscard]] const std::unique_ptr<CompletionCallbackInterface> &completion_callback() const noexcept
{
return _complection_callback;
}
[[nodiscard]] bool has_completion_callback() const noexcept { return _complection_callback != nullptr; }
private:
bool _is_parallel{false};
std::unique_ptr<TokenGenerator<T>> _token_generator;
enum tasking::annotation::resource_boundness _resource_boundness{tasking::annotation::resource_boundness::mixed};
FinalizationType _finalization_type{FinalizationType::sequential};
std::vector<mx::resource::ptr> _finalized_data;
/// Pipelines are finalized, when the last node is finished.
/// However, a node may finalize the pipeline premature.
bool _is_finalizes_pipeline{false};
/// Callback that evalutes if a node is "completed".
/// Some nodes may spawn further tasks during finalization.
/// They will complete only after executing those tasks.
std::unique_ptr<CompletionCallbackInterface> _complection_callback{nullptr};
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,44 @@
#pragma once
#include "node.h"
#include "producer.h"
#include <atomic>
#include <cstdint>
#include <mx/tasking/runtime.h>
#include <mx/tasking/task.h>
namespace mx::tasking::dataflow {
/**
* The finalization barrier is spawned on every channel that executed
* at least one task of the TaskNode.
* After the last finalization barrier was hit, the graph will finalize the node.
*/
template <class T> class FinalizationBarrierTask final : public TaskInterface
{
public:
FinalizationBarrierTask(std::atomic_int16_t &counter, EmitterInterface<T> &graph, NodeInterface<T> *node) noexcept
: _count_pending_workers(counter), _graph(graph), _node(node)
{
}
~FinalizationBarrierTask() override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
const auto pending_workers = _count_pending_workers.fetch_sub(1);
if (pending_workers == 0)
{
_graph.finalize(worker_id, _node);
}
return TaskResult::make_remove();
}
[[nodiscard]] std::uint64_t trace_id() const noexcept override { return _node->trace_id(); }
private:
std::atomic_int16_t &_count_pending_workers;
EmitterInterface<T> &_graph;
NodeInterface<T> *_node;
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,40 @@
#pragma once
#include <atomic>
#include <cstdint>
namespace mx::tasking::dataflow {
class ParallelProducingFinalizeCounter
{
public:
ParallelProducingFinalizeCounter(std::atomic_uint16_t *worker_counter, std::atomic_uint64_t *task_counter) noexcept
: _task_counter(task_counter), _worker_counter(worker_counter)
{
}
ParallelProducingFinalizeCounter(const ParallelProducingFinalizeCounter &) noexcept = default;
ParallelProducingFinalizeCounter(ParallelProducingFinalizeCounter &&) noexcept = default;
~ParallelProducingFinalizeCounter() noexcept = default;
[[nodiscard]] bool tick() noexcept
{
if (_task_counter->fetch_sub(1U) == 1U)
{
std::free(_task_counter);
if (_worker_counter->fetch_sub(1U) == 1U)
{
std::free(_worker_counter);
return true;
}
}
return false;
}
private:
std::atomic_uint64_t *_task_counter;
std::atomic_uint16_t *_worker_counter;
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,994 @@
#pragma once
#include "barrier_task.h"
#include "finalize_counter.h"
#include "node.h"
#include "pipeline.h"
#include "producer.h"
#include <bitset>
#include <chrono>
#include <mx/memory/global_heap.h>
#include <mx/synchronization/spinlock.h>
#include <mx/tasking/runtime.h>
#include <mx/tasking/task.h>
#include <ranges>
#include <unordered_map>
#include <vector>
namespace mx::tasking::dataflow {
template <typename T> class Graph;
/**
* The SequentialProducingTask takes a graph node and calls the nodes produce()
* method for an annotated amount or until the produce() call returns false.
* The task will only spawned once; produced will be called in a loop.
*/
template <typename T> class SequentialProducingTask final : public TaskInterface
{
public:
SequentialProducingTask(Graph<T> *graph, NodeInterface<T> *node) noexcept : _graph(graph), _node(node) {}
~SequentialProducingTask() noexcept override = default;
TaskResult execute(std::uint16_t worker_id) override;
[[nodiscard]] std::uint64_t trace_id() const noexcept override { return _node->trace_id(); }
private:
Graph<T> *_graph;
NodeInterface<T> *_node;
};
/**
* The ParallelProducingTask takes a node that is annotated to produce data
* in parallel. The node has to provide a set of resource annotations that
* are used to produce data or a annotated number how often the node should
* produce().
* For each sequence, one ParallelProducingTask will be spawned.
*/
template <typename T> class ParallelProducingTask final : public TaskInterface
{
public:
ParallelProducingTask(Graph<T> *graph, NodeInterface<T> *node, T &&data,
ParallelProducingFinalizeCounter finalize_counter) noexcept
: _graph(graph), _node(node), _data(std::move(data)), _finalize_counter(finalize_counter)
{
}
~ParallelProducingTask() noexcept override = default;
TaskResult execute(std::uint16_t worker_id) override;
[[nodiscard]] std::uint64_t trace_id() const noexcept override { return _node->trace_id(); }
private:
Graph<T> *_graph;
NodeInterface<T> *_node;
T _data;
ParallelProducingFinalizeCounter _finalize_counter;
};
/**
* Since the produced data may become very large, a single task that spawns
* all parallel producing tasks may block a worker for a long time.
* The SpawnParallelProducingTask will be spawned on every worker and spawn
* parallel producing tasks for a partition of the data.
*/
template <typename T> class SpawnParallelProducingTask final : public TaskInterface
{
public:
SpawnParallelProducingTask(Graph<T> *graph, NodeInterface<T> *node,
std::atomic_uint16_t *spawned_worker_counter) noexcept
: _graph(graph), _node(node), _spawned_worker_counter(spawned_worker_counter)
{
}
~SpawnParallelProducingTask() noexcept override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
auto &generator = _node->annotation().token_generator();
if (generator != nullptr) [[likely]]
{
/// Data spawned for this worker by this task.
auto data = generator->generate(worker_id);
if (data.empty() == false) [[likely]]
{
/// Counter of tasks spawned by this worker.
/// Hopefully, they will be spawned at the same core.
auto *finalize_counter =
new (std::aligned_alloc(system::cache::line_size(), sizeof(std::atomic_uint64_t)))
std::atomic_uint64_t(data.size());
for (auto &&token : data)
{
/// Spawn producing tasks with
auto *source_task = runtime::new_task<ParallelProducingTask<T>>(
worker_id, _graph, _node, std::move(token.data()),
ParallelProducingFinalizeCounter{_spawned_worker_counter, finalize_counter});
source_task->annotate(token.annotation());
runtime::spawn(*source_task, worker_id);
}
}
/// It may happen, that a worker has no data. In this case,
/// we may finalize the graph if all other tasks have
/// already executed.
else if (_spawned_worker_counter->fetch_sub(1U) == 1U)
{
_graph->finalize(worker_id, _node);
}
}
return TaskResult::make_remove();
}
private:
Graph<T> *_graph;
NodeInterface<T> *_node;
std::atomic_uint16_t *_spawned_worker_counter;
};
/**
* Since there are multiple ways to finalize a node (sequential, parallel, reduce),
* we need different sort of finalize tasks. This is the abstract finalize task
* that implements a function that calls the in_complete of the following node
* and starts pipelines of the graph if needed.
*/
template <typename T> class AbstractFinalizeTask;
/**
* The graph is a set of nodes that produce and consume data.
* Every time, a node emits data to the graph, the graph makes sure
* that the nodes successor will consume the data.
*
* Further, the nodes are arranged in pipelines to solve dependencies
* between nodes. Everytime a pipeline finishes, the graph will start
* depending pipelines.
*
* After executing the last node of the graph, all nodes and pipelines
* will be removed.
*/
template <typename T> class Graph : public EmitterInterface<T>
{
public:
friend class AbstractFinalizeTask<T>;
Graph(const bool is_record_times = false) : _is_record_times(is_record_times)
{
_pipelines.reserve(1U << 3U);
_node_pipelines.reserve(1U << 6U);
_pipeline_dependencies.reserve(1U << 3U);
_pipeline_dependencies_lock.unlock();
if constexpr (config::is_record_graph_times())
{
if (_is_record_times)
{
_pipeline_start_times.reserve(_pipelines.capacity());
_node_finish_times.reserve(1U << 6U);
}
}
}
~Graph() override
{
std::for_each(_pipelines.begin(), _pipelines.end(), [](auto *pipeline) {
pipeline->~Pipeline();
std::free(pipeline);
});
}
/**
* @return All pipelines of the graph.
*/
[[nodiscard]] const std::vector<Pipeline<T> *> &pipelines() const noexcept { return _pipelines; }
/**
* @return A list of node pairs (A,B) where A depends on B. A will be started when B finishes.
*/
[[nodiscard]] const std::vector<std::pair<NodeInterface<T> *, NodeInterface<T> *>> &node_dependencies()
const noexcept
{
return _node_dependencies;
}
/**
* Emit data to the graph. The data will be consumed by the successor of the given node.
*
* @param worker_id Worker where emit() is called.
* @param node The node emitting data.
* @param data The emitted data.
*/
void emit(const std::uint16_t worker_id, NodeInterface<T> *node, Token<T> &&data) override
{
if (_is_active) [[likely]]
{
node->out()->consume(worker_id, *this, std::move(data));
if constexpr (config::is_count_graph_emits())
{
++this->_emit_counter.at(node)[worker_id].value();
}
}
}
/**
* Finalizes a given node. When a node with dependencies finalizes,
* the graph will start depending pipelines.
* When the last node of the graph finalizes, the graph will be freed
* from memory.
*
* @param worker_id Worker where the node finalizes.
* @param node Node that finalizes.
*/
void finalize(std::uint16_t worker_id, NodeInterface<T> *node) override;
void for_each_node(std::function<void(NodeInterface<T> *)> &&callback) const override
{
for (auto *pipeline : _pipelines)
{
for (auto *node : pipeline->nodes())
{
callback(node);
}
}
}
/**
* Adds a single node to the graph.
*
* @param node Node to add.
*/
void add(NodeInterface<T> *node)
{
auto *pipeline = make_pipeline();
pipeline->emplace(node);
_node_pipelines.template insert(std::make_pair(node, pipeline));
}
/**
* Adds two nodes to the graph (if not added, yet) and
* creates an edge between the nodes. Whenever the from node
* emits data to the graph, to will consume this data.
*
* @param from Node that will produce data.
* @param to Node that will consume the data emitted by from.
*/
void make_edge(NodeInterface<T> *from_node, NodeInterface<T> *to_node)
{
if (_node_pipelines.contains(from_node) == false && _node_pipelines.contains(to_node) == false)
{
/// Both nodes will be in the same pipeline
auto *pipeline = make_pipeline();
pipeline->emplace(from_node);
pipeline->emplace(to_node);
_node_pipelines.template insert(std::make_pair(from_node, pipeline));
_node_pipelines.template insert(std::make_pair(to_node, _node_pipelines.at(from_node)));
}
else if (_node_pipelines.contains(to_node) == false)
{
/// From is already part of a pipeline. Add "to" to the same.
auto *pipeline = _node_pipelines.at(from_node);
_node_pipelines.template insert(std::make_pair(to_node, pipeline));
pipeline->emplace(to_node);
}
else if (_node_pipelines.contains(from_node) == false)
{
/// To is already part of a pipeline. Add "from" to the same.
auto *pipeline = _node_pipelines.at(to_node);
_node_pipelines.template insert(std::make_pair(from_node, pipeline));
pipeline->emplace(from_node);
}
from_node->out(to_node);
to_node->add_in(from_node);
}
/**
* Creates a dependency between the node pair (A,B) where A will be
* started only when B finishes.
*
* @param node Node with dependency.
* @param node_to_wait_for Node that has to finish.
*/
void make_dependency(NodeInterface<T> *node, NodeInterface<T> *node_to_wait_for)
{
_node_dependencies.template emplace_back(std::make_pair(node, node_to_wait_for));
auto *node_pipeline = _node_pipelines.at(node);
auto *wait_for_pipeline = _node_pipelines.at(node_to_wait_for);
if (node_pipeline == wait_for_pipeline)
{
auto *new_pipeline = make_pipeline();
change_pipeline(node_to_wait_for, node_pipeline, new_pipeline);
_pipeline_dependencies.at(node_pipeline).template emplace_back(new_pipeline);
}
else
{
_pipeline_dependencies.at(node_pipeline).template emplace_back(wait_for_pipeline);
}
}
/**
* Starts the graph by spawning tasks that call produce() for all
* nodes assigned to a pipeline without dependencies.
*
* @param worker_id Worker where the graph is started.
*/
void start(const std::uint16_t worker_id)
{
if constexpr (config::is_count_graph_emits())
{
this->for_each_node([&emits = this->_emit_counter](auto *node) {
emits.insert(std::make_pair(node, std::array<util::aligned_t<std::uint64_t>, config::max_cores()>{}));
emits.at(node).fill(util::aligned_t<std::uint64_t>{0U});
});
}
/// Start all tasks for preparatory work.
for (auto *task : _preparatory_tasks)
{
runtime::spawn(*task, worker_id);
}
_preparatory_tasks.clear();
/// Collect pipelines without dependencies and start them.
auto pipelines_to_start = std::vector<Pipeline<T> *>{};
for (auto &[pipeline, dependencies] : _pipeline_dependencies)
{
if (dependencies.empty())
{
pipelines_to_start.emplace_back(pipeline);
}
}
for (auto *pipeline : pipelines_to_start)
{
_pipeline_dependencies.erase(pipeline);
start(worker_id, pipeline);
}
}
void interrupt() override { _is_active = false; }
void add(std::vector<TaskInterface *> &&preparatory_tasks)
{
std::move(preparatory_tasks.begin(), preparatory_tasks.end(), std::back_inserter(_preparatory_tasks));
}
[[nodiscard]] std::uint64_t count_emitted(NodeInterface<T> *node) const noexcept
{
if constexpr (config::is_count_graph_emits())
{
const auto &iterator = this->_emit_counter.at(node);
return std::accumulate(iterator.begin(), iterator.end(), 0U,
[](const auto sum, const auto count) { return sum + count.value(); });
}
else
{
return 0U;
}
}
[[nodiscard]] std::optional<std::chrono::system_clock::time_point> start_time(Pipeline<T> *pipeline) const noexcept
{
if (auto iterator = _pipeline_start_times.find(pipeline); iterator != _pipeline_start_times.end())
{
return std::make_optional(iterator->second);
}
return std::nullopt;
}
[[nodiscard]] std::optional<std::chrono::system_clock::time_point> finish_time(
NodeInterface<T> *node) const noexcept
{
if (auto iterator = _node_finish_times.find(node); iterator != _node_finish_times.end())
{
return std::make_optional(iterator->second);
}
return std::nullopt;
}
[[nodiscard]] std::vector<std::pair<NodeInterface<T> *, std::chrono::nanoseconds>> node_times() const
{
auto times = std::vector<std::pair<NodeInterface<T> *, std::chrono::nanoseconds>>{};
for (auto *pipeline : _pipelines)
{
if (auto pipeline_iterator = _pipeline_start_times.find(pipeline);
pipeline_iterator != _pipeline_start_times.end())
{
auto last_start = pipeline_iterator->second;
for (auto *node : pipeline->nodes())
{
if (auto node_iterator = _node_finish_times.find(node); node_iterator != _node_finish_times.end())
{
auto node_finish = node_iterator->second;
times.template emplace_back(std::make_pair(
node, std::chrono::duration_cast<std::chrono::nanoseconds>(node_finish - last_start)));
last_start = node_finish;
}
}
}
}
return times;
}
private:
/// All pipelines of the graph.
std::vector<Pipeline<T> *> _pipelines;
/// Map to assign each node a specific pipeline.
std::unordered_map<NodeInterface<T> *, Pipeline<T> *> _node_pipelines;
/// Dependencies between pipelines. Each pipeline might have multiple dependencies.
std::unordered_map<Pipeline<T> *, std::vector<Pipeline<T> *>> _pipeline_dependencies;
/// List of node pairs (A,B) where A has to wait until B finishes.
std::vector<std::pair<NodeInterface<T> *, NodeInterface<T> *>> _node_dependencies;
/// Tasks that should be executed before executing the graph to set up i.e. data structures.
std::vector<TaskInterface *> _preparatory_tasks;
/// Record execution times?
bool _is_record_times;
/// Start time of each pipeline.
std::unordered_map<Pipeline<T> *, std::chrono::system_clock::time_point> _pipeline_start_times;
/// End time of each node.
std::unordered_map<NodeInterface<T> *, std::chrono::system_clock::time_point> _node_finish_times;
/// Lock for pipeline dependencies. Will be locked whenever a pipeline finishes.
alignas(64) synchronization::Spinlock _pipeline_dependencies_lock;
std::atomic_uint32_t _finished_pipelines{0U};
alignas(64) bool _is_active{true};
alignas(64) std::unordered_map<NodeInterface<T> *,
std::array<util::aligned_t<std::uint64_t>, config::max_cores()>> _emit_counter;
[[nodiscard]] bool complete(std::uint16_t worker_id, NodeInterface<T> *node);
/**
* @return A new pipeline.
*/
Pipeline<T> *make_pipeline()
{
auto *pipeline = _pipelines.template emplace_back(
new (memory::GlobalHeap::allocate_cache_line_aligned(sizeof(Pipeline<T>))) Pipeline<T>());
_pipeline_dependencies.insert(std::make_pair(pipeline, std::vector<Pipeline<T> *>{}));
return pipeline;
}
/**
* Moves a given node from a pipeline to another one.
* All predecessors will be moved, too.
*
* @param node Node to move from one to another pipeline.
* @param original_pipeline The original pipeline of the node.
* @param new_pipeline The new pipeline of the node and its predecessors.
*/
void change_pipeline(NodeInterface<T> *node, Pipeline<T> *original_pipeline, Pipeline<T> *new_pipeline)
{
if (_node_pipelines.at(node) == original_pipeline)
{
_node_pipelines.at(node) = new_pipeline;
for (auto *node_in : node->in())
{
change_pipeline(node_in, original_pipeline, new_pipeline);
}
}
}
/**
* Starts a given pipeline. This will spawn the produce tasks for the first node of the pipeline.
*
* @param worker_id Worker where start() is called.
* @param pipeline Pipeline to start.
*/
void start(const std::uint16_t worker_id, Pipeline<T> *pipeline)
{
auto *node = pipeline->nodes().front();
if constexpr (config::is_record_graph_times())
{
if (this->_is_record_times)
{
this->_pipeline_start_times.insert(std::make_pair(pipeline, std::chrono::system_clock::now()));
}
}
if (node->annotation().is_parallel() && node->annotation().is_producing())
{
const auto count_workers = runtime::workers();
auto *spawned_worker_counter =
new (std::aligned_alloc(system::cache::line_size(), sizeof(std::atomic_uint16_t)))
std::atomic_uint16_t(count_workers);
for (auto target_worker_id = std::uint16_t(0U); target_worker_id < count_workers; ++target_worker_id)
{
auto *spawn_task =
runtime::new_task<SpawnParallelProducingTask<T>>(worker_id, this, node, spawned_worker_counter);
spawn_task->annotate(std::uint16_t(target_worker_id));
runtime::spawn(*spawn_task, worker_id);
}
}
else if (node->annotation().is_producing()) /// Produce sequential.
{
auto *source_task = runtime::new_task<SequentialProducingTask<T>>(worker_id, this, node);
runtime::spawn(*source_task, worker_id);
}
else
{
this->finalize(worker_id, node);
}
}
};
template <typename T> class AbstractFinalizeTask : public TaskInterface
{
public:
constexpr AbstractFinalizeTask(Graph<T> *graph, NodeInterface<T> *node) noexcept : _graph(graph), _node(node) {}
~AbstractFinalizeTask() noexcept override = default;
void complete(const std::uint16_t worker_id)
{
if (_graph->complete(worker_id, _node))
{
_graph = nullptr;
_node = nullptr;
}
}
[[nodiscard]] std::uint64_t trace_id() const noexcept override
{
if (_node != nullptr) [[likely]]
{
return _node->trace_id();
}
return TaskInterface::trace_id();
}
protected:
Graph<T> *_graph;
NodeInterface<T> *_node;
};
/**
* The sequentual finalize task will call "finalize" of a node once.
*/
template <typename T> class SequentialFinalizeTask final : public AbstractFinalizeTask<T>
{
public:
constexpr SequentialFinalizeTask(Graph<T> *graph, NodeInterface<T> *node) noexcept
: AbstractFinalizeTask<T>(graph, node)
{
}
~SequentialFinalizeTask() noexcept override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
const auto ressource = this->annotation().has_resource() ? this->annotation().resource() : nullptr;
AbstractFinalizeTask<T>::_node->finalize(worker_id, *AbstractFinalizeTask<T>::_graph, true, ressource, nullptr);
AbstractFinalizeTask<T>::complete(worker_id);
return TaskResult::make_remove();
}
};
template <typename T> class ParallelCompletionTask final : public AbstractFinalizeTask<T>
{
public:
ParallelCompletionTask(Graph<T> *graph, NodeInterface<T> *node,
std::atomic_uint16_t *count_finalized_cores) noexcept
: AbstractFinalizeTask<T>(graph, node), _count_finalized_workers(count_finalized_cores)
{
}
~ParallelCompletionTask() noexcept override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
const auto is_last = _count_finalized_workers->fetch_sub(1U) == 1U;
if (is_last)
{
const auto is_completed = this->_node->annotation().completion_callback()->is_complete();
if (is_completed)
{
std::free(_count_finalized_workers);
AbstractFinalizeTask<T>::complete(worker_id);
}
else
{
_count_finalized_workers->store(this->_node->annotation().finalize_sequence().size());
for (const auto finalize_data : this->_node->annotation().finalize_sequence())
{
const auto target_worker_id = finalize_data.worker_id();
auto *completion_task = mx::tasking::runtime::new_task<ParallelCompletionTask<T>>(
worker_id, this->_graph, this->_node, _count_finalized_workers);
completion_task->annotate(target_worker_id);
mx::tasking::runtime::spawn(*completion_task, worker_id);
}
}
}
return TaskResult::make_remove();
}
private:
std::atomic_uint16_t *_count_finalized_workers{nullptr};
};
/**
* The parallel finalize task will be spawned on every worker and call
* the finalize of a node in parallel. Only the last executed finalize task
* will call the in_complete of the follow-up node, keeping track by a atomic
* counter.
*/
template <typename T> class ParallelFinalizeTask final : public AbstractFinalizeTask<T>
{
public:
constexpr ParallelFinalizeTask(Graph<T> *graph, NodeInterface<T> *node,
std::atomic_uint16_t *count_finalized_cores) noexcept
: AbstractFinalizeTask<T>(graph, node), _count_finalized_workers(count_finalized_cores)
{
}
~ParallelFinalizeTask() noexcept override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
const auto is_last = _count_finalized_workers->fetch_sub(1U) == 1U;
/// Let the node finalize it's results. Maybe, the node will emit some data to the graph.
AbstractFinalizeTask<T>::_node->finalize(worker_id, *AbstractFinalizeTask<T>::_graph, is_last,
this->annotation().resource(), nullptr);
if (is_last)
{
if (this->_node->annotation().has_completion_callback() &&
this->_node->annotation().completion_callback()->is_complete() == false)
{
const auto &finalize_sequence = this->_node->annotation().finalize_sequence();
_count_finalized_workers->store(finalize_sequence.size());
for (const auto finalize_data : finalize_sequence)
{
const auto target_worker_id = finalize_data.worker_id();
auto *completion_task = mx::tasking::runtime::new_task<ParallelCompletionTask<T>>(
worker_id, this->_graph, this->_node, _count_finalized_workers);
completion_task->annotate(target_worker_id);
mx::tasking::runtime::spawn(*completion_task, worker_id);
}
}
else
{
std::free(_count_finalized_workers);
AbstractFinalizeTask<T>::complete(worker_id);
}
}
return TaskResult::make_remove();
}
private:
std::atomic_uint16_t *_count_finalized_workers{nullptr};
};
template <typename T> class ReduceFinalizeTask final : public AbstractFinalizeTask<T>
{
public:
constexpr ReduceFinalizeTask(Graph<T> *graph, NodeInterface<T> *node, const mx::resource::ptr reduced_data) noexcept
: AbstractFinalizeTask<T>(graph, node), _reduced_data(reduced_data)
{
}
~ReduceFinalizeTask() noexcept override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
AbstractFinalizeTask<T>::_node->finalize(worker_id, *AbstractFinalizeTask<T>::_graph, false,
this->annotation().resource(), _reduced_data);
if (auto *next_task = dynamic_cast<ReduceFinalizeTask<T> *>(_follow_up_task))
{
if (next_task->_pending_preceding_counter.fetch_sub(1U) == 1U)
{
return TaskResult::make_succeed_and_remove(next_task);
}
}
else if (auto *sequential_finalize_task = dynamic_cast<SequentialFinalizeTask<T> *>(_follow_up_task))
{
return TaskResult::make_succeed_and_remove(sequential_finalize_task);
}
return TaskResult::make_remove();
}
void follow_up_task(AbstractFinalizeTask<T> *task) noexcept { _follow_up_task = task; }
[[nodiscard]] resource::ptr reduced_resource() const noexcept { return _reduced_data; }
private:
/// The task that will reduce next.
AbstractFinalizeTask<T> *_follow_up_task;
/// The worker id that will be reduced.
resource::ptr _reduced_data;
/// Counts how many reduce tasks are pending.
std::atomic_uint8_t _pending_preceding_counter{2U};
};
/**
* Calls consume of a node for every object if the data sequentially.
*/
template <typename T> TaskResult SequentialProducingTask<T>::execute(const std::uint16_t worker_id)
{
auto sequence = 0ULL;
for (auto &data : this->_node->annotation().token_generator()->generate(worker_id))
{
this->_node->consume(worker_id, *_graph, std::move(data));
}
auto *finalize_task = mx::tasking::runtime::new_task<SequentialFinalizeTask<T>>(worker_id, _graph, _node);
finalize_task->annotate(worker_id);
return TaskResult::make_succeed_and_remove(finalize_task);
}
/**
* For every part of the data, a single parallel producing task will be spawned.
* Keeping track using an atomic counter, the last task will call finalize for the node.
*/
template <typename T> TaskResult ParallelProducingTask<T>::execute(const std::uint16_t worker_id)
{
_node->consume(worker_id, *_graph, Token<T>{std::move(this->_data), this->annotation()});
if (_finalize_counter.tick())
{
_graph->finalize(worker_id, _node);
}
return TaskResult::make_remove();
}
class FinalizeReduceCalculator
{
public:
[[nodiscard]] static std::pair<std::vector<std::vector<std::pair<resource::ptr, resource::ptr>>>, resource::ptr>
pairs(const std::vector<resource::ptr> &data)
{
auto pairs = std::vector<std::vector<std::pair<resource::ptr, resource::ptr>>>{};
auto reduced_data = data;
while (reduced_data.size() > 1U)
{
reduced_data = FinalizeReduceCalculator::reduce(pairs, std::move(reduced_data));
}
return std::make_pair(std::move(pairs), reduced_data.front());
}
private:
[[nodiscard]] static std::vector<mx::resource::ptr> reduce(
std::vector<std::vector<std::pair<resource::ptr, resource::ptr>>> &reduce_passes,
std::vector<resource::ptr> &&to_reduce)
{
/// Pairs to reduce.
auto pairs = std::vector<std::pair<mx::resource::ptr, mx::resource::ptr>>{};
pairs.reserve(to_reduce.size() / 2U);
/// Data that will be reduced in the next step.
auto to_reduce_next = std::vector<resource::ptr>{};
to_reduce_next.reserve((to_reduce.size() / 2U) + 1U);
const auto is_odd = to_reduce.size() % 2U != 0U;
for (auto i = 0U; i < to_reduce.size() - static_cast<std::uint16_t>(is_odd); i += 2U)
{
pairs.emplace_back(std::make_pair(to_reduce[i], to_reduce[i + 1U]));
to_reduce_next.emplace_back(to_reduce[i]);
}
if (is_odd)
{
to_reduce_next.emplace_back(to_reduce.back());
}
if (pairs.empty() == false)
{
reduce_passes.emplace_back(std::move(pairs));
}
return to_reduce_next;
}
};
template <typename T> void Graph<T>::finalize(const std::uint16_t worker_id, NodeInterface<T> *node)
{
const auto finalization_type = node->annotation().finalization_type();
if (finalization_type == annotation<T>::FinalizationType::parallel)
{
auto *finalized_worker_counter =
new (std::aligned_alloc(system::cache::line_size(), sizeof(std::atomic_uint16_t)))
std::atomic_uint16_t(node->annotation().finalize_sequence().size());
for (auto data : node->annotation().finalize_sequence())
{
data.reset(resource::information{data.worker_id(), synchronization::primitive::ScheduleAll});
auto *finalize_task =
runtime::new_task<ParallelFinalizeTask<T>>(worker_id, this, node, finalized_worker_counter);
finalize_task->annotate(data);
runtime::spawn(*finalize_task, worker_id);
}
}
else if (finalization_type == annotation<T>::FinalizationType::reduce)
{
/// List of list of resource pairs to reduce.
auto reduce_resource_pairs = FinalizeReduceCalculator::pairs(node->annotation().finalize_sequence());
auto &pair_lists = std::get<0>(reduce_resource_pairs);
/// The resource with the final finalization task (that will be a sequential finalization task).
const auto last_resource = std::get<1>(reduce_resource_pairs);
/// Create the last and final finalization task.
auto *last_finalization_task = runtime::new_task<SequentialFinalizeTask<T>>(worker_id, this, node);
last_finalization_task->annotate(last_resource);
if (pair_lists.empty())
{
runtime::spawn(*last_finalization_task, worker_id);
return;
}
/// Reduce tasks, one map (main worker id -> finalization task) per "reduce stage".
auto tasks = std::vector<std::unordered_map<resource::ptr, ReduceFinalizeTask<T> *>>{};
tasks.reserve(pair_lists.size());
/// Create all reduce tasks.
for (auto &pair_list : pair_lists)
{
auto task_map = std::unordered_map<resource::ptr, ReduceFinalizeTask<T> *>{};
task_map.reserve(pair_list.size());
for (auto &pair : pair_list)
{
const auto main_resource = std::get<0>(pair);
const auto reduced_resource = std::get<1>(pair);
auto *reduce_task = runtime::new_task<ReduceFinalizeTask<T>>(worker_id, this, node, reduced_resource);
reduce_task->annotate(main_resource);
task_map.insert(std::make_pair(main_resource, reduce_task));
}
tasks.template emplace_back(std::move(task_map));
}
/// Setup the last reduce step.
if (auto iterator = tasks.back().find(last_resource); iterator != tasks.back().end())
{
iterator->second->follow_up_task(last_finalization_task);
}
/// Setup all follow up tasks.
for (auto stage = tasks.size() - 1U; stage > 0U; --stage)
{
for (auto [main_resource, task] : tasks[stage])
{
/// The task for the main worker will be in the last stage.
if (auto main_resource_task = tasks[stage - 1U].find(main_resource);
main_resource_task != tasks[stage - 1U].end())
{
main_resource_task->second->follow_up_task(task);
}
/// The task for the reduced worker may be in any stage before.
const auto reduced_resource = task->reduced_resource();
for (auto reduced_stage = stage - 1U; reduced_stage >= 0U; --reduced_stage)
{
if (auto reduced_resource_task = tasks[reduced_stage].find(reduced_resource);
reduced_resource_task != tasks[reduced_stage].end())
{
reduced_resource_task->second->follow_up_task(task);
break;
}
}
}
}
/// Spawn tasks of the first stage.
for (auto [_, finalization_task] : tasks[0])
{
runtime::spawn(*finalization_task, worker_id);
}
}
else if (finalization_type == annotation<T>::FinalizationType::none)
{
node->finalize(worker_id, *this, true, nullptr, nullptr);
this->complete(worker_id, node);
}
else
{
auto *finalize_task = runtime::new_task<SequentialFinalizeTask<T>>(worker_id, this, node);
finalize_task->annotate(mx::tasking::annotation::local);
runtime::spawn(*finalize_task, worker_id);
}
}
template <typename T> bool Graph<T>::complete(std::uint16_t worker_id, NodeInterface<T> *node)
{
if constexpr (config::is_record_graph_times())
{
if (this->_is_record_times)
{
this->_node_finish_times.insert(std::make_pair(node, std::chrono::system_clock::now()));
}
}
/// Tell the next node, that this node has completed.
Pipeline<T> *next_node_pipeline{nullptr};
if (node->out() != nullptr)
{
node->out()->in_completed(worker_id, *this, *node);
next_node_pipeline = this->_node_pipelines.at(node->out());
}
/// Maybe, the node was the last one in it's pipeline.
/// If so, we may trigger one or multiple pipelines that depended on the nodes pipeline.
auto *node_pipeline = this->_node_pipelines.at(node);
auto pipelines_to_start = std::vector<Pipeline<T> *>{};
if (node_pipeline != next_node_pipeline || node->annotation().is_finalizes_pipeline())
{
this->_pipeline_dependencies_lock.lock();
/// Remove the nodes pipeline from the dependency list of all other pipelines.
/// If, after removing the pipeline from dependencies, there are pipelines without
/// dependency, we can start that pipelines.
for (auto &[pipeline, dependencies] : this->_pipeline_dependencies)
{
auto dependency_iterator = std::find(dependencies.begin(), dependencies.end(), node_pipeline);
if (dependency_iterator != dependencies.end())
{
dependencies.erase(dependency_iterator);
}
if (dependencies.empty())
{
pipelines_to_start.emplace_back(pipeline);
}
}
/// Start all pipelines without dependency.
for (auto *pipeline : pipelines_to_start)
{
this->_pipeline_dependencies.erase(pipeline);
this->start(worker_id, pipeline);
}
this->_pipeline_dependencies_lock.unlock();
}
/// If the node was has no successor and we did not start any pipeline, we finished.
if (node->out() == nullptr)
{
if (this->_finished_pipelines.fetch_add(1U) == (this->_pipelines.size() - 1U))
{
delete this;
return true;
}
}
return false;
}
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,134 @@
#pragma once
#include "annotation.h"
#include "producer.h"
#include "token.h"
#include <cstdint>
#include <optional>
#include <vector>
namespace mx::tasking::dataflow {
template <typename T> class NodeInterface
{
public:
NodeInterface() = default;
virtual ~NodeInterface() = default;
/**
* Updates the successor.
*
* @param out New successor.
*/
virtual void out(NodeInterface *out) noexcept { _out = out; }
/**
* @return The successor of this node.
*/
[[nodiscard]] NodeInterface *out() const noexcept { return _out; }
/**
* Inserts the given node as a predecessor of this node.
*
* @param incomeing Node to insert in the predecessor list.
*/
virtual void add_in(NodeInterface *incomeing) noexcept { _in.emplace_back(incomeing); }
/**
* @return A list of predecessors of this node.
*/
[[nodiscard]] const std::vector<NodeInterface *> &in() const noexcept { return _in; }
/**
* Updates the annotation of this node.
*
* @param annotation New annotation.
*/
void annotate(dataflow::annotation<T> &&annotation) noexcept { _annotation = std::move(annotation); }
/**
* @return The annotation of the node.
*/
[[nodiscard]] const dataflow::annotation<T> &annotation() const noexcept { return _annotation; }
/**
* @return The annotation of the node.
*/
[[nodiscard]] dataflow::annotation<T> &annotation() noexcept { return _annotation; }
/**
* Consumes data and may emit data to the graph. This function is called
* by the graph when a predecessor emits data to the graph.
*
* @param worker_id Worker, where the data is consumed.
* @param emitter Emitter that takes data when the node wants to emit data.
* @param data Data that is consumed.
*/
virtual void consume(std::uint16_t worker_id, EmitterInterface<T> &emitter, Token<T> &&data) = 0;
/**
* Callback that is called by the graph when one of the
* incoming nodes completes its execution.
*
* @param worker_id Worker, where the incoming node completed.
* @param emitter Emitter that takes data when the node wants to emit data.
* @param in_node Node that completed.
*/
virtual void in_completed(std::uint16_t worker_id, EmitterInterface<T> &emitter, NodeInterface<T> &in_node) = 0;
/**
* Callback that is called by the graph, when a nodes completes.
*
* @param worker_id Worker, where the node closes.
* @param emitter Emitter that takes data when the node wants to emit data.
* @param is_last True, if this is the last finalization call (may be interesting for parallel finalization).
* @param data Data that is finalized, may be nullptr.
* @param reduced_data Data that is reduced, may be nullptr.
*/
virtual void finalize(const std::uint16_t /*worker_id*/, EmitterInterface<T> & /*emitter*/, const bool /*is_last*/,
const mx::resource::ptr /*data*/, const mx::resource::ptr /*reduced_data*/)
{
}
[[nodiscard]] virtual std::string to_string() const noexcept = 0;
[[nodiscard]] virtual std::uint64_t trace_id() const noexcept { return 0U; }
private:
/// Node where data is emitted to.
NodeInterface<T> *_out{nullptr};
/// Nodes from where data is consumed.
std::vector<NodeInterface<T> *> _in;
/// Annotation.
dataflow::annotation<T> _annotation;
};
template <typename T> class ProducingNodeInterface : public NodeInterface<T>
{
public:
ProducingNodeInterface<T>() = default;
~ProducingNodeInterface<T>() override = default;
void in_completed(const std::uint16_t /*worker_id*/, EmitterInterface<T> & /*emitter*/,
NodeInterface<T> & /*in_node*/) override
{
}
};
template <typename T> class EmptyNode final : public NodeInterface<T>
{
public:
EmptyNode<T>() = default;
~EmptyNode<T>() override = default;
void consume(const std::uint16_t /*worker_id*/, EmitterInterface<T> & /*emitter*/, Token<T> && /*data*/) override {}
void in_completed(const std::uint16_t worker_id, EmitterInterface<T> &emitter,
NodeInterface<T> & /*in_node*/) override
{
emitter.finalize(worker_id, this);
}
[[nodiscard]] std::string to_string() const noexcept override { return "Empty Node"; }
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,30 @@
#pragma once
#include "node.h"
#include <atomic>
#include <vector>
namespace mx::tasking::dataflow {
template <typename T> class alignas(64) Pipeline
{
public:
Pipeline() { _nodes.reserve(1U << 4U); }
~Pipeline()
{
std::for_each(_nodes.begin(), _nodes.end(), [](auto node) { delete node; });
}
void emplace(NodeInterface<T> *node) { _nodes.template emplace_back(node); }
[[nodiscard]] const std::vector<NodeInterface<T> *> &nodes() const noexcept { return _nodes; }
[[nodiscard]] std::atomic_uint16_t &finalization_barrier_counter() noexcept
{
return _finalization_barrier_counter;
}
private:
std::vector<NodeInterface<T> *> _nodes;
alignas(64) std::atomic_uint16_t _finalization_barrier_counter;
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,19 @@
#pragma once
#include "token.h"
#include <cstdint>
namespace mx::tasking::dataflow {
template <typename T> class NodeInterface;
template <typename T> class EmitterInterface
{
public:
constexpr EmitterInterface() noexcept = default;
virtual ~EmitterInterface() noexcept = default;
virtual void emit(std::uint16_t worker_id, NodeInterface<T> *node, Token<T> &&data) = 0;
virtual void finalize(std::uint16_t worker_id, NodeInterface<T> *node) = 0;
virtual void interrupt() = 0;
virtual void for_each_node(std::function<void(NodeInterface<T> *)> &&callback) const = 0;
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,149 @@
#pragma once
#include "barrier_task.h"
#include "node.h"
#include "producer.h"
#include "token.h"
#include <array>
#include <cstdint>
#include <mx/tasking/runtime.h>
#include <mx/tasking/task.h>
#include <mx/util/aligned_t.h>
#include <optional>
#include <string>
namespace mx::tasking::dataflow {
/**
* Task that consumes and produces data in context of nodes.
*/
template <typename T> class DataTaskInterface
{
public:
using value_type = T;
constexpr DataTaskInterface() noexcept = default;
virtual ~DataTaskInterface() noexcept = default;
/**
* Consumes the given data.
* New data may be emitted to the given node.
*
* @param worker_id Local worker id where the task is executed.
* @param node Node that executes that task.
* @param emitter Emitter that can emit new data.
* @param data Data that is consumed by that task.
*/
virtual void execute(std::uint16_t worker_id, NodeInterface<T> *node, EmitterInterface<T> &emitter,
Token<T> &&data) = 0;
};
enum input_cardinality
{
single,
multiple
};
template <class DataTask> class NodeTask;
template <typename DataTask> class TaskNode : public NodeInterface<typename DataTask::value_type>
{
friend NodeTask<DataTask>;
public:
using value_type = typename DataTask::value_type;
TaskNode() noexcept = default;
~TaskNode() noexcept override = default;
void add_in(NodeInterface<value_type> *in_node) noexcept override
{
_count_nodes_in.fetch_add(1);
NodeInterface<value_type>::add_in(in_node);
}
/**
* Consumes the data by spawning a task of type TASK_TYPE.
*
* @param worker_id Worker where consume is called.
* @param graph Graph where the node is located in.
* @param token Data that is consumed.
*/
void consume(std::uint16_t worker_id, EmitterInterface<value_type> &graph, Token<value_type> &&token) override;
/**
* Called whenever the succeeding node was finalized.
*
* @param worker_id Core where consume is called.
* @param graph Graph where the node is located in.
*/
void in_completed(const std::uint16_t worker_id, EmitterInterface<value_type> &graph,
NodeInterface<value_type> & /*in_node*/) override
{
if (_count_nodes_in.fetch_sub(1) == 1)
{
const auto count_workers = mx::tasking::runtime::workers();
_count_pending_workers = count_workers - 1;
for (auto target_worker_id = std::uint16_t(0U); target_worker_id < count_workers; ++target_worker_id)
{
auto *barrier_task = mx::tasking::runtime::new_task<FinalizationBarrierTask<value_type>>(
worker_id, _count_pending_workers, graph, this);
barrier_task->annotate(target_worker_id);
mx::tasking::runtime::spawn(*barrier_task, worker_id);
}
}
}
[[nodiscard]] std::string to_string() const noexcept override
{
return std::string{"Task Skeleton ["} + typeid(DataTask).name() + "]";
}
private:
std::atomic_int16_t _count_nodes_in{0U};
std::atomic_int16_t _count_pending_workers{0U};
};
/**
* The NodeTask executes ("wraps") the DataTask of the given node and executed the node logic.
* @tparam DataTask
*/
template <class DataTask> class NodeTask final : public TaskInterface
{
public:
NodeTask(TaskNode<DataTask> *owning_node, EmitterInterface<typename DataTask::value_type> &graph,
Token<typename DataTask::value_type> &&token) noexcept
: _owning_node(owning_node), _graph(graph), _token_data(std::move(token.data()))
{
}
~NodeTask() noexcept override = default;
TaskResult execute(const std::uint16_t worker_id) override
{
DataTask{}.execute(worker_id, _owning_node, _graph,
Token<typename DataTask::value_type>{std::move(_token_data), annotation()});
return TaskResult::make_remove();
}
[[nodiscard]] std::uint64_t trace_id() const noexcept override { return _owning_node->trace_id(); }
private:
TaskNode<DataTask> *_owning_node;
EmitterInterface<typename DataTask::value_type> &_graph;
/// Data that was consumed by the node.
typename DataTask::value_type _token_data;
};
template <typename DataTask>
void TaskNode<DataTask>::consume(const std::uint16_t worker_id, EmitterInterface<typename DataTask::value_type> &graph,
Token<typename DataTask::value_type> &&token)
{
// _task_counter.add(worker_id, 1);
const auto annotation = token.annotation();
auto *node_task = runtime::new_task<NodeTask<DataTask>>(worker_id, this, graph, std::move(token));
node_task->annotate(annotation);
runtime::spawn(*node_task, worker_id);
}
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,58 @@
#pragma once
#include <mx/tasking/annotation.h>
namespace mx::tasking::dataflow {
template <typename T> class Token
{
public:
Token() noexcept = default;
explicit Token(T &&data) noexcept : _data(std::move(data)) {}
explicit Token(const T &data) noexcept : _data(data) {}
Token(T &&data, tasking::annotation annotation) noexcept
: _data(std::move(data)), _annotation(std::move(annotation))
{
}
Token(const T &data, tasking::annotation annotation) noexcept : _data(data), _annotation(std::move(annotation)) {}
Token(Token<T> &&other) noexcept : _data(std::move(other._data)), _annotation(std::move(other._annotation)) {}
~Token() noexcept = default;
Token<T> &operator=(Token<T> &&other) noexcept
{
_data = std::move(other._data);
_annotation = other._annotation;
return *this;
}
[[nodiscard]] const T &data() const noexcept { return _data; }
[[nodiscard]] T &data() noexcept { return _data; }
[[nodiscard]] tasking::annotation annotation() const noexcept { return _annotation; }
[[nodiscard]] tasking::annotation &annotation() noexcept { return _annotation; }
private:
T _data;
class tasking::annotation _annotation
{
};
};
template <typename T> static inline Token<T> make_token(T &&data) noexcept
{
return Token<T>{std::move(data)};
}
template <typename T> static inline Token<T> make_token(const T &data) noexcept
{
return Token<T>{data};
}
template <typename T> static inline Token<T> make_token(T &&data, tasking::annotation annotation) noexcept
{
return Token<T>{std::move(data), annotation};
}
template <typename T> static inline Token<T> make_token(const T &data, tasking::annotation annotation) noexcept
{
return Token<T>{data, annotation};
}
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,18 @@
#pragma once
#include "token.h"
#include <cstdint>
#include <mx/tasking/prefetch_descriptor.h>
#include <vector>
namespace mx::tasking::dataflow {
template <typename T> class TokenGenerator
{
public:
constexpr TokenGenerator() noexcept = default;
virtual ~TokenGenerator() noexcept = default;
[[nodiscard]] virtual std::vector<Token<T>> generate(std::uint16_t worker_id) = 0;
[[nodiscard]] virtual std::uint64_t count() = 0;
};
} // namespace mx::tasking::dataflow

View File

@@ -0,0 +1,26 @@
#pragma once
#include "config.h"
#include <bitset>
#include <cstdint>
namespace mx::tasking {
/**
* Persists the channel load for the last 64 requests.
*/
class Load
{
public:
constexpr Load() = default;
~Load() = default;
void set(const std::uint16_t count_withdrawed_task) noexcept
{
_load = count_withdrawed_task / float(config::task_buffer_size());
}
[[nodiscard]] float get() const noexcept { return _load; }
private:
float _load{0U};
};
} // namespace mx::tasking

View File

@@ -0,0 +1,296 @@
#pragma once
#include <array>
#include <bit>
#include <cmath>
#include <cstdint>
#include <mx/memory/alignment_helper.h>
#include <mx/resource/ptr.h>
#include <mx/system/builtin.h>
#include <mx/system/environment.h>
#include <sstream>
#include <string>
#include <utility>
namespace mx::tasking {
class PrefetchDescriptor
{
public:
using data_t = std::uint64_t;
enum ExecuteType : std::uint8_t
{
Size = 0b01,
Callback = 0b10,
Mask = 0b11,
};
enum PrefetchType : std::uint8_t
{
Temporal = 0b01,
NonTemporal = 0b10,
Write = 0b11,
};
enum Type : std::uint8_t
{
None = 0b0000,
SizeTemporal = (ExecuteType::Size << 2U) | PrefetchType::Temporal,
SizeNonTemporal = (ExecuteType::Size << 2U) | PrefetchType::NonTemporal,
SizeWrite = (ExecuteType::Size << 2U) | PrefetchType::Write,
CallbackAny = ExecuteType::Callback << 2U,
MaskTemporal = (ExecuteType::Mask << 2U) | PrefetchType::Temporal,
MaskNonTemporal = (ExecuteType::Mask << 2U) | PrefetchType::NonTemporal,
MaskWrite = (ExecuteType::Mask << 2U) | PrefetchType::Write,
};
private:
static inline constexpr auto BITS = sizeof(data_t) * 8U;
static inline constexpr auto RESERVED_BITS = 4U;
static inline constexpr auto DATA_BITS = BITS - RESERVED_BITS;
static inline constexpr auto CLEAR_TYPE_MASK = std::numeric_limits<data_t>::max() >> RESERVED_BITS;
public:
[[nodiscard]] static constexpr auto capacity() { return DATA_BITS; }
[[nodiscard]] static constexpr auto bits() { return BITS; }
[[nodiscard]] static PrefetchDescriptor make_size(const PrefetchType type, const data_t data) noexcept
{
return PrefetchDescriptor{((data_t(ExecuteType::Size << 2U) | type) << DATA_BITS) | data};
}
[[nodiscard]] static PrefetchDescriptor make_mask(const PrefetchType type, const data_t data) noexcept
{
return PrefetchDescriptor{((data_t(ExecuteType::Mask << 2U) | type) << DATA_BITS) | data};
}
[[nodiscard]] static PrefetchDescriptor make_callback(const data_t data) noexcept
{
return PrefetchDescriptor{(data_t(ExecuteType::Callback << 2U) << DATA_BITS) | data};
}
constexpr PrefetchDescriptor() noexcept = default;
constexpr explicit PrefetchDescriptor(const data_t data) noexcept : _data(data) {}
~PrefetchDescriptor() noexcept = default;
/**
* @return The type if the descriptor.
*/
[[nodiscard]] Type id() const noexcept { return static_cast<Type>(_data >> DATA_BITS); }
[[nodiscard]] bool empty() const noexcept { return (_data & CLEAR_TYPE_MASK) == 0U; }
[[nodiscard]] data_t data() const noexcept { return _data; }
[[nodiscard]] data_t &data() noexcept { return _data; }
[[nodiscard]] data_t data_without_descriptor_bits() const noexcept { return _data & CLEAR_TYPE_MASK; }
PrefetchDescriptor operator|(const PrefetchDescriptor other) const noexcept
{
return PrefetchDescriptor{_data | other._data};
}
PrefetchDescriptor &operator|=(const PrefetchDescriptor other) noexcept
{
_data |= other._data;
return *this;
}
bool operator==(const PrefetchDescriptor other) const noexcept { return _data == other._data; }
private:
data_t _data{0U};
};
class PrefetchSizeView
{
public:
constexpr PrefetchSizeView(const PrefetchDescriptor::data_t data) noexcept : _data(data) {}
PrefetchSizeView(const PrefetchDescriptor data) noexcept : _data(data.data_without_descriptor_bits()) {}
~PrefetchSizeView() noexcept = default;
PrefetchSizeView &operator=(PrefetchSizeView &&) noexcept = default;
PrefetchSizeView &operator=(const PrefetchSizeView &) noexcept = default;
/**
* @return The size to prefetch.
*/
[[nodiscard]] std::uint64_t get() const noexcept { return _data; }
private:
PrefetchDescriptor::data_t _data;
};
class PrefetchMaskView
{
public:
constexpr PrefetchMaskView(const PrefetchDescriptor::data_t data) noexcept : _data(data) {}
PrefetchMaskView(const PrefetchDescriptor data) noexcept : _data(data.data_without_descriptor_bits()) {}
~PrefetchMaskView() noexcept = default;
PrefetchMaskView &operator=(PrefetchMaskView &&) noexcept = default;
PrefetchMaskView &operator=(const PrefetchMaskView &) noexcept = default;
/**
* @return Number of cache lines that can be stored within the mask.
*/
[[nodiscard]] static constexpr auto capacity() { return PrefetchDescriptor::capacity(); }
/**
* @return Number of set bits.
*/
[[nodiscard]] std::uint8_t count() const noexcept { return std::popcount(_data); }
/**
* @return True, if the data is empty.
*/
[[nodiscard]] bool empty() const noexcept { return _data == 0U; }
/**
* Tests if a given index is set.
*
* @param index Index to test.
* @return True, if the given index is set.
*/
[[nodiscard]] bool test(const std::uint8_t index) const noexcept
{
return static_cast<bool>(_data & (PrefetchDescriptor::data_t{1U} << index));
}
private:
PrefetchDescriptor::data_t _data;
};
class PrefetchCallbackView
{
public:
using callback_t = void (*)(void *);
[[nodiscard]] static constexpr auto bits_for_size() { return 8U; }
[[nodiscard]] static constexpr auto bits_for_pointer() { return PrefetchDescriptor::capacity() - bits_for_size(); }
constexpr PrefetchCallbackView(const PrefetchDescriptor::data_t data) noexcept : _data(data) {}
PrefetchCallbackView(const PrefetchDescriptor data) noexcept : _data(data.data_without_descriptor_bits()) {}
~PrefetchCallbackView() noexcept = default;
PrefetchCallbackView &operator=(PrefetchCallbackView &&) noexcept = default;
PrefetchCallbackView &operator=(const PrefetchCallbackView &) noexcept = default;
/**
* @return The number of cache lines that will be prefetched by the callback.
*/
[[nodiscard]] std::uint8_t size() const noexcept { return _data >> bits_for_pointer(); }
/**
* @return The callback for prefetching.
*/
[[nodiscard]] callback_t get() const noexcept
{
return reinterpret_cast<callback_t>(_data & PrefetchDescriptor::data_t(std::pow(2, bits_for_pointer()) - 1));
}
private:
PrefetchDescriptor::data_t _data;
};
class PrefetchSize
{
public:
[[nodiscard]] static PrefetchDescriptor make(const PrefetchDescriptor::PrefetchType type,
const std::uint64_t size) noexcept
{
return PrefetchDescriptor::make_size(type, size);
}
};
class PrefetchMask
{
public:
constexpr PrefetchMask() noexcept = default;
~PrefetchMask() noexcept = default;
void set(const std::uint8_t index) noexcept { _data |= (1U << index); }
[[nodiscard]] PrefetchDescriptor make(const PrefetchDescriptor::PrefetchType type) const noexcept
{
return PrefetchDescriptor::make_mask(type, _data);
}
[[nodiscard]] static PrefetchDescriptor make(const PrefetchDescriptor::PrefetchType type,
PrefetchDescriptor::data_t data) noexcept
{
return PrefetchDescriptor::make_mask(type, data);
}
private:
PrefetchDescriptor::data_t _data{0U};
};
class PrefetchCallback
{
public:
constexpr PrefetchCallback() noexcept = default;
~PrefetchCallback() noexcept = default;
[[nodiscard]] static PrefetchDescriptor make(const std::uint8_t size, const std::uintptr_t callback) noexcept
{
const auto data =
(PrefetchDescriptor::data_t(size) << PrefetchCallbackView::bits_for_pointer()) |
(callback & PrefetchDescriptor::data_t(std::pow(2, PrefetchCallbackView::bits_for_pointer()) - 1));
return PrefetchDescriptor::make_callback(data);
}
};
class PrefetchHint
{
public:
[[nodiscard]] static PrefetchHint make_size(const PrefetchDescriptor::PrefetchType type, const std::uint64_t size,
const resource::ptr resource) noexcept
{
return PrefetchHint{PrefetchSize::make(type, size), resource};
}
[[nodiscard]] static PrefetchHint make_callback(const std::uint8_t size, const std::uintptr_t callback,
const resource::ptr resource) noexcept
{
return PrefetchHint{PrefetchCallback::make(size, callback), resource};
}
constexpr PrefetchHint() noexcept = default;
constexpr PrefetchHint(const PrefetchDescriptor descriptor, const resource::ptr resource) noexcept
: _descriptor(descriptor), _resource(resource)
{
}
constexpr PrefetchHint(const PrefetchHint &) noexcept = default;
constexpr PrefetchHint(PrefetchHint &&) noexcept = default;
~PrefetchHint() noexcept = default;
PrefetchHint &operator=(const PrefetchHint &) = default;
PrefetchHint &operator=(PrefetchHint &&) = default;
[[nodiscard]] bool empty() const noexcept { return _descriptor.empty(); }
[[nodiscard]] PrefetchDescriptor descriptor() const noexcept { return _descriptor; }
[[nodiscard]] PrefetchMaskView as_mask() const noexcept
{
return PrefetchMaskView{_descriptor.data_without_descriptor_bits()};
}
[[nodiscard]] PrefetchSizeView as_size() const noexcept
{
return PrefetchSizeView{_descriptor.data_without_descriptor_bits()};
}
[[nodiscard]] resource::ptr resource() const noexcept { return _resource; }
bool operator==(const PrefetchHint &other) const noexcept
{
return _resource == other._resource && _descriptor == other._descriptor;
}
private:
PrefetchDescriptor _descriptor;
resource::ptr _resource;
};
} // namespace mx::tasking

View File

@@ -0,0 +1,39 @@
#pragma once
#include <cstdint>
#include <limits>
namespace mx::tasking {
class PrefetchDistance
{
public:
[[nodiscard]] constexpr static PrefetchDistance make_automatic() noexcept
{
return PrefetchDistance{std::numeric_limits<std::uint8_t>::max()};
}
constexpr explicit PrefetchDistance(const std::uint8_t prefetch_distance) noexcept
: _prefetch_distance(prefetch_distance)
{
}
constexpr PrefetchDistance(const PrefetchDistance &) noexcept = default;
~PrefetchDistance() noexcept = default;
PrefetchDistance &operator=(PrefetchDistance &&) noexcept = default;
[[nodiscard]] bool is_enabled() const noexcept { return _prefetch_distance > 0U; }
[[nodiscard]] bool is_automatic() const noexcept
{
return _prefetch_distance == std::numeric_limits<std::uint8_t>::max();
}
[[nodiscard]] bool is_fixed() const noexcept { return is_enabled() && is_automatic() == false; }
[[nodiscard]] std::uint8_t fixed_distance() const noexcept { return _prefetch_distance; }
private:
std::uint8_t _prefetch_distance;
};
} // namespace mx::tasking

View File

@@ -0,0 +1,59 @@
#include "prefetch_slot.h"
#include <mx/system/cache.h>
#include <mx/system/environment.h>
using namespace mx::tasking;
void PrefetchSlot::assign(const resource::ptr resource, const PrefetchDescriptor descriptor) noexcept
{
if (this->_item.has_resource() == false)
{
this->_item.resource(resource.get<std::int64_t>(), descriptor);
}
}
void PrefetchSlot::prefetch() noexcept
{
const auto prefetch_type = this->_item.prefetch_descriptor().id();
const auto prefetch_data = this->_item.prefetch_descriptor().data_without_descriptor_bits();
auto *resource = this->_item.resource();
switch (prefetch_type)
{
case PrefetchDescriptor::Type::SizeNonTemporal: {
const auto size = PrefetchSizeView{prefetch_data}.get();
system::cache::prefetch_range<system::cache::NTA, system::cache::read>(resource, size);
break;
}
case PrefetchDescriptor::Type::SizeTemporal: {
const auto size = PrefetchSizeView{prefetch_data}.get();
system::cache::prefetch_range<system::cache::L2, system::cache::read>(resource, size);
break;
}
case PrefetchDescriptor::Type::SizeWrite: {
const auto size = PrefetchSizeView{prefetch_data}.get();
system::cache::prefetch_range<system::cache::ALL, system::cache::write>(resource, size);
break;
}
case PrefetchDescriptor::Type::CallbackAny: {
auto *callback = PrefetchCallbackView{prefetch_data}.get();
callback(reinterpret_cast<void *>(resource));
break;
}
case PrefetchDescriptor::Type::None:
return;
case PrefetchDescriptor::Type::MaskTemporal: {
PrefetchSlot::prefetch_mask<system::cache::L2, system::cache::read>(resource, prefetch_data);
break;
}
case PrefetchDescriptor::Type::MaskNonTemporal: {
PrefetchSlot::prefetch_mask<system::cache::NTA, system::cache::read>(resource, prefetch_data);
break;
}
case PrefetchDescriptor::Type::MaskWrite: {
PrefetchSlot::prefetch_mask<system::cache::ALL, system::cache::write>(resource, prefetch_data);
break;
}
}
this->_item = PrefetchItem{};
}

View File

@@ -0,0 +1,197 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:

View File

@@ -0,0 +1,924 @@
#pragma once
#include "task.h"
#include <ealanos/util/ecpp/static_vector.h>
#include <mx/system/cache.h>
#include <utility>
namespace mx::tasking {
class PrefetchItem
{
public:
constexpr PrefetchItem() noexcept = default;
~PrefetchItem() noexcept = default;
PrefetchItem &operator=(PrefetchItem &&) noexcept = default;
[[nodiscard]] bool has_resource() const noexcept
{
return _resource != nullptr && _prefetch_descriptor.empty() == false;
}
[[nodiscard]] std::int64_t *resource() const noexcept { return _resource; }
[[nodiscard]] PrefetchDescriptor prefetch_descriptor() const noexcept { return _prefetch_descriptor; }
void resource(std::int64_t *resource, PrefetchDescriptor descriptor) noexcept
{
_resource = resource;
_prefetch_descriptor = descriptor;
}
private:
std::int64_t *_resource{nullptr};
PrefetchDescriptor _prefetch_descriptor{};
};
template <std::uint8_t S> class PrefetchMaskExecutor
{
public:
template <std::uint8_t N> static constexpr bool is_prefetch_cl()
{
constexpr auto MASK = 0b1 << N;
return (S & MASK) == MASK;
}
template <system::cache::level L, system::cache::access A>
static void execute([[maybe_unused]] const std::int64_t *address, [[maybe_unused]] const std::uint8_t word_offset)
{
if constexpr (is_prefetch_cl<0U>())
{
const auto *addr = address + (word_offset * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<1U>())
{
const auto *addr = address + ((word_offset + 1U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<2U>())
{
const auto *addr = address + ((word_offset + 2U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<3U>())
{
const auto *addr = address + ((word_offset + 3U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<4U>())
{
const auto *addr = address + ((word_offset + 4U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<5U>())
{
const auto *addr = address + ((word_offset + 5U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<6U>())
{
const auto *addr = address + ((word_offset + 6U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
if constexpr (is_prefetch_cl<7U>())
{
const auto *addr = address + ((word_offset + 7U) * 8U);
system::cache::prefetch<L, A, 1U>(addr);
}
}
};
/**
* A prefetch slot is part of the prefetch buffer used for task
* and resource prefetching
* A slot can contain up to one task and one resource that are
* prefetched by the channel.
*/
class PrefetchSlot
{
public:
constexpr PrefetchSlot() noexcept = default;
~PrefetchSlot() = default;
void assign(resource::ptr resource, PrefetchDescriptor descriptor) noexcept;
void prefetch() noexcept;
private:
PrefetchItem _item;
template <system::cache::level L, system::cache::access A>
static void prefetch_mask(const std::int64_t *address, const PrefetchDescriptor::data_t prefetch_mask) noexcept
{
PrefetchSlot::prefetch_word_at_offset<L, A, 0U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 1U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 2U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 3U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 4U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 5U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 6U>(address, prefetch_mask);
PrefetchSlot::prefetch_word_at_offset<L, A, 7U>(address, prefetch_mask);
}
template <system::cache::level L, system::cache::access A, std::uint8_t O>
static void prefetch_word_at_offset(const std::int64_t *address, const PrefetchDescriptor::data_t mask)
{
if constexpr (O == 0U)
{
const auto prefetch_word = std::uint8_t(mask & std::numeric_limits<std::uint8_t>::max());
PrefetchSlot::prefetch_word<L, A>(address, prefetch_word, 0U);
}
else
{
constexpr auto offset = O * 8U;
const auto prefetch_word = std::uint8_t((mask >> offset) & std::numeric_limits<std::uint8_t>::max());
if (prefetch_word > 0U)
{
PrefetchSlot::prefetch_word<L, A>(address, prefetch_word, offset);
}
}
}
template <system::cache::level L, system::cache::access A>
static void prefetch_word(const std::int64_t *address, const std::uint8_t word, const std::uint8_t word_offset)
{
switch (word)
{
case 0:
return;
case 1:
PrefetchMaskExecutor<1>::execute<L, A>(address, word_offset);
return;
case 2:
PrefetchMaskExecutor<2>::execute<L, A>(address, word_offset);
return;
case 3:
PrefetchMaskExecutor<3>::execute<L, A>(address, word_offset);
return;
case 4:
PrefetchMaskExecutor<4>::execute<L, A>(address, word_offset);
return;
case 5:
PrefetchMaskExecutor<5>::execute<L, A>(address, word_offset);
return;
case 6:
PrefetchMaskExecutor<6>::execute<L, A>(address, word_offset);
return;
case 7:
PrefetchMaskExecutor<7>::execute<L, A>(address, word_offset);
return;
case 8:
PrefetchMaskExecutor<8>::execute<L, A>(address, word_offset);
return;
case 9:
PrefetchMaskExecutor<9>::execute<L, A>(address, word_offset);
return;
case 10:
PrefetchMaskExecutor<10>::execute<L, A>(address, word_offset);
return;
case 11:
PrefetchMaskExecutor<11>::execute<L, A>(address, word_offset);
return;
case 12:
PrefetchMaskExecutor<12>::execute<L, A>(address, word_offset);
return;
case 13:
PrefetchMaskExecutor<13>::execute<L, A>(address, word_offset);
return;
case 14:
PrefetchMaskExecutor<14>::execute<L, A>(address, word_offset);
return;
case 15:
PrefetchMaskExecutor<15>::execute<L, A>(address, word_offset);
return;
case 16:
PrefetchMaskExecutor<16>::execute<L, A>(address, word_offset);
return;
case 17:
PrefetchMaskExecutor<17>::execute<L, A>(address, word_offset);
return;
case 18:
PrefetchMaskExecutor<18>::execute<L, A>(address, word_offset);
return;
case 19:
PrefetchMaskExecutor<19>::execute<L, A>(address, word_offset);
return;
case 20:
PrefetchMaskExecutor<20>::execute<L, A>(address, word_offset);
return;
case 21:
PrefetchMaskExecutor<21>::execute<L, A>(address, word_offset);
return;
case 22:
PrefetchMaskExecutor<22>::execute<L, A>(address, word_offset);
return;
case 23:
PrefetchMaskExecutor<23>::execute<L, A>(address, word_offset);
return;
case 24:
PrefetchMaskExecutor<24>::execute<L, A>(address, word_offset);
return;
case 25:
PrefetchMaskExecutor<25>::execute<L, A>(address, word_offset);
return;
case 26:
PrefetchMaskExecutor<26>::execute<L, A>(address, word_offset);
return;
case 27:
PrefetchMaskExecutor<27>::execute<L, A>(address, word_offset);
return;
case 28:
PrefetchMaskExecutor<28>::execute<L, A>(address, word_offset);
return;
case 29:
PrefetchMaskExecutor<29>::execute<L, A>(address, word_offset);
return;
case 30:
PrefetchMaskExecutor<30>::execute<L, A>(address, word_offset);
return;
case 31:
PrefetchMaskExecutor<31>::execute<L, A>(address, word_offset);
return;
case 32:
PrefetchMaskExecutor<32>::execute<L, A>(address, word_offset);
return;
case 33:
PrefetchMaskExecutor<33>::execute<L, A>(address, word_offset);
return;
case 34:
PrefetchMaskExecutor<34>::execute<L, A>(address, word_offset);
return;
case 35:
PrefetchMaskExecutor<35>::execute<L, A>(address, word_offset);
return;
case 36:
PrefetchMaskExecutor<36>::execute<L, A>(address, word_offset);
return;
case 37:
PrefetchMaskExecutor<37>::execute<L, A>(address, word_offset);
return;
case 38:
PrefetchMaskExecutor<38>::execute<L, A>(address, word_offset);
return;
case 39:
PrefetchMaskExecutor<39>::execute<L, A>(address, word_offset);
return;
case 40:
PrefetchMaskExecutor<40>::execute<L, A>(address, word_offset);
return;
case 41:
PrefetchMaskExecutor<41>::execute<L, A>(address, word_offset);
return;
case 42:
PrefetchMaskExecutor<42>::execute<L, A>(address, word_offset);
return;
case 43:
PrefetchMaskExecutor<43>::execute<L, A>(address, word_offset);
return;
case 44:
PrefetchMaskExecutor<44>::execute<L, A>(address, word_offset);
return;
case 45:
PrefetchMaskExecutor<45>::execute<L, A>(address, word_offset);
return;
case 46:
PrefetchMaskExecutor<46>::execute<L, A>(address, word_offset);
return;
case 47:
PrefetchMaskExecutor<47>::execute<L, A>(address, word_offset);
return;
case 48:
PrefetchMaskExecutor<48>::execute<L, A>(address, word_offset);
return;
case 49:
PrefetchMaskExecutor<49>::execute<L, A>(address, word_offset);
return;
case 50:
PrefetchMaskExecutor<50>::execute<L, A>(address, word_offset);
return;
case 51:
PrefetchMaskExecutor<51>::execute<L, A>(address, word_offset);
return;
case 52:
PrefetchMaskExecutor<52>::execute<L, A>(address, word_offset);
return;
case 53:
PrefetchMaskExecutor<53>::execute<L, A>(address, word_offset);
return;
case 54:
PrefetchMaskExecutor<54>::execute<L, A>(address, word_offset);
return;
case 55:
PrefetchMaskExecutor<55>::execute<L, A>(address, word_offset);
return;
case 56:
PrefetchMaskExecutor<56>::execute<L, A>(address, word_offset);
return;
case 57:
PrefetchMaskExecutor<57>::execute<L, A>(address, word_offset);
return;
case 58:
PrefetchMaskExecutor<58>::execute<L, A>(address, word_offset);
return;
case 59:
PrefetchMaskExecutor<59>::execute<L, A>(address, word_offset);
return;
case 60:
PrefetchMaskExecutor<60>::execute<L, A>(address, word_offset);
return;
case 61:
PrefetchMaskExecutor<61>::execute<L, A>(address, word_offset);
return;
case 62:
PrefetchMaskExecutor<62>::execute<L, A>(address, word_offset);
return;
case 63:
PrefetchMaskExecutor<63>::execute<L, A>(address, word_offset);
return;
case 64:
PrefetchMaskExecutor<64>::execute<L, A>(address, word_offset);
return;
case 65:
PrefetchMaskExecutor<65>::execute<L, A>(address, word_offset);
return;
case 66:
PrefetchMaskExecutor<66>::execute<L, A>(address, word_offset);
return;
case 67:
PrefetchMaskExecutor<67>::execute<L, A>(address, word_offset);
return;
case 68:
PrefetchMaskExecutor<68>::execute<L, A>(address, word_offset);
return;
case 69:
PrefetchMaskExecutor<69>::execute<L, A>(address, word_offset);
return;
case 70:
PrefetchMaskExecutor<70>::execute<L, A>(address, word_offset);
return;
case 71:
PrefetchMaskExecutor<71>::execute<L, A>(address, word_offset);
return;
case 72:
PrefetchMaskExecutor<72>::execute<L, A>(address, word_offset);
return;
case 73:
PrefetchMaskExecutor<73>::execute<L, A>(address, word_offset);
return;
case 74:
PrefetchMaskExecutor<74>::execute<L, A>(address, word_offset);
return;
case 75:
PrefetchMaskExecutor<75>::execute<L, A>(address, word_offset);
return;
case 76:
PrefetchMaskExecutor<76>::execute<L, A>(address, word_offset);
return;
case 77:
PrefetchMaskExecutor<77>::execute<L, A>(address, word_offset);
return;
case 78:
PrefetchMaskExecutor<78>::execute<L, A>(address, word_offset);
return;
case 79:
PrefetchMaskExecutor<79>::execute<L, A>(address, word_offset);
return;
case 80:
PrefetchMaskExecutor<80>::execute<L, A>(address, word_offset);
return;
case 81:
PrefetchMaskExecutor<81>::execute<L, A>(address, word_offset);
return;
case 82:
PrefetchMaskExecutor<82>::execute<L, A>(address, word_offset);
return;
case 83:
PrefetchMaskExecutor<83>::execute<L, A>(address, word_offset);
return;
case 84:
PrefetchMaskExecutor<84>::execute<L, A>(address, word_offset);
return;
case 85:
PrefetchMaskExecutor<85>::execute<L, A>(address, word_offset);
return;
case 86:
PrefetchMaskExecutor<86>::execute<L, A>(address, word_offset);
return;
case 87:
PrefetchMaskExecutor<87>::execute<L, A>(address, word_offset);
return;
case 88:
PrefetchMaskExecutor<88>::execute<L, A>(address, word_offset);
return;
case 89:
PrefetchMaskExecutor<89>::execute<L, A>(address, word_offset);
return;
case 90:
PrefetchMaskExecutor<90>::execute<L, A>(address, word_offset);
return;
case 91:
PrefetchMaskExecutor<91>::execute<L, A>(address, word_offset);
return;
case 92:
PrefetchMaskExecutor<92>::execute<L, A>(address, word_offset);
return;
case 93:
PrefetchMaskExecutor<93>::execute<L, A>(address, word_offset);
return;
case 94:
PrefetchMaskExecutor<94>::execute<L, A>(address, word_offset);
return;
case 95:
PrefetchMaskExecutor<95>::execute<L, A>(address, word_offset);
return;
case 96:
PrefetchMaskExecutor<96>::execute<L, A>(address, word_offset);
return;
case 97:
PrefetchMaskExecutor<97>::execute<L, A>(address, word_offset);
return;
case 98:
PrefetchMaskExecutor<98>::execute<L, A>(address, word_offset);
return;
case 99:
PrefetchMaskExecutor<99>::execute<L, A>(address, word_offset);
return;
case 100:
PrefetchMaskExecutor<100>::execute<L, A>(address, word_offset);
return;
case 101:
PrefetchMaskExecutor<101>::execute<L, A>(address, word_offset);
return;
case 102:
PrefetchMaskExecutor<102>::execute<L, A>(address, word_offset);
return;
case 103:
PrefetchMaskExecutor<103>::execute<L, A>(address, word_offset);
return;
case 104:
PrefetchMaskExecutor<104>::execute<L, A>(address, word_offset);
return;
case 105:
PrefetchMaskExecutor<105>::execute<L, A>(address, word_offset);
return;
case 106:
PrefetchMaskExecutor<106>::execute<L, A>(address, word_offset);
return;
case 107:
PrefetchMaskExecutor<107>::execute<L, A>(address, word_offset);
return;
case 108:
PrefetchMaskExecutor<108>::execute<L, A>(address, word_offset);
return;
case 109:
PrefetchMaskExecutor<109>::execute<L, A>(address, word_offset);
return;
case 110:
PrefetchMaskExecutor<110>::execute<L, A>(address, word_offset);
return;
case 111:
PrefetchMaskExecutor<111>::execute<L, A>(address, word_offset);
return;
case 112:
PrefetchMaskExecutor<112>::execute<L, A>(address, word_offset);
return;
case 113:
PrefetchMaskExecutor<113>::execute<L, A>(address, word_offset);
return;
case 114:
PrefetchMaskExecutor<114>::execute<L, A>(address, word_offset);
return;
case 115:
PrefetchMaskExecutor<115>::execute<L, A>(address, word_offset);
return;
case 116:
PrefetchMaskExecutor<116>::execute<L, A>(address, word_offset);
return;
case 117:
PrefetchMaskExecutor<117>::execute<L, A>(address, word_offset);
return;
case 118:
PrefetchMaskExecutor<118>::execute<L, A>(address, word_offset);
return;
case 119:
PrefetchMaskExecutor<119>::execute<L, A>(address, word_offset);
return;
case 120:
PrefetchMaskExecutor<120>::execute<L, A>(address, word_offset);
return;
case 121:
PrefetchMaskExecutor<121>::execute<L, A>(address, word_offset);
return;
case 122:
PrefetchMaskExecutor<122>::execute<L, A>(address, word_offset);
return;
case 123:
PrefetchMaskExecutor<123>::execute<L, A>(address, word_offset);
return;
case 124:
PrefetchMaskExecutor<124>::execute<L, A>(address, word_offset);
return;
case 125:
PrefetchMaskExecutor<125>::execute<L, A>(address, word_offset);
return;
case 126:
PrefetchMaskExecutor<126>::execute<L, A>(address, word_offset);
return;
case 127:
PrefetchMaskExecutor<127>::execute<L, A>(address, word_offset);
return;
case 128:
PrefetchMaskExecutor<128>::execute<L, A>(address, word_offset);
return;
case 129:
PrefetchMaskExecutor<129>::execute<L, A>(address, word_offset);
return;
case 130:
PrefetchMaskExecutor<130>::execute<L, A>(address, word_offset);
return;
case 131:
PrefetchMaskExecutor<131>::execute<L, A>(address, word_offset);
return;
case 132:
PrefetchMaskExecutor<132>::execute<L, A>(address, word_offset);
return;
case 133:
PrefetchMaskExecutor<133>::execute<L, A>(address, word_offset);
return;
case 134:
PrefetchMaskExecutor<134>::execute<L, A>(address, word_offset);
return;
case 135:
PrefetchMaskExecutor<135>::execute<L, A>(address, word_offset);
return;
case 136:
PrefetchMaskExecutor<136>::execute<L, A>(address, word_offset);
return;
case 137:
PrefetchMaskExecutor<137>::execute<L, A>(address, word_offset);
return;
case 138:
PrefetchMaskExecutor<138>::execute<L, A>(address, word_offset);
return;
case 139:
PrefetchMaskExecutor<139>::execute<L, A>(address, word_offset);
return;
case 140:
PrefetchMaskExecutor<140>::execute<L, A>(address, word_offset);
return;
case 141:
PrefetchMaskExecutor<141>::execute<L, A>(address, word_offset);
return;
case 142:
PrefetchMaskExecutor<142>::execute<L, A>(address, word_offset);
return;
case 143:
PrefetchMaskExecutor<143>::execute<L, A>(address, word_offset);
return;
case 144:
PrefetchMaskExecutor<144>::execute<L, A>(address, word_offset);
return;
case 145:
PrefetchMaskExecutor<145>::execute<L, A>(address, word_offset);
return;
case 146:
PrefetchMaskExecutor<146>::execute<L, A>(address, word_offset);
return;
case 147:
PrefetchMaskExecutor<147>::execute<L, A>(address, word_offset);
return;
case 148:
PrefetchMaskExecutor<148>::execute<L, A>(address, word_offset);
return;
case 149:
PrefetchMaskExecutor<149>::execute<L, A>(address, word_offset);
return;
case 150:
PrefetchMaskExecutor<150>::execute<L, A>(address, word_offset);
return;
case 151:
PrefetchMaskExecutor<151>::execute<L, A>(address, word_offset);
return;
case 152:
PrefetchMaskExecutor<152>::execute<L, A>(address, word_offset);
return;
case 153:
PrefetchMaskExecutor<153>::execute<L, A>(address, word_offset);
return;
case 154:
PrefetchMaskExecutor<154>::execute<L, A>(address, word_offset);
return;
case 155:
PrefetchMaskExecutor<155>::execute<L, A>(address, word_offset);
return;
case 156:
PrefetchMaskExecutor<156>::execute<L, A>(address, word_offset);
return;
case 157:
PrefetchMaskExecutor<157>::execute<L, A>(address, word_offset);
return;
case 158:
PrefetchMaskExecutor<158>::execute<L, A>(address, word_offset);
return;
case 159:
PrefetchMaskExecutor<159>::execute<L, A>(address, word_offset);
return;
case 160:
PrefetchMaskExecutor<160>::execute<L, A>(address, word_offset);
return;
case 161:
PrefetchMaskExecutor<161>::execute<L, A>(address, word_offset);
return;
case 162:
PrefetchMaskExecutor<162>::execute<L, A>(address, word_offset);
return;
case 163:
PrefetchMaskExecutor<163>::execute<L, A>(address, word_offset);
return;
case 164:
PrefetchMaskExecutor<164>::execute<L, A>(address, word_offset);
return;
case 165:
PrefetchMaskExecutor<165>::execute<L, A>(address, word_offset);
return;
case 166:
PrefetchMaskExecutor<166>::execute<L, A>(address, word_offset);
return;
case 167:
PrefetchMaskExecutor<167>::execute<L, A>(address, word_offset);
return;
case 168:
PrefetchMaskExecutor<168>::execute<L, A>(address, word_offset);
return;
case 169:
PrefetchMaskExecutor<169>::execute<L, A>(address, word_offset);
return;
case 170:
PrefetchMaskExecutor<170>::execute<L, A>(address, word_offset);
return;
case 171:
PrefetchMaskExecutor<171>::execute<L, A>(address, word_offset);
return;
case 172:
PrefetchMaskExecutor<172>::execute<L, A>(address, word_offset);
return;
case 173:
PrefetchMaskExecutor<173>::execute<L, A>(address, word_offset);
return;
case 174:
PrefetchMaskExecutor<174>::execute<L, A>(address, word_offset);
return;
case 175:
PrefetchMaskExecutor<175>::execute<L, A>(address, word_offset);
return;
case 176:
PrefetchMaskExecutor<176>::execute<L, A>(address, word_offset);
return;
case 177:
PrefetchMaskExecutor<177>::execute<L, A>(address, word_offset);
return;
case 178:
PrefetchMaskExecutor<178>::execute<L, A>(address, word_offset);
return;
case 179:
PrefetchMaskExecutor<179>::execute<L, A>(address, word_offset);
return;
case 180:
PrefetchMaskExecutor<180>::execute<L, A>(address, word_offset);
return;
case 181:
PrefetchMaskExecutor<181>::execute<L, A>(address, word_offset);
return;
case 182:
PrefetchMaskExecutor<182>::execute<L, A>(address, word_offset);
return;
case 183:
PrefetchMaskExecutor<183>::execute<L, A>(address, word_offset);
return;
case 184:
PrefetchMaskExecutor<184>::execute<L, A>(address, word_offset);
return;
case 185:
PrefetchMaskExecutor<185>::execute<L, A>(address, word_offset);
return;
case 186:
PrefetchMaskExecutor<186>::execute<L, A>(address, word_offset);
return;
case 187:
PrefetchMaskExecutor<187>::execute<L, A>(address, word_offset);
return;
case 188:
PrefetchMaskExecutor<188>::execute<L, A>(address, word_offset);
return;
case 189:
PrefetchMaskExecutor<189>::execute<L, A>(address, word_offset);
return;
case 190:
PrefetchMaskExecutor<190>::execute<L, A>(address, word_offset);
return;
case 191:
PrefetchMaskExecutor<191>::execute<L, A>(address, word_offset);
return;
case 192:
PrefetchMaskExecutor<192>::execute<L, A>(address, word_offset);
return;
case 193:
PrefetchMaskExecutor<193>::execute<L, A>(address, word_offset);
return;
case 194:
PrefetchMaskExecutor<194>::execute<L, A>(address, word_offset);
return;
case 195:
PrefetchMaskExecutor<195>::execute<L, A>(address, word_offset);
return;
case 196:
PrefetchMaskExecutor<196>::execute<L, A>(address, word_offset);
return;
case 197:
PrefetchMaskExecutor<197>::execute<L, A>(address, word_offset);
return;
case 198:
PrefetchMaskExecutor<198>::execute<L, A>(address, word_offset);
return;
case 199:
PrefetchMaskExecutor<199>::execute<L, A>(address, word_offset);
return;
case 200:
PrefetchMaskExecutor<200>::execute<L, A>(address, word_offset);
return;
case 201:
PrefetchMaskExecutor<201>::execute<L, A>(address, word_offset);
return;
case 202:
PrefetchMaskExecutor<202>::execute<L, A>(address, word_offset);
return;
case 203:
PrefetchMaskExecutor<203>::execute<L, A>(address, word_offset);
return;
case 204:
PrefetchMaskExecutor<204>::execute<L, A>(address, word_offset);
return;
case 205:
PrefetchMaskExecutor<205>::execute<L, A>(address, word_offset);
return;
case 206:
PrefetchMaskExecutor<206>::execute<L, A>(address, word_offset);
return;
case 207:
PrefetchMaskExecutor<207>::execute<L, A>(address, word_offset);
return;
case 208:
PrefetchMaskExecutor<208>::execute<L, A>(address, word_offset);
return;
case 209:
PrefetchMaskExecutor<209>::execute<L, A>(address, word_offset);
return;
case 210:
PrefetchMaskExecutor<210>::execute<L, A>(address, word_offset);
return;
case 211:
PrefetchMaskExecutor<211>::execute<L, A>(address, word_offset);
return;
case 212:
PrefetchMaskExecutor<212>::execute<L, A>(address, word_offset);
return;
case 213:
PrefetchMaskExecutor<213>::execute<L, A>(address, word_offset);
return;
case 214:
PrefetchMaskExecutor<214>::execute<L, A>(address, word_offset);
return;
case 215:
PrefetchMaskExecutor<215>::execute<L, A>(address, word_offset);
return;
case 216:
PrefetchMaskExecutor<216>::execute<L, A>(address, word_offset);
return;
case 217:
PrefetchMaskExecutor<217>::execute<L, A>(address, word_offset);
return;
case 218:
PrefetchMaskExecutor<218>::execute<L, A>(address, word_offset);
return;
case 219:
PrefetchMaskExecutor<219>::execute<L, A>(address, word_offset);
return;
case 220:
PrefetchMaskExecutor<220>::execute<L, A>(address, word_offset);
return;
case 221:
PrefetchMaskExecutor<221>::execute<L, A>(address, word_offset);
return;
case 222:
PrefetchMaskExecutor<222>::execute<L, A>(address, word_offset);
return;
case 223:
PrefetchMaskExecutor<223>::execute<L, A>(address, word_offset);
return;
case 224:
PrefetchMaskExecutor<224>::execute<L, A>(address, word_offset);
return;
case 225:
PrefetchMaskExecutor<225>::execute<L, A>(address, word_offset);
return;
case 226:
PrefetchMaskExecutor<226>::execute<L, A>(address, word_offset);
return;
case 227:
PrefetchMaskExecutor<227>::execute<L, A>(address, word_offset);
return;
case 228:
PrefetchMaskExecutor<228>::execute<L, A>(address, word_offset);
return;
case 229:
PrefetchMaskExecutor<229>::execute<L, A>(address, word_offset);
return;
case 230:
PrefetchMaskExecutor<230>::execute<L, A>(address, word_offset);
return;
case 231:
PrefetchMaskExecutor<231>::execute<L, A>(address, word_offset);
return;
case 232:
PrefetchMaskExecutor<232>::execute<L, A>(address, word_offset);
return;
case 233:
PrefetchMaskExecutor<233>::execute<L, A>(address, word_offset);
return;
case 234:
PrefetchMaskExecutor<234>::execute<L, A>(address, word_offset);
return;
case 235:
PrefetchMaskExecutor<235>::execute<L, A>(address, word_offset);
return;
case 236:
PrefetchMaskExecutor<236>::execute<L, A>(address, word_offset);
return;
case 237:
PrefetchMaskExecutor<237>::execute<L, A>(address, word_offset);
return;
case 238:
PrefetchMaskExecutor<238>::execute<L, A>(address, word_offset);
return;
case 239:
PrefetchMaskExecutor<239>::execute<L, A>(address, word_offset);
return;
case 240:
PrefetchMaskExecutor<240>::execute<L, A>(address, word_offset);
return;
case 241:
PrefetchMaskExecutor<241>::execute<L, A>(address, word_offset);
return;
case 242:
PrefetchMaskExecutor<242>::execute<L, A>(address, word_offset);
return;
case 243:
PrefetchMaskExecutor<243>::execute<L, A>(address, word_offset);
return;
case 244:
PrefetchMaskExecutor<244>::execute<L, A>(address, word_offset);
return;
case 245:
PrefetchMaskExecutor<245>::execute<L, A>(address, word_offset);
return;
case 246:
PrefetchMaskExecutor<246>::execute<L, A>(address, word_offset);
return;
case 247:
PrefetchMaskExecutor<247>::execute<L, A>(address, word_offset);
return;
case 248:
PrefetchMaskExecutor<248>::execute<L, A>(address, word_offset);
return;
case 249:
PrefetchMaskExecutor<249>::execute<L, A>(address, word_offset);
return;
case 250:
PrefetchMaskExecutor<250>::execute<L, A>(address, word_offset);
return;
case 251:
PrefetchMaskExecutor<251>::execute<L, A>(address, word_offset);
return;
case 252:
PrefetchMaskExecutor<252>::execute<L, A>(address, word_offset);
return;
case 253:
PrefetchMaskExecutor<253>::execute<L, A>(address, word_offset);
return;
case 254:
PrefetchMaskExecutor<254>::execute<L, A>(address, word_offset);
return;
case 255:
PrefetchMaskExecutor<255>::execute<L, A>(address, word_offset);
return;
}
}
};
} // namespace mx::tasking

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
namespace mx::tasking {
enum priority : std::uint8_t
{
low = 0U,
normal = 1U
};
}

View File

@@ -0,0 +1,110 @@
#include "idle_profiler.h"
#include <mx/memory/global_heap.h>
#include <mx/tasking/runtime.h>
using namespace mx::tasking::profiling;
IdleProfileTask::IdleProfileTask(util::maybe_atomic<bool> &is_running) : _is_profiler_running(is_running)
{
_idle_ranges.reserve(1U << 16U);
}
mx::tasking::TaskResult IdleProfileTask::execute(const std::uint16_t /*worker_id*/)
{
this->_is_task_running = true;
auto range = TimeRange{};
// while (this->_is_profiler_running && this->_channel.empty())
// {
// this->_channel.fill();
// }
range.stop();
if (range.nanoseconds() > 10U)
{
this->_idle_ranges.emplace_back(std::move(range));
}
this->_is_task_running = false;
if (this->_is_profiler_running)
{
return tasking::TaskResult::make_succeed(this);
}
return tasking::TaskResult::make_null();
}
IdleProfiler::~IdleProfiler()
{
for (auto *task : this->_tasks)
{
delete task;
}
}
void IdleProfiler::start() noexcept
{
if (this->_is_running)
{
return;
}
for (auto *task : this->_tasks)
{
delete task;
}
this->_tasks.clear();
this->_start = std::chrono::steady_clock::now();
this->_is_running = true;
}
// void IdleProfiler::start() noexcept
//{
//// auto *task = new (memory::GlobalHeap::allocate_cache_line_aligned(sizeof(IdleProfileTask)))
//// IdleProfileTask(this->_is_running);
//// task->annotate(channel.id());
//// task->annotate(mx::tasking::priority::low);
//// this->_tasks.push_back(task);
//// mx::tasking::runtime::spawn(*task);
//}
IdleTimes IdleProfiler::stop() noexcept
{
this->_is_running = false;
const auto end = std::chrono::steady_clock::now();
const auto start = this->_start;
auto idle_ranges = std::vector<std::vector<NormalizedTimeRange>>{};
idle_ranges.reserve(mx::tasking::runtime::workers());
for (auto *task : this->_tasks)
{
if (task == nullptr)
{
continue;
}
// Wait for the task to finish.
// while(channel_task->is_running());
if (task->idle_ranges().empty() == false)
{
// const auto &idle_range = task->idle_ranges();
// auto normalized_range = std::vector<NormalizedTimeRange>{};
// std::transform(idle_range.begin(), idle_range.end(), std::back_inserter(normalized_range),
// [start](const auto &time_range) { return time_range.normalize(start); });
//
// idle_ranges.emplace_back(std::move(normalized_range));
}
else
{
idle_ranges.emplace_back(std::vector<NormalizedTimeRange>{});
}
}
return IdleTimes{std::move(idle_ranges), end - start};
}

View File

@@ -0,0 +1,641 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h \
/home/mml/genode-igb/repos/libports/include/libc/component.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h:
/home/mml/genode-igb/repos/libports/include/libc/component.h:

View File

@@ -0,0 +1,71 @@
#pragma once
#include "time.h"
#include <chrono>
#include <mx/tasking/task.h>
#include <mx/util/maybe_atomic.h>
#include <optional>
#include <utility>
#include <vector>
namespace mx::tasking::profiling {
/**
* Task, that is scheduled with low priority and gets CPU time,
* whenever no other task is available.
* Every time the task gets executed, it will record the time range,
* until the channel has new tasks for execution.
*/
class IdleProfileTask final : public TaskInterface
{
public:
IdleProfileTask(util::maybe_atomic<bool> &is_running);
~IdleProfileTask() override = default;
TaskResult execute(std::uint16_t worker_id) override;
[[nodiscard]] std::vector<TimeRange> &idle_ranges() noexcept { return _idle_ranges; }
[[nodiscard]] bool is_running() const noexcept { return _is_task_running; }
private:
util::maybe_atomic<bool> &_is_profiler_running;
util::maybe_atomic<bool> _is_task_running{false};
std::vector<TimeRange> _idle_ranges;
};
/**
* Schedules the idle/profiling task to every channel and
* writes the memory to a given file.
*/
class IdleProfiler
{
public:
IdleProfiler() noexcept = default;
~IdleProfiler();
/**
* Enable profiling and set the result file.
* @param profiling_output_file File, where results should be written to.
*/
void start() noexcept;
/**
* Normalizes all time ranges and writes them to the specified
* file.
*/
IdleTimes stop() noexcept;
[[nodiscard]] bool is_running() const noexcept { return _is_running; }
private:
util::maybe_atomic<bool> _is_running{false};
// Time point of the runtime start.
alignas(64) std::chrono::steady_clock::time_point _start;
// List of all idle/profile tasks.
std::vector<IdleProfileTask *> _tasks;
};
} // namespace mx::tasking::profiling

View File

@@ -0,0 +1,171 @@
#pragma once
#include <array>
#include <cstdint>
#include <cstring>
#include <mx/memory/global_heap.h>
#include <mx/tasking/config.h>
#include <mx/util/aligned_t.h>
#include <numeric>
#include <unordered_map>
#include <utility>
#include <vector>
namespace mx::tasking::profiling {
class WorkerTaskCounter
{
public:
WorkerTaskCounter() noexcept = default;
explicit WorkerTaskCounter(const std::uint16_t count_workers) noexcept { _counter.resize(count_workers, 0U); }
WorkerTaskCounter(WorkerTaskCounter &&) noexcept = default;
WorkerTaskCounter(const WorkerTaskCounter &) noexcept = default;
~WorkerTaskCounter() noexcept = default;
WorkerTaskCounter &operator=(WorkerTaskCounter &&) noexcept = default;
std::uint64_t operator[](const std::size_t index) const noexcept { return _counter[index]; }
std::uint64_t &operator[](const std::size_t index) noexcept { return _counter[index]; }
[[nodiscard]] std::uint64_t sum() const noexcept { return std::accumulate(_counter.begin(), _counter.end(), 0U); }
[[nodiscard]] std::uint16_t size() const noexcept { return _counter.size(); }
WorkerTaskCounter operator-(const WorkerTaskCounter &other) const noexcept
{
auto counter = this->_counter;
for (auto worker_id = 0U; worker_id < counter.size(); ++worker_id)
{
counter[worker_id] -= other[worker_id];
}
return WorkerTaskCounter{std::move(counter)};
}
WorkerTaskCounter &operator-=(const WorkerTaskCounter &other) noexcept
{
for (auto worker_id = 0U; worker_id < _counter.size(); ++worker_id)
{
_counter[worker_id] -= other[worker_id];
}
return *this;
}
private:
explicit WorkerTaskCounter(std::vector<std::uint64_t> &&counter) noexcept : _counter(std::move(counter)) {}
std::vector<std::uint64_t> _counter;
};
/**
* Collector for tasking statistics (scheduled tasks, executed tasks, ...).
*/
class TaskCounter
{
public:
using counter_line_t = util::aligned_t<std::array<std::uint64_t, 7U>>;
enum Counter : std::uint8_t
{
Dispatched,
DispatchedLocally,
DispatchedRemotely,
Executed,
ExecutedReader,
ExecutedWriter,
FilledBuffer
};
explicit TaskCounter(const std::uint16_t count_workers) noexcept : _count_workers(count_workers)
{
_counter = new (memory::GlobalHeap::allocate_cache_line_aligned(sizeof(counter_line_t) * count_workers))
counter_line_t[count_workers];
clear();
}
TaskCounter(const TaskCounter &) = delete;
TaskCounter(TaskCounter &&other) noexcept
: _count_workers(other._count_workers), _counter(std::exchange(other._counter, nullptr))
{
}
~TaskCounter() noexcept { delete[] this->_counter; }
TaskCounter &operator=(const TaskCounter &) = delete;
/**
* Clears all collected statistics.
*/
void clear() noexcept { std::memset(static_cast<void *>(_counter), 0, sizeof(counter_line_t) * _count_workers); }
/**
* Increment the template-given counter by one for the given channel.
* @param worker_id Worker to increment the statistics for.
*/
template <Counter C> void increment(const std::uint16_t worker_id) noexcept
{
++_counter[worker_id].value()[static_cast<std::uint8_t>(C)];
}
/**
* Read the given counter for a given channel.
* @param counter Counter to read.
* @param worker_id Worker the counter is for.
* @return Value of the counter.
*/
[[nodiscard]] std::uint64_t get(const Counter counter, const std::uint16_t worker_id) const noexcept
{
return _counter[worker_id].value()[static_cast<std::uint8_t>(counter)];
}
/**
* Read and aggregate the counter for all channels.
* @param counter Counter to read.
* @return Value of the counter for all channels.
*/
[[nodiscard]] WorkerTaskCounter get(const Counter counter) const noexcept
{
auto worker_counter = WorkerTaskCounter{_count_workers};
for (auto i = 0U; i < _count_workers; ++i)
{
worker_counter[i] = get(counter, i);
}
return worker_counter;
}
/**
* Read counter for all channels.
*
* @return List of channel counter for every counter.
*/
[[nodiscard]] std::unordered_map<Counter, WorkerTaskCounter> get() const noexcept
{
auto counter = std::unordered_map<Counter, WorkerTaskCounter>{};
counter.reserve(7U);
counter.insert(std::make_pair(Counter::Dispatched, get(Counter::Dispatched)));
counter.insert(std::make_pair(Counter::DispatchedLocally, get(Counter::DispatchedLocally)));
counter.insert(std::make_pair(Counter::DispatchedRemotely, get(Counter::DispatchedRemotely)));
counter.insert(std::make_pair(Counter::Executed, get(Counter::Executed)));
counter.insert(std::make_pair(Counter::ExecutedReader, get(Counter::ExecutedReader)));
counter.insert(std::make_pair(Counter::ExecutedWriter, get(Counter::ExecutedWriter)));
counter.insert(std::make_pair(Counter::FilledBuffer, get(Counter::FilledBuffer)));
return counter;
}
private:
// Number of channels to monitor.
const std::uint16_t _count_workers;
// Memory for storing the counter.
counter_line_t *_counter{nullptr};
};
} // namespace mx::tasking::profiling

View File

@@ -0,0 +1,150 @@
#include "task_tracer.h"
using namespace mx::tasking::profiling;
TaskTraces TaskTracer::stop()
{
_is_enabled = false;
auto traces = std::vector<std::vector<std::pair<std::uint64_t, NormalizedTimeRange>>>{};
traces.reserve(this->_worker_task_tracers.size());
const auto start = this->_start;
for (auto &channel_tracer : this->_worker_task_tracers)
{
/// Get list of traces from channel tracer.
const auto &channel_traces = channel_tracer.traces();
/// Create a single list containing all traces from that channel.
auto &channel_normalized_traces =
traces.emplace_back(std::vector<std::pair<std::uint64_t, NormalizedTimeRange>>{});
channel_normalized_traces.reserve(channel_traces.size() * WorkerTaskTracer::SIZE);
/// Flatten the list of lists into a normalized list.
for (const auto &trace_list : channel_traces)
{
std::transform(trace_list.begin(), trace_list.end(), std::back_inserter(channel_normalized_traces),
[start](const auto &trace) {
return std::make_pair(std::get<0>(trace), std::get<1>(trace).normalize(start));
});
}
/// Clear the channel tracer.
channel_tracer.clear();
}
auto task_traces = TaskTraces{this->_start.time_since_epoch(), this->_task_trace_ids, std::move(traces)};
this->_task_trace_ids.clear();
return task_traces;
}
nlohmann::json TaskTraces::to_json() const
{
auto task_times = std::unordered_map<std::uint64_t, std::uint64_t>{};
auto traces = nlohmann::json{};
traces["start"] = this->_start_timestamp.count();
traces["tasks"] = nlohmann::json{};
/// If not found, add the "unknown" tasks for all tasks without trace id.
if (this->_names.find(0U) == this->_names.end())
{
traces["tasks"].emplace_back(nlohmann::json{{"id", 0U}, {"name", "Unknown"}});
task_times.insert(std::make_pair(0U, 0U));
}
/// Add the registered tasks.
for (const auto &[task_id, name] : this->_names)
{
traces["tasks"].emplace_back(nlohmann::json{{"id", task_id}, {"name", name}});
task_times.insert(std::make_pair(task_id, 0U));
}
/// Calculate traces per channel.
traces["traces"] = nlohmann::json{};
for (const auto &worker : this->_traces)
{
auto channel_traces = nlohmann::json{};
for (const auto &trace : worker)
{
const auto task_id = std::get<0>(trace);
const auto start = std::get<1>(trace).start().count();
const auto end = std::get<1>(trace).end().count();
auto task = nlohmann::json{};
task["tid"] = task_id;
task["s"] = start;
task["e"] = end;
channel_traces.emplace_back(std::move(task));
auto task_times_iterator = task_times.find(task_id);
if (task_times_iterator != task_times.end()) [[likely]]
{
task_times_iterator->second += end - start;
}
else
{
task_times.insert(std::make_pair(task_id, end - start));
}
}
traces["traces"].emplace_back(std::move(channel_traces));
}
/// Aggregate sums for every task type.
const auto count_worker = this->_traces.size();
for (auto &task : traces["tasks"])
{
if (task.contains("id"))
{
const auto task_id = task["id"].get<std::uint64_t>();
auto task_iterator = task_times.find(task_id);
if (task_iterator != task_times.end())
{
const auto time_ms = double(task_iterator->second) / 1000000.0;
task["ms"] = time_ms;
task["ms_per_worker"] = time_ms / count_worker;
}
else
{
task["ms"] = double(0);
task["ms_per_worker"] = double(0);
}
}
}
/// Calculate idle times.
auto min_ns = std::numeric_limits<std::uint64_t>::max();
auto max_ns = std::numeric_limits<std::uint64_t>::min();
for (const auto &channel : this->_traces)
{
if (channel.empty() == false)
{
min_ns = std::min<std::uint64_t>(min_ns, channel.front().second.start().count());
max_ns = std::max<std::uint64_t>(max_ns, channel.back().second.end().count());
}
}
const auto runtime_ns = (max_ns - min_ns) * count_worker;
const auto runtime_ms = runtime_ns / 1000000.0;
const auto task_time_ns = std::accumulate(task_times.begin(), task_times.end(), double(0),
[](const auto sum, const auto &time) { return sum + time.second; });
const auto idle_ms = double(runtime_ns - task_time_ns) / 1000000.0;
traces["ms_idle"] = idle_ms;
traces["ms_idle_per_worker"] = idle_ms / count_worker;
/// Calculate task percent.
traces["percent_idle"] = 100.0 / runtime_ms * (idle_ms / count_worker);
for (auto &task : traces["tasks"])
{
if (task.contains("ms_per_worker"))
{
task["percent"] = 100.0 / runtime_ms * task["ms_per_worker"].get<float>();
}
else
{
task["percent"] = double(0);
}
}
return traces;
}

View File

@@ -0,0 +1,161 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:

View File

@@ -0,0 +1,141 @@
#pragma once
#include "time.h"
#include <cstdlib>
#include <mx/system/cache.h>
#include <ealanos/util/json.hpp>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace mx::tasking::profiling {
class alignas(system::cache::line_size()) WorkerTaskTracer
{
public:
constexpr static auto inline SIZE = 1U << 16U;
WorkerTaskTracer() = default;
WorkerTaskTracer(WorkerTaskTracer &&) noexcept = default;
WorkerTaskTracer(const WorkerTaskTracer &) = delete;
WorkerTaskTracer &operator=(WorkerTaskTracer &&) noexcept = default;
WorkerTaskTracer &operator=(const WorkerTaskTracer &) = delete;
~WorkerTaskTracer() = default;
void emplace_back(const std::uint64_t task_id, TimeRange &&time_range)
{
_traces.back().emplace_back(std::make_pair(task_id, std::move(time_range)));
if (_traces.back().size() >= _traces.back().capacity() - 1U) [[unlikely]]
{
_traces.emplace_back(std::vector<std::pair<std::uint64_t, TimeRange>>{});
_traces.back().reserve(SIZE);
}
}
[[nodiscard]] const std::vector<std::vector<std::pair<std::uint64_t, TimeRange>>> &traces() const noexcept
{
return _traces;
}
void clear()
{
_traces.clear();
_traces.reserve(1U << 5U);
_traces.emplace_back(std::vector<std::pair<std::uint64_t, TimeRange>>{});
_traces.back().reserve(SIZE);
}
private:
std::vector<std::vector<std::pair<std::uint64_t, TimeRange>>> _traces;
};
class TaskTraces
{
public:
TaskTraces(const std::chrono::nanoseconds start_timestamp,
const std::unordered_map<std::uint64_t, std::string> &names,
std::vector<std::vector<std::pair<std::uint64_t, NormalizedTimeRange>>> &&traces) noexcept
: _start_timestamp(start_timestamp), _names(names), _traces(std::move(traces))
{
}
TaskTraces() = default;
TaskTraces(TaskTraces &&) noexcept = default;
~TaskTraces() = default;
[[nodiscard]] const std::unordered_map<std::uint64_t, std::string> &names() const noexcept { return _names; }
[[nodiscard]] const std::vector<std::vector<std::pair<std::uint64_t, NormalizedTimeRange>>> &traces() const noexcept
{
return _traces;
}
[[nodiscard]] nlohmann::json to_json() const;
private:
std::chrono::nanoseconds _start_timestamp;
std::unordered_map<std::uint64_t, std::string> _names;
std::vector<std::vector<std::pair<std::uint64_t, NormalizedTimeRange>>> _traces;
};
class TaskTracer
{
public:
TaskTracer(const std::uint16_t count_workers)
{
_worker_task_tracers.resize(count_workers);
_task_trace_ids.reserve(1024U);
}
TaskTracer(TaskTracer &&) noexcept = default;
~TaskTracer() = default;
void register_task(const std::uint64_t task_id, std::string &&name)
{
_task_trace_ids.insert_or_assign(task_id, std::move(name));
}
[[nodiscard]] std::optional<std::string> get(const std::uint64_t task_id) const noexcept
{
if (auto iterator = _task_trace_ids.find(task_id); iterator != _task_trace_ids.end())
{
return iterator->second;
}
return std::nullopt;
}
void emplace_back(const std::uint16_t worker_id, const std::uint64_t task_id, TimeRange &&time_range)
{
if (_is_enabled)
{
_worker_task_tracers[worker_id].emplace_back(task_id, std::move(time_range));
}
}
void start()
{
_start = std::chrono::system_clock::now();
for (auto &worker_tracer : _worker_task_tracers)
{
worker_tracer.clear();
}
_is_enabled = true;
}
[[nodiscard]] TaskTraces stop();
private:
bool _is_enabled{false};
std::chrono::system_clock::time_point _start;
std::vector<WorkerTaskTracer> _worker_task_tracers;
std::unordered_map<std::uint64_t, std::string> _task_trace_ids;
};
} // namespace mx::tasking::profiling

View File

@@ -0,0 +1,69 @@
#include "time.h"
using namespace mx::tasking::profiling;
WorkerIdleFrames IdleTimes::group(const std::chrono::nanoseconds frame_size) const noexcept
{
const auto count_frames = std::size_t(_duration / frame_size) + 1U;
auto idle_frames = std::vector<std::vector<std::chrono::nanoseconds>>{};
idle_frames.reserve(this->channels());
for (auto worker_id = 0U; worker_id < this->channels(); ++worker_id)
{
auto frames = std::vector<std::chrono::nanoseconds>{count_frames, std::chrono::nanoseconds{0U}};
for (const auto &time_range : this->_idle_ranges[worker_id])
{
const auto start_frame_id = time_range.start() / frame_size;
const auto end_frame_id = time_range.end() / frame_size;
if (start_frame_id == end_frame_id)
{
frames[start_frame_id] += time_range.duration();
}
else
{
/// Append to start.
const auto start_frame_end = (start_frame_id + 1U) * frame_size;
frames[start_frame_id] += start_frame_end - time_range.start();
/// Append to end.
const auto end_frame_start = end_frame_id * frame_size;
frames[end_frame_id] += (time_range.end() - end_frame_start) + std::chrono::nanoseconds(1U);
/// Fill frames between.
for (auto frame_id = start_frame_id + 1U; frame_id <= end_frame_id - 1U; ++frame_id)
{
frames[frame_id] += frame_size;
}
}
}
idle_frames.emplace_back(std::move(frames));
}
return WorkerIdleFrames{std::move(idle_frames), this->_duration, frame_size};
}
nlohmann::json WorkerIdleFrames::to_json() const noexcept
{
nlohmann::json idle_times;
idle_times["duration"] = this->_duration.count();
idle_times["frame-size"] = this->_frame_size.count();
idle_times["count-channels"] = this->channels();
idle_times["count-frames"] = this->_idle_frames.front().size();
nlohmann::json channels;
for (const auto &channel_frames : this->_idle_frames)
{
nlohmann::json channel;
for (const auto frame : channel_frames)
{
channel.emplace_back(frame.count());
}
channels.emplace_back(std::move(channel));
}
idle_times["channels"] = std::move(channels);
return idle_times;
}

View File

@@ -0,0 +1,110 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:

View File

@@ -0,0 +1,131 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <ealanos/util/json.hpp>
#include <vector>
namespace mx::tasking::profiling {
class NormalizedTimeRange
{
public:
constexpr NormalizedTimeRange(const std::chrono::nanoseconds start, const std::chrono::nanoseconds end) noexcept
: _start(start), _end(end)
{
}
NormalizedTimeRange(NormalizedTimeRange &&) noexcept = default;
~NormalizedTimeRange() noexcept = default;
[[nodiscard]] std::chrono::nanoseconds start() const noexcept { return _start; }
[[nodiscard]] std::chrono::nanoseconds end() const noexcept { return _end; }
[[nodiscard]] std::chrono::nanoseconds duration() const noexcept { return _end - _start; }
private:
std::chrono::nanoseconds _start;
std::chrono::nanoseconds _end;
};
/**
* Time range (from -- to) for idled time of a single channel.
*/
class TimeRange
{
public:
TimeRange() noexcept : _start(std::chrono::system_clock::now()) {}
constexpr explicit TimeRange(const std::chrono::system_clock::time_point start) noexcept : _start(start) {}
constexpr TimeRange(const std::chrono::system_clock::time_point start,
const std::chrono::system_clock::time_point end) noexcept
: _start(start), _end(end)
{
}
constexpr TimeRange(TimeRange &&) noexcept = default;
~TimeRange() = default;
/**
* Sets the end of the idle range to the current time.
*/
void stop() noexcept { _end = std::chrono::system_clock::now(); }
/**
* @return Number of nanoseconds idled.
*/
[[nodiscard]] std::uint64_t nanoseconds() const noexcept
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(_end - _start).count();
}
/**
* Normalizes this range with respect to a given point in time.
* @param global_start Point in time to normalize.
* @return Pair of (start, stop) normalized to the given time point.
*/
[[nodiscard]] NormalizedTimeRange normalize(const std::chrono::system_clock::time_point global_start) const noexcept
{
return NormalizedTimeRange{std::max(_start, global_start) - global_start, _end - global_start};
}
private:
// Start of idling.
std::chrono::system_clock::time_point _start;
// End of idling.
std::chrono::system_clock::time_point _end;
};
class WorkerIdleFrames
{
public:
WorkerIdleFrames(std::vector<std::vector<std::chrono::nanoseconds>> &&idle_frames,
const std::chrono::nanoseconds duration, const std::chrono::nanoseconds frame_size) noexcept
: _duration(duration), _frame_size(frame_size), _idle_frames(std::move(idle_frames))
{
}
WorkerIdleFrames(WorkerIdleFrames &&) noexcept = default;
~WorkerIdleFrames() noexcept = default;
[[nodiscard]] std::chrono::nanoseconds duration() const noexcept { return _duration; }
[[nodiscard]] std::chrono::nanoseconds frame_size() const noexcept { return _frame_size; }
[[nodiscard]] std::uint16_t channels() const noexcept { return _idle_frames.size(); }
[[nodiscard]] const std::vector<std::vector<std::chrono::nanoseconds>> &idle_frames() const noexcept
{
return _idle_frames;
}
[[nodiscard]] nlohmann::json to_json() const noexcept;
private:
const std::chrono::nanoseconds _duration;
const std::chrono::nanoseconds _frame_size;
std::vector<std::vector<std::chrono::nanoseconds>> _idle_frames;
};
class IdleTimes
{
public:
IdleTimes(std::vector<std::vector<NormalizedTimeRange>> &&idle_ranges,
const std::chrono::nanoseconds duration) noexcept
: _duration(duration), _idle_ranges(std::move(idle_ranges))
{
}
IdleTimes(IdleTimes &&) noexcept = default;
~IdleTimes() noexcept = default;
[[nodiscard]] std::chrono::nanoseconds duration() const noexcept { return _duration; }
[[nodiscard]] std::uint16_t channels() const noexcept { return _idle_ranges.size(); }
[[nodiscard]] const std::vector<std::vector<NormalizedTimeRange>> &idle_ranges() const noexcept
{
return _idle_ranges;
}
[[nodiscard]] WorkerIdleFrames group(std::chrono::nanoseconds frame_size) const noexcept;
private:
const std::chrono::nanoseconds _duration;
std::vector<std::vector<NormalizedTimeRange>> _idle_ranges;
};
} // namespace mx::tasking::profiling

View File

@@ -0,0 +1,567 @@
#pragma once
#include "ealanos/memory/hamstraaja.h"
#include "prefetch_distance.h"
#include "scheduler.h"
#include "task.h"
#include "task_squad.h"
#include <iostream>
#include <memory>
#include <mx/io/network/server.h>
#include <mx/memory/fixed_size_allocator.h>
#include <mx/memory/task_allocator_interface.h>
#include <mx/memory/worker_local_dynamic_size_allocator.h>
#include <mx/resource/annotation.h>
#include <mx/resource/builder.h>
#include <mx/system/environment.h>
#include <mx/system/thread.h>
#include <mx/util/core_set.h>
#include <mx/util/logger.h>
#include <utility>
#include <libc/component.h>
namespace mx::tasking {
/**
* The runtime is the central access structure to MxTasking.
* Here, we can initialize MxTasking, spawn and allocate tasks, allocate
* data objects.
*/
class runtime
{
public:
/**
* Initializes the MxTasking runtime.
*
* @param core_set Cores, where the runtime should execute on.
* @param prefetch_distance Distance for prefetching.
* @param channels_per_core Number of channels per core (more than one enables channel-stealing).
* @param use_system_allocator Should we use the systems malloc interface or our allocator?
* @return True, when the runtime was started successfully.
*/
static bool init(const util::core_set &core_set, const PrefetchDistance prefetch_distance,
const bool use_system_allocator, Libc::Env& env)
{
util::Logger::info_if(system::Environment::is_debug(), "Starting MxTasking in DEBUG mode.");
util::Logger::warn_if(system::Environment::is_debug() == false && config::is_use_task_counter(),
"Task statistics will be collected in RELEASE build.");
util::Logger::warn_if(system::Environment::is_debug() == false && config::is_collect_task_traces(),
"Task traces will be collected in RELEASE build.");
util::Logger::warn_if(system::Environment::is_debug() == false &&
config::worker_mode() == config::worker_mode::PowerSave,
"Power safe mode activated in RELEASE build.");
if (!memory::GlobalHeap::initialized()) {
memory::GlobalHeap::init(new Ealan::Memory::Hamstraaja<memory::config::min_block_size(), memory::config::superblock_cutoff()>(env.pd(), env.rm()));
}
// Are we ready to re-initialize the scheduler?
if (_scheduler != nullptr && _scheduler->is_running())
{
return false;
}
/// The starting thread operates as worker#0.
_worker_id = 0U;
// Create a new resource allocator.
if (_resource_allocator == nullptr)
{
/// Only called the first time.
_resource_allocator.reset(new (memory::GlobalHeap::allocate_cache_line_aligned(
sizeof(memory::dynamic::local::Allocator))) memory::dynamic::local::Allocator(core_set));
}
else if (_resource_allocator->is_free())
{
/// The application cleared the whole memory, but
/// we won't re-allocate the allocator to keep references
/// that are given to the resource builder and epoch manager
/// since the scheduler does not change.
_resource_allocator->reset(core_set, true);
}
else
{
/// The application holds allocated memory.
_resource_allocator->reset(core_set, false);
}
// Create a new task allocator.
if (use_system_allocator)
{
_task_allocator.reset(new (memory::GlobalHeap::allocate_cache_line_aligned(sizeof(
memory::SystemTaskAllocator<config::task_size()>))) memory::SystemTaskAllocator<config::task_size()>());
}
else
{
_task_allocator.reset(new (
memory::GlobalHeap::allocate_cache_line_aligned(sizeof(memory::fixed::Allocator<config::task_size()>)))
memory::fixed::Allocator<config::task_size()>(util::core_set::build()));
}
// Create a new scheduler.
const auto need_new_scheduler = _scheduler == nullptr || *_scheduler != core_set;
if (need_new_scheduler)
{
_scheduler.reset(new (memory::GlobalHeap::allocate_cache_line_aligned(sizeof(Scheduler)))
Scheduler(core_set, prefetch_distance, *_resource_allocator));
}
else
{
_scheduler->reset();
}
// Create a new resource builder.
if (_resource_builder == nullptr || need_new_scheduler)
{
_resource_builder = std::make_unique<mx::resource::Builder>(*_scheduler, *_resource_allocator);
}
return true;
}
/**
* Spawns the given task.
*
* @param task Task to be scheduled.
* @param local_worker_id Worker, the spawn request came from.
*/
static std::uint16_t spawn(TaskInterface &task, const std::uint16_t local_worker_id) noexcept
{
return _scheduler->dispatch(task, local_worker_id);
}
/**
* Spawns the given task.
*
* @param task Task to be scheduled.
*/
static void spawn(TaskInterface &task) noexcept { _scheduler->dispatch(task, _worker_id); }
/**
* Spawns a list of concatenated tasks.
*
* @param first_task First task of the list to be scheduled.
* @param last_task Last task of the list to be scheduled.
* @param local_worker_id Worker, the spawn request came from.
*/
static void spawn(TaskInterface &first_task, TaskInterface &last_task, const std::uint16_t local_worker_id) noexcept
{
_scheduler->dispatch(first_task, last_task, local_worker_id);
}
/**
* Spawns the given squad.
*
* @param squad Squad to be scheduled.
* @param local_worker_id Worker, the spawn request came from.
*/
static std::uint16_t spawn(const mx::resource::ptr squad, const std::uint16_t local_worker_id) noexcept
{
return spawn(squad, annotation::resource_boundness::mixed, local_worker_id);
}
/**
* Spawns the given squad.
*
* @param squad Squad to be scheduled.
* @param boundness Boundness of the squad.
* @param local_worker_id Worker, the spawn request came from.
*/
static std::uint16_t spawn(const mx::resource::ptr squad, const enum annotation::resource_boundness boundness,
const std::uint16_t local_worker_id) noexcept
{
return _scheduler->dispatch(squad, boundness, local_worker_id);
}
/**
* @return Number of available cores.
*/
[[nodiscard]] static std::uint16_t workers() noexcept { return _scheduler->count_cores(); }
/**
* @return Prefetch distance.
*/
static PrefetchDistance prefetch_distance() noexcept { return _scheduler->prefetch_distance(); }
/**
* Starts the runtime and suspends the starting thread until MxTasking is stopped.
*/
static void start_and_wait() { _scheduler->start_and_wait(); }
/**
* Instructs all worker threads to stop their work.
* After all worker threads are stopped, the starting
* thread will be resumed.
*
* @param stop_network If set to true, the network server will also be stopped.
*/
static void stop(const bool stop_network = true) noexcept
{
_scheduler->interrupt();
if (_network_server != nullptr && stop_network)
{
_network_server->stop();
_network_server_thread->join();
}
}
/**
* Creates a new task.
*
* @param worker_id Worker to allocate memory from.
* @param arguments Arguments for the task.
* @return The new task.
*/
template <typename T, typename... Args> static T *new_task(const std::uint16_t worker_id, Args &&...arguments)
{
static_assert(sizeof(T) <= config::task_size() && "Task must be <= defined task size.");
return new (_task_allocator->allocate(worker_id)) T(std::forward<Args>(arguments)...);
}
/**
* Frees a given task.
*
* @param worker_id Worker id to return the memory to.
* @param task Task to be freed.
*/
template <typename T> static void delete_task(const std::uint16_t worker_id, T *task) noexcept
{
task->~T();
_task_allocator->free(worker_id, static_cast<void *>(task));
}
/**
* Creates a resource.
*
* @param size Size of the data object.
* @param annotation Annotation for allocation and scheduling.
* @param arguments Arguments for the data object.
* @return The resource pointer.
*/
template <typename T, typename... Args>
static mx::resource::ptr new_resource(const std::size_t size, mx::resource::annotation &&annotation,
Args &&...arguments) noexcept
{
return _resource_builder->build<T>(_worker_id, size, std::move(annotation), std::forward<Args>(arguments)...);
}
/**
* Creates a resource from a given pointer.
*
* @param object Pointer to the existing object.
* @param annotation Annotation for allocation and scheduling.
* @return The resource pointer.
*/
template <typename T>
static mx::resource::ptr to_resource(T *object, mx::resource::annotation &&annotation) noexcept
{
return _resource_builder->build<T>(object, annotation);
}
/**
* Deletes the given data object.
*
* @param resource Data object to be deleted.
*/
template <typename T> static void delete_resource(const mx::resource::ptr resource) noexcept
{
_resource_builder->destroy<T>(_worker_id, resource);
}
/**
* Creates a new task squad.
*
* @param size Size of the task squad object.
* @param worker_id Worker to map the squad to.
* @param arguments Arguments for the task squad object.
* @return A resource pointer to the squad.
*/
template <typename T, typename... Args>
static mx::resource::ptr new_squad(const std::size_t size, const std::uint16_t worker_id,
Args &&...arguments) noexcept
{
static_assert(std::is_base_of<TaskSquad, T>::value && "Squads needs to extend mx::tasking::TaskSquad");
auto annotation = resource::annotation{worker_id, synchronization::isolation_level::Exclusive,
synchronization::protocol::Batched};
return _resource_builder->build<T>(_worker_id, size, std::move(annotation), std::forward<Args>(arguments)...);
}
/**
* Flushes the given task squad.
*/
static void flush_squad(resource::ptr task_squad) noexcept { task_squad.get<TaskSquad>()->flush(); }
/**
* Creates a simple task squad.
*
* @param worker_id Worker to map the squad to.
* @return A resource pointer to the squad.
*/
static mx::resource::ptr new_squad(const std::uint16_t worker_id) noexcept
{
auto annotation = resource::annotation{worker_id, synchronization::isolation_level::Exclusive,
synchronization::protocol::Batched};
return _resource_builder->build<TaskSquad>(_worker_id, sizeof(TaskSquad), std::move(annotation));
}
/**
* Deletes the given data squad.
*
* @param resource Squad to be deleted.
*/
template <typename T> static void delete_squad(const mx::resource::ptr resource) noexcept
{
_resource_builder->destroy<T>(_worker_id, resource);
}
/**
* Allocates memory from the worker-local heap.
*
* @param numa_node_id NUMA node id where to allocate memory from.
* @param alignment Alighment of the allocation.
* @param size Size to allocate.
* @return Pointer to the allocated memory.
*/
static void *allocate(const std::uint8_t numa_node_id, const std::size_t alignment, const std::size_t size) noexcept
{
return _resource_allocator->allocate(_worker_id, numa_node_id, alignment, size);
}
/**
* Frees a region allocated from the worker-local heap.
*
* @param pointer Pointer to memory.
*/
static void free(void *pointer) noexcept { _resource_allocator->free(_worker_id, pointer); }
/**
* Spawns a task for every worker thread to release the free memory.
*/
static void defragment()
{
const auto local_worker_id = _worker_id;
for (auto worker_id = std::uint16_t(0U); worker_id < workers(); ++worker_id)
{
auto *clean_up_task =
new_task<memory::dynamic::local::CleanUpMemoryTask>(local_worker_id, *_resource_allocator);
clean_up_task->annotate(worker_id);
spawn(*clean_up_task, local_worker_id);
}
}
/**
* Updates the prediction of a data object.
*
* @param resource Data object, whose usage should be predicted.
* @param old_prediction Prediction so far.
* @param new_prediction New usage prediction.
*/
static void modify_predicted_usage(const mx::resource::ptr resource,
const mx::resource::expected_access_frequency old_prediction,
const mx::resource::expected_access_frequency new_prediction) noexcept
{
_scheduler->modify_predicted_usage(resource.worker_id(), old_prediction, new_prediction);
}
/**
* ID of the NUMA region of a worker.
* @param worker_id Worker.
* @return ID of the NUMA region.
*/
static std::uint8_t numa_node_id(const std::uint16_t worker_id) noexcept
{
return _scheduler->numa_node_id(worker_id);
}
/**
* Start profiling of idle times.
*/
static void start_idle_profiler() noexcept { _scheduler->start_idle_profiler(); }
/**
* Stops idle profiling.
*
* @return List of idle times for every channel.
*/
[[nodiscard]] static profiling::IdleTimes stop_idle_profiler() { return _scheduler->stop_idle_profiler(); }
/**
* Reads the task statistics for a given counter and all channels.
* @return List of all counters for every channel.
*/
static std::unordered_map<profiling::TaskCounter::Counter, profiling::WorkerTaskCounter> task_counter() noexcept
{
if constexpr (config::is_use_task_counter())
{
return _scheduler->task_counter()->get();
}
else
{
return std::unordered_map<profiling::TaskCounter::Counter, profiling::WorkerTaskCounter>{};
}
}
/**
* Reads the task statistics for a given counter and all channels.
* @param counter Counter to be read.
* @return Aggregated value of all channels.
*/
static profiling::WorkerTaskCounter task_counter(
[[maybe_unused]] const profiling::TaskCounter::Counter counter) noexcept
{
if constexpr (config::is_use_task_counter())
{
return _scheduler->task_counter()->get(counter);
}
else
{
return profiling::WorkerTaskCounter{_scheduler->core_set().count_cores()};
}
}
/**
* Reads the task task_counter for a given counter on a given channel.
* @param counter Counter to be read.
* @param worker_id Worker.
* @return Value of the counter of the given channel.
*/
static std::uint64_t task_counter([[maybe_unused]] const profiling::TaskCounter::Counter counter,
[[maybe_unused]] const std::uint16_t worker_id) noexcept
{
if constexpr (config::is_use_task_counter())
{
return _scheduler->task_counter()->get(counter, worker_id);
}
else
{
return 0U;
}
}
static void register_task_for_trace([[maybe_unused]] const std::uint64_t task_id,
[[maybe_unused]] std::string &&name)
{
_scheduler->task_tracer()->register_task(task_id, std::move(name));
}
static std::string task_name([[maybe_unused]] const std::uint64_t task_id)
{
auto name = _scheduler->task_tracer()->get(task_id);
return name.value_or(std::to_string(task_id));
}
static void start_tracing()
{
if constexpr (config::is_collect_task_traces())
{
_scheduler->task_tracer()->start();
}
}
[[nodiscard]] static profiling::TaskTraces stop_tracing()
{
if constexpr (config::is_collect_task_traces())
{
return _scheduler->task_tracer()->stop();
}
else
{
return profiling::TaskTraces{};
}
}
static void listen_on_port(std::unique_ptr<io::network::MessageHandler> &&message_handler, const std::uint16_t port)
{
_network_server =
std::make_unique<io::network::Server>(std::move(message_handler), port, _scheduler->count_cores());
_network_server_thread = std::make_unique<std::thread>([] { mx::tasking::runtime::_network_server->listen(); });
}
static void send_message(const std::uint32_t client_id, std::string &&message)
{
_network_server->send(client_id, std::move(message));
}
static bool is_listening()
{
if (_network_server == nullptr)
{
return false;
}
return _network_server->is_running();
}
static void initialize_worker(const std::uint16_t worker_id)
{
_worker_id = worker_id;
_resource_allocator->initialize_heap(worker_id, _scheduler->count_numa_nodes());
}
/**
* @return The id of the executing worker. May be costly, use it carefully.
*/
[[nodiscard]] static std::uint16_t worker_id() noexcept { return _worker_id; }
[[nodiscard]] static std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>
memory_tags()
{
auto tags = _scheduler->memory_tags();
for (auto &[name, ranges] : _task_allocator->allocated_chunks())
{
if (auto iterator = tags.find(name); iterator != tags.end())
{
std::move(ranges.begin(), ranges.end(), std::back_inserter(iterator->second));
}
else
{
tags.insert(std::make_pair(std::move(name), std::move(ranges)));
}
}
return tags;
}
private:
inline static thread_local std::uint16_t _worker_id{std::numeric_limits<std::uint16_t>::max()};
// Scheduler to spawn tasks.
inline static std::unique_ptr<Scheduler> _scheduler{nullptr};
// Allocator to allocate tasks (could be systems malloc or our Multi-level allocator).
inline static std::unique_ptr<memory::TaskAllocatorInterface> _task_allocator{nullptr};
// Allocator to allocate resources.
inline static std::unique_ptr<memory::dynamic::local::Allocator> _resource_allocator{nullptr};
// Allocator to allocate data objects.
inline static std::unique_ptr<mx::resource::Builder> _resource_builder{nullptr};
// Optional network server.
inline static std::unique_ptr<io::network::Server> _network_server{nullptr};
inline static std::unique_ptr<std::thread> _network_server_thread{nullptr};
};
/**
* The runtime_guard initializes the runtime at initialization and starts
* the runtime when the object is deleted. This allows MxTasking to execute
* within a specific scope.
*/
class runtime_guard
{
public:
runtime_guard(Libc::Env &env, const bool use_system_allocator, const util::core_set &core_set,
const PrefetchDistance prefetch_distance = PrefetchDistance{0U}) noexcept
{
runtime::init(core_set, prefetch_distance, use_system_allocator, env);
}
runtime_guard(Libc::Env &env, const util::core_set &core_set,
const PrefetchDistance prefetch_distance = PrefetchDistance{0U}) noexcept
: runtime_guard(env, false, core_set, prefetch_distance)
{
}
~runtime_guard() noexcept { runtime::start_and_wait(); }
};
} // namespace mx::tasking

View File

@@ -0,0 +1,308 @@
#include "scheduler.h"
#include "runtime.h"
#include <mx/memory/global_heap.h>
#include <mx/synchronization/synchronization.h>
#include <mx/system/cpu.h>
#include <mx/system/thread.h>
#include <thread>
#include <vector>
using namespace mx::tasking;
Scheduler::Scheduler(const mx::util::core_set &core_set, const PrefetchDistance prefetch_distance,
memory::dynamic::local::Allocator &resource_allocator) noexcept
: _core_set(core_set), _prefetch_distance(prefetch_distance), _worker({nullptr}),
_epoch_manager(core_set.count_cores(), resource_allocator, _is_running)
{
this->_worker_numa_node_map.fill(0U);
/// Set up profiling utilities.
if constexpr (config::is_use_task_counter())
{
this->_task_counter.emplace(profiling::TaskCounter{this->_core_set.count_cores()});
}
if constexpr (config::is_collect_task_traces() || config::is_monitor_task_cycles_for_prefetching())
{
this->_task_tracer.emplace(profiling::TaskTracer{this->_core_set.count_cores()});
}
/// Create worker.
for (auto worker_id = std::uint16_t(0U); worker_id < this->_core_set.count_cores(); ++worker_id)
{
/// The core the worker is binded to.
const auto core_id = this->_core_set[worker_id];
/// The corresponding NUMA Node.
const auto numa_node_id = system::cpu::node_id(core_id);
this->_worker_numa_node_map[worker_id] = numa_node_id;
this->_worker[worker_id] = new (memory::GlobalHeap::allocate(numa_node_id, sizeof(Worker)))
Worker(this->_core_set.count_cores(), worker_id, core_id, this->_is_running, prefetch_distance,
this->_epoch_manager[worker_id], this->_epoch_manager.global_epoch(), this->_task_counter,
this->_task_tracer);
}
}
Scheduler::~Scheduler() noexcept
{
std::for_each(this->_worker.begin(), this->_worker.begin() + this->_core_set.count_cores(), [](auto *worker) {
const auto numa_node_id = system::cpu::node_id(worker->core_id());
worker->~Worker();
memory::GlobalHeap::free(worker, sizeof(Worker), numa_node_id);
});
}
void Scheduler::start_and_wait()
{
// Create threads for worker...
std::vector<std::thread> worker_threads(this->_core_set.count_cores() +
static_cast<std::uint16_t>(config::memory_reclamation() != config::None));
for (auto worker_id = 0U; worker_id < this->_core_set.count_cores(); ++worker_id)
{
auto *worker = this->_worker[worker_id];
worker_threads[worker_id] = std::thread([worker] { worker->execute(); });
//system::thread::pin(worker_threads[worker_id], worker->core_id());
//system::thread::name(worker_threads[worker_id], "mx::worker#" + std::to_string(worker_id));
}
// ... and epoch management (if enabled).
if constexpr (config::memory_reclamation() != config::None)
{
const auto memory_reclamation_thread_id = this->_core_set.count_cores();
// In case we enable memory reclamation: Use an additional thread.
worker_threads[memory_reclamation_thread_id] =
std::thread([this] { this->_epoch_manager.enter_epoch_periodically(); });
// Set name.
//system::thread::name(worker_threads[memory_reclamation_thread_id], "mx::mem_reclam");
}
// Turning the flag on starts all worker threads to execute tasks.
this->_is_running = true;
// Wait for the worker threads to end. This will only
// reached when the _is_running flag is set to false
// from somewhere in the application.
for (auto &worker_thread : worker_threads)
{
worker_thread.join();
}
if constexpr (config::memory_reclamation() != config::None)
{
// At this point, no task will execute on any resource;
// but the epoch manager has joined, too. Therefore,
// we will reclaim all memory manually.
this->_epoch_manager.reclaim_all();
}
}
std::uint16_t Scheduler::dispatch(TaskInterface &task, const std::uint16_t local_worker_id) noexcept
{
/// The "local_channel_id" (the id of the calling channel) may be "invalid" (=uint16_t::max).
/// If it is not, we set the worker_id of the worker_id either by contacting the map (virtualization on)
/// or just using the worker_id as worker_id (virtualization off).
const auto has_local_worker_id = local_worker_id != std::numeric_limits<std::uint16_t>::max();
if constexpr (config::is_use_task_counter())
{
if (has_local_worker_id) [[likely]]
{
this->_task_counter->increment<profiling::TaskCounter::Dispatched>(local_worker_id);
}
}
const auto &annotation = task.annotation();
// Scheduling is based on the annotated resource of the given task.
if (annotation.has_resource())
{
const auto annotated_resource = annotation.resource();
auto resource_worker_id = annotated_resource.worker_id();
if (annotated_resource.synchronization_primitive() == synchronization::primitive::Batched)
{
// if (resource_worker_id == local_worker_id)
// {
// annotated_resource.get<TaskSquad>()->push_back_local(task);
// return local_worker_id;
// }
annotated_resource.get<TaskSquad>()->push_back_remote(task);
return resource_worker_id;
}
// For performance reasons, we prefer the local (not synchronized) queue
// whenever possible to spawn the task. The decision is based on the
// synchronization primitive and the access mode of the task (reader/writer).
if (has_local_worker_id &&
Scheduler::keep_task_local(annotation.is_readonly(), annotated_resource.synchronization_primitive()))
{
this->_worker[local_worker_id]->queues().push_back_local(&task);
if constexpr (config::is_use_task_counter())
{
this->_task_counter->increment<profiling::TaskCounter::DispatchedLocally>(local_worker_id);
}
return resource_worker_id;
}
if (has_local_worker_id) [[likely]]
{
this->_worker[resource_worker_id]->queues().push_back_remote(&task, this->numa_node_id(local_worker_id),
local_worker_id);
}
else
{
this->_worker[resource_worker_id]->queues().push_back_remote(&task, system::cpu::node_id(),
runtime::worker_id());
}
if constexpr (config::is_use_task_counter())
{
if (has_local_worker_id) [[likely]]
{
this->_task_counter->increment<profiling::TaskCounter::DispatchedRemotely>(local_worker_id);
}
}
return resource_worker_id;
}
// The developer assigned a fixed channel to the task.
if (annotation.has_worker_id())
{
const auto target_worker_id = annotation.worker_id();
if (has_local_worker_id)
{
// For performance reasons, we prefer the local (not synchronized) queue
// whenever possible to spawn the task.
if (local_worker_id == target_worker_id)
{
this->_worker[target_worker_id]->queues().push_back_local(&task);
if constexpr (config::is_use_task_counter())
{
this->_task_counter->increment<profiling::TaskCounter::DispatchedLocally>(target_worker_id);
}
return target_worker_id;
}
this->_worker[target_worker_id]->queues().push_back_remote(&task, this->numa_node_id(local_worker_id),
local_worker_id);
}
else
{
this->_worker[target_worker_id]->queues().push_back_remote(&task, system::cpu::node_id(),
runtime::worker_id());
}
if constexpr (config::is_use_task_counter())
{
if (has_local_worker_id) [[likely]]
{
this->_task_counter->increment<profiling::TaskCounter::DispatchedRemotely>(local_worker_id);
}
}
return target_worker_id;
}
// The developer assigned a fixed NUMA region to the task.
// if (annotation.has_numa_node_id())
// {
// this->_numa_node_queues[annotation.numa_node_id()].get(annotation.priority()).push_back(&task);
// if constexpr (config::is_use_task_counter())
// {
// if (current_channel_id != std::numeric_limits<std::uint16_t>::max()) [[likely]]
// {
// this->_task_counter->increment<profiling::TaskCounter::DispatchedRemotely>(current_channel_id);
// }
// }
//
// /// TODO: What to return?
// return 0U;
// }
// The task should be spawned on the local channel.
if (annotation.is_locally())
{
if (has_local_worker_id) [[likely]]
{
this->_worker[local_worker_id]->queues().push_back_local(&task);
if constexpr (config::is_use_task_counter())
{
this->_task_counter->increment<profiling::TaskCounter::DispatchedLocally>(local_worker_id);
}
return local_worker_id;
}
assert(false && "Spawn was expected to be 'locally' but no local channel was provided.");
}
// The task can run everywhere.
// this->_global_queue.get(annotation.priority()).push_back(&task);
// if constexpr (config::is_use_task_counter())
// {
// if (current_channel_id != std::numeric_limits<std::uint16_t>::max()) [[likely]]
// {
// this->_task_counter->increment<profiling::TaskCounter::DispatchedRemotely>(current_channel_id);
// }
// }
/// TODO: What to return?
return 0U;
}
std::uint16_t Scheduler::dispatch(TaskInterface &first, TaskInterface &last, const uint16_t local_worker_id) noexcept
{
this->_worker[local_worker_id]->queues().push_back_local(&first, &last);
return local_worker_id;
}
std::uint16_t Scheduler::dispatch(const mx::resource::ptr squad, const enum annotation::resource_boundness boundness,
const std::uint16_t local_worker_id) noexcept
{
auto *dispatch_task = runtime::new_task<TaskSquadSpawnTask>(local_worker_id, *squad.get<TaskSquad>());
dispatch_task->annotate(squad.worker_id());
return this->dispatch(*dispatch_task, local_worker_id);
}
void Scheduler::reset() noexcept
{
if constexpr (config::is_use_task_counter())
{
this->_task_counter->clear();
}
this->_epoch_manager.reset();
}
void Scheduler::start_idle_profiler()
{
// TODO: Should we measure idle times?
// this->_idle_profiler.start();
// for (auto worker_id = 0U; worker_id < this->_core_set.count_cores(); ++worker_id)
// {
// this->_idle_profiler.start(this->_worker[worker_id]->channel());
// }
}
std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>> Scheduler::memory_tags()
{
auto tags = std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>>{};
auto workers = std::vector<std::pair<std::uintptr_t, std::uintptr_t>>{};
workers.reserve(this->_core_set.count_cores());
for (auto worker_id = 0U; worker_id < this->_core_set.count_cores(); ++worker_id)
{
const auto begin = std::uintptr_t(this->_worker[worker_id]);
const auto end = begin + sizeof(Worker);
workers.emplace_back(std::make_pair(begin, end));
}
tags.insert(std::make_pair("worker", std::move(workers)));
return tags;
}

View File

@@ -0,0 +1,641 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h \
/home/mml/genode-igb/repos/libports/include/libc/component.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h:
/home/mml/genode-igb/repos/libports/include/libc/component.h:

View File

@@ -0,0 +1,282 @@
#pragma once
#include "prefetch_distance.h"
#include "shared_task_queue.h"
#include "task.h"
#include "task_squad.h"
#include "worker.h"
#include <array>
#include <atomic>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <memory>
#include <mx/memory/config.h>
#include <mx/memory/reclamation/epoch_manager.h>
#include <mx/memory/worker_local_dynamic_size_allocator.h>
#include <mx/resource/ptr.h>
#include <mx/tasking/profiling/idle_profiler.h>
#include <mx/tasking/profiling/task_counter.h>
#include <mx/tasking/profiling/task_tracer.h>
#include <mx/util/core_set.h>
#include <mx/util/random.h>
#include <optional>
#include <string>
namespace mx::tasking {
/**
* The scheduler is the central (but hidden by the runtime) data structure to spawn
* tasks between worker threads.
*/
class Scheduler
{
public:
Scheduler(const util::core_set &core_set, PrefetchDistance prefetch_distance,
memory::dynamic::local::Allocator &resource_allocator) noexcept;
~Scheduler() noexcept;
/**
* Schedules a given task.
*
* @param task Task to be scheduled.
* @param local_worker_id Worker, the request came from.
* @return Worker ID where the task was dispatched to.
*/
std::uint16_t dispatch(TaskInterface &task, std::uint16_t local_worker_id) noexcept;
/**
* Schedules a given list of tasks to the local worker.
* The tasks have to be concatenated.
*
* @param first First task of the list.
* @param last Last task of the list.
* @param local_worker_id Worker, the request came from.
* @return Worker ID where the task was dispatched to.
*/
std::uint16_t dispatch(TaskInterface &first, TaskInterface &last, std::uint16_t local_worker_id) noexcept;
/**
* Schedules all tasks of a given squad.
* @param squad Squad to be scheduled.
* @param local_worker_id Channel, the request came from.
* @return Worker ID where the task was dispatched to.
*/
std::uint16_t dispatch(mx::resource::ptr squad, enum annotation::resource_boundness boundness,
std::uint16_t local_worker_id) noexcept;
/**
* Starts all worker threads and waits until they finish.
*/
void start_and_wait();
/**
* Interrupts the worker threads. They will finish after executing
* their current tasks.
*/
void interrupt() noexcept
{
_is_running = false;
if (this->_idle_profiler.is_running())
{
this->_idle_profiler.stop();
}
}
/**
* @return Core set of this instance.
*/
[[nodiscard]] const util::core_set &core_set() const noexcept { return _core_set; }
/**
* @return True, when the worker threads are not interrupted.
*/
[[nodiscard]] bool is_running() const noexcept { return _is_running; }
/**
* @return The global epoch manager.
*/
[[nodiscard]] memory::reclamation::EpochManager &epoch_manager() noexcept { return _epoch_manager; }
/**
* @return Number of all cores.
*/
[[nodiscard]] std::uint16_t count_cores() const noexcept { return _core_set.count_cores(); }
/**
* @return Number of all numa regions.
*/
[[nodiscard]] std::uint8_t count_numa_nodes() const noexcept { return _core_set.numa_nodes(); }
/**
* @return Prefetch distance.
*/
[[nodiscard]] PrefetchDistance prefetch_distance() const noexcept { return _prefetch_distance; }
/**
* Reads the NUMA region of a given worker thread.
* @param worker_id Worker.
* @return NUMA region of the given worker.
*/
[[nodiscard]] std::uint8_t numa_node_id(const std::uint16_t worker_id) const noexcept
{
return _worker_numa_node_map[worker_id];
}
/**
* Predicts usage for a given channel.
* @param worker_id Worker.
* @param usage Usage to predict.
*/
void predict_usage(const std::uint16_t worker_id, const mx::resource::expected_access_frequency usage) noexcept
{
_worker[worker_id]->occupancy().predict(usage);
}
/**
* Updates the predicted usage of a channel.
* @param worker_id Worker.
* @param old_prediction So far predicted usage.
* @param new_prediction New prediction.
*/
void modify_predicted_usage(const std::uint16_t worker_id,
const mx::resource::expected_access_frequency old_prediction,
const mx::resource::expected_access_frequency new_prediction) noexcept
{
_worker[worker_id]->occupancy().revoke(old_prediction);
_worker[worker_id]->occupancy().predict(new_prediction);
}
/**
* @param worker_id Worker.
* @return True, when a least one usage was predicted to be "excessive" for the given channel.
*/
[[nodiscard]] bool has_excessive_usage_prediction(const std::uint16_t worker_id) const noexcept
{
return _worker[worker_id]->occupancy().has_excessive_usage_prediction();
}
/**
* Resets the statistics.
*/
void reset() noexcept;
/**
* Starts profiling of idle times.
*/
void start_idle_profiler();
/**
* Stops idle profiling.
* @return List of idle times for each channel.
*/
[[nodiscard]] profiling::IdleTimes stop_idle_profiler() { return this->_idle_profiler.stop(); }
/**
* @return Statistic.
*/
[[nodiscard]] std::optional<profiling::TaskCounter> &task_counter() noexcept { return _task_counter; }
/**
* @return Task tracer.
*/
[[nodiscard]] std::optional<profiling::TaskTracer> &task_tracer() noexcept { return _task_tracer; }
[[nodiscard]] std::unordered_map<std::string, std::vector<std::pair<std::uintptr_t, std::uintptr_t>>> memory_tags();
bool operator==(const util::core_set &cores) const noexcept { return _core_set == cores; }
bool operator!=(const util::core_set &cores) const noexcept { return _core_set != cores; }
private:
class PhysicalCoreResourceWorkerIds
{
public:
PhysicalCoreResourceWorkerIds() noexcept
: _worker_ids{std::numeric_limits<std::uint16_t>::max(), std::numeric_limits<std::uint16_t>::max(),
std::numeric_limits<std::uint16_t>::max()}
{
}
explicit PhysicalCoreResourceWorkerIds(const std::uint16_t worker_id) noexcept
: _worker_ids{worker_id, worker_id, worker_id}
{
}
PhysicalCoreResourceWorkerIds(const std::uint16_t memory_bound_worker_id,
const std::uint16_t compute_bound_worker_id,
const std::uint16_t mixed_worker_id) noexcept
: _worker_ids{memory_bound_worker_id, compute_bound_worker_id, mixed_worker_id}
{
}
~PhysicalCoreResourceWorkerIds() noexcept = default;
PhysicalCoreResourceWorkerIds &operator=(const PhysicalCoreResourceWorkerIds &) noexcept = default;
PhysicalCoreResourceWorkerIds &operator=(PhysicalCoreResourceWorkerIds &&) noexcept = default;
[[nodiscard]] std::uint16_t operator[](const enum annotation::resource_boundness boundness) const noexcept
{
return _worker_ids[boundness];
}
[[nodiscard]] std::uint16_t &operator[](const enum annotation::resource_boundness boundness) noexcept
{
return _worker_ids[boundness];
}
operator bool()
{
return _worker_ids[0U] != std::numeric_limits<std::uint16_t>::max() &&
_worker_ids[1U] != std::numeric_limits<std::uint16_t>::max() &&
_worker_ids[2U] != std::numeric_limits<std::uint16_t>::max();
}
private:
std::array<std::uint16_t, 3U> _worker_ids;
};
// Cores to run the worker threads on.
const util::core_set _core_set;
// Number of tasks a resource will be prefetched in front of.
const PrefetchDistance _prefetch_distance;
// All initialized workers.
std::array<Worker *, config::max_cores()> _worker{nullptr};
// Map of worker id to NUMA region id.
std::array<std::uint8_t, config::max_cores()> _worker_numa_node_map{0U};
// Flag for the worker threads. If false, the worker threads will stop.
// This is atomic for hardware that does not guarantee atomic reads/writes of booleans.
alignas(64) util::maybe_atomic<bool> _is_running{false};
// Epoch manager for memory reclamation,
alignas(64) memory::reclamation::EpochManager _epoch_manager;
// Profiler for task statistics.
alignas(64) std::optional<profiling::TaskCounter> _task_counter{std::nullopt};
// Profiler for idle times.
alignas(64) profiling::IdleProfiler _idle_profiler;
// Recorder for tracing task run times.
alignas(64) std::optional<profiling::TaskTracer> _task_tracer{std::nullopt};
/**
* Make a decision whether a task should be scheduled to the local
* channel or a remote.
*
* @param is_readonly Access mode of the task.
* @param primitive The synchronization primitive of the task annotated resource.
* @param resource_worker_id Worker id of the task annotated resource.
* @param current_worker_id Worker id where the spawn() operation is called.
* @return True, if the task should be scheduled local.
*/
[[nodiscard]] static inline bool keep_task_local(const bool is_readonly, const synchronization::primitive primitive)
{
return (is_readonly && primitive != synchronization::primitive::ScheduleAll) ||
(primitive != synchronization::primitive::None && primitive != synchronization::primitive::ScheduleAll &&
primitive != synchronization::primitive::ScheduleWriter);
}
};
} // namespace mx::tasking

View File

@@ -0,0 +1,32 @@
#pragma once
#include "task.h"
#include <cstdint>
#include <mx/queue/bound_mpmc.h>
#include <mx/queue/priority_queue.h>
namespace mx::tasking {
template <std::size_t CAPACITY> class SharedTaskQueue
{
public:
SharedTaskQueue() : _queue(CAPACITY) {}
~SharedTaskQueue() = default;
void push_back(TaskInterface *task) noexcept { _queue.try_push_back(task); }
[[nodiscard]] TaskInterface *pop_front() noexcept
{
TaskInterface *task = nullptr;
_queue.try_pop_front(task);
return task;
}
[[nodiscard]] bool empty() const noexcept { return _queue.empty(); }
private:
queue::BoundMPMC<TaskInterface *> _queue;
};
using GlobalSharedTaskQueue = queue::PriorityQueue<SharedTaskQueue<1U << 22U>, priority::low, priority::normal>;
using NUMASharedTaskQueue = queue::PriorityQueue<SharedTaskQueue<1U << 20U>, priority::low, priority::normal>;
} // namespace mx::tasking

View File

@@ -0,0 +1,43 @@
#include "task.h"
#include "runtime.h"
#include <mx/system/cpu.h>
using namespace mx::tasking;
TaskResult TaskResult::make_stop(const std::uint16_t worker_id, const bool stop_network) noexcept
{
auto *stop_task = runtime::new_task<StopTaskingTask>(worker_id, stop_network);
stop_task->annotate(std::uint16_t{0U});
return TaskResult::make_succeed_and_remove(stop_task);
}
TaskResult TaskLine::execute(const std::uint16_t worker_id)
{
auto result = this->_next_task->execute(worker_id);
if (result.is_remove())
{
mx::tasking::runtime::delete_task(worker_id, this->_next_task);
}
if (result.has_successor())
{
this->_next_task = static_cast<TaskInterface *>(result);
this->annotate(_next_task);
return TaskResult::make_succeed(this);
}
if (this->_waiting_tasks.empty() == false)
{
this->_next_task = this->_waiting_tasks.pop_front();
this->annotate(_next_task);
return TaskResult::make_succeed(this);
}
return TaskResult::make_remove();
}
TaskResult StopTaskingTask::execute(const std::uint16_t /*worker_id*/)
{
runtime::stop(this->_stop_network);
return TaskResult::make_remove();
}

View File

@@ -0,0 +1,641 @@
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.o /home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.d: \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.cpp \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h \
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h \
/home/mml/genode-igb/repos/base/include/base/stdint.h \
/home/mml/genode-igb/repos/base/include/base/log.h \
/home/mml/genode-igb/repos/base/include/base/output.h \
/home/mml/genode-igb/repos/base/include/util/interface.h \
/home/mml/genode-igb/repos/base/include/base/buffered_output.h \
/home/mml/genode-igb/repos/base/include/base/mutex.h \
/home/mml/genode-igb/repos/base/include/base/lock.h \
/home/mml/genode-igb/repos/base/include/util/noncopyable.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h \
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h \
/home/mml/genode-igb/repos/base/include/util/attempt.h \
/home/mml/genode-igb/repos/base/include/base/capability.h \
/home/mml/genode-igb/repos/base/include/util/string.h \
/home/mml/genode-igb/repos/base/include/util/misc_math.h \
/home/mml/genode-igb/repos/base/include/cpu/string.h \
/home/mml/genode-igb/repos/base/include/base/rpc.h \
/home/mml/genode-igb/repos/base/include/util/meta.h \
/home/mml/genode-igb/repos/base/include/base/native_capability.h \
/home/mml/genode-igb/repos/base/include/base/exception.h \
/home/mml/genode-igb/repos/base/include/base/quota_guard.h \
/home/mml/genode-igb/repos/base/include/base/cache.h \
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h \
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp \
/home/mml/genode-igb/repos/base/include/base/affinity.h \
/home/mml/genode-igb/repos/base/include/util/xml_node.h \
/home/mml/genode-igb/repos/base/include/util/token.h \
/home/mml/genode-igb/repos/base/include/base/thread.h \
/home/mml/genode-igb/repos/base/include/base/blockade.h \
/home/mml/genode-igb/repos/base/include/base/trace/logger.h \
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h \
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h \
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h \
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h \
/home/mml/genode-igb/repos/base/include/base/thread_state.h \
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h \
/home/mml/genode-igb/repos/base/include/base/signal.h \
/home/mml/genode-igb/repos/base/include/util/list.h \
/home/mml/genode-igb/repos/base/include/base/semaphore.h \
/home/mml/genode-igb/repos/base/include/util/fifo.h \
/home/mml/genode-igb/repos/base/include/dataspace/capability.h \
/home/mml/genode-igb/repos/base/include/base/rpc_args.h \
/home/mml/genode-igb/repos/base/include/session/session.h \
/home/mml/genode-igb/repos/base/include/base/session_label.h \
/home/mml/genode-igb/repos/base/include/util/arg_string.h \
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h \
/home/mml/genode-igb/repos/base/include/region_map/region_map.h \
/home/mml/genode-igb/repos/base/include/base/allocator.h \
/home/mml/genode-igb/repos/base/include/util/register.h \
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h \
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h \
/home/mml/genode-igb/repos/base/include/util/touch.h \
/home/mml/genode-igb/repos/base/include/base/env.h \
/home/mml/genode-igb/repos/base/include/parent/parent.h \
/home/mml/genode-igb/repos/base/include/base/id_space.h \
/home/mml/genode-igb/repos/base/include/util/avl_tree.h \
/home/mml/genode-igb/repos/base/include/session/capability.h \
/home/mml/genode-igb/repos/base/include/root/capability.h \
/home/mml/genode-igb/repos/base/include/root/root.h \
/home/mml/genode-igb/repos/base/include/base/entrypoint.h \
/home/mml/genode-igb/repos/base/include/util/reconstructible.h \
/home/mml/genode-igb/repos/base/include/util/construct_at.h \
/home/mml/genode-igb/repos/base/include/base/rpc_server.h \
/home/mml/genode-igb/repos/base/include/base/ipc.h \
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h \
/home/mml/genode-igb/repos/base/include/base/object_pool.h \
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h \
/home/mml/genode-igb/repos/base/include/base/trace/events.h \
/home/mml/genode-igb/repos/base/include/base/trace/policy.h \
/home/mml/genode-igb/repos/base/include/pd_session/capability.h \
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h \
/home/mml/genode-igb/repos/base/include/dataspace/client.h \
/home/mml/genode-igb/repos/base/include/base/rpc_client.h \
/home/mml/genode-igb/repos/base/include/base/heap.h \
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h \
/home/mml/genode-igb/repos/base/include/base/tslab.h \
/home/mml/genode-igb/repos/base/include/base/slab.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646 \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h \
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h \
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h \
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h \
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h \
/home/mml/genode-igb/repos/libports/include/libc/component.h
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_descriptor.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/array:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/pstl/pstl_config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bit:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cmath:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdint:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/alignment_helper.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/ptr.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/resource_interface.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/atomic:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/memory_transaction.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/spinlock.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/builtin.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/optimistic_lock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/limits:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/rw_spinlock.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/algorithm:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/thread:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cassert:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/tagged_ptr.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/functional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/synchronization/synchronization.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/random.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/new:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/environment.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/fstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/sstream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/string:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/utility:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/priority.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/variant:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_stack.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstddef:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstring:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/bitset:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/list.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/vector:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/runtime.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/hamstraaja.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/coreheap.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/memory/superblock.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/bit_alloc.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/bits.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/stdint.h:
/home/mml/genode-igb/repos/base/include/spec/64bit/base/fixed_stdint.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/atomic.h:
/home/mml/genode-igb/repos/base/include/base/stdint.h:
/home/mml/genode-igb/repos/base/include/base/log.h:
/home/mml/genode-igb/repos/base/include/base/output.h:
/home/mml/genode-igb/repos/base/include/util/interface.h:
/home/mml/genode-igb/repos/base/include/base/buffered_output.h:
/home/mml/genode-igb/repos/base/include/base/mutex.h:
/home/mml/genode-igb/repos/base/include/base/lock.h:
/home/mml/genode-igb/repos/base/include/util/noncopyable.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/trace/timestamp.h:
/home/mml/genode-igb/repos/base/include/base/ram_allocator.h:
/home/mml/genode-igb/repos/base/include/util/attempt.h:
/home/mml/genode-igb/repos/base/include/base/capability.h:
/home/mml/genode-igb/repos/base/include/util/string.h:
/home/mml/genode-igb/repos/base/include/util/misc_math.h:
/home/mml/genode-igb/repos/base/include/cpu/string.h:
/home/mml/genode-igb/repos/base/include/base/rpc.h:
/home/mml/genode-igb/repos/base/include/util/meta.h:
/home/mml/genode-igb/repos/base/include/base/native_capability.h:
/home/mml/genode-igb/repos/base/include/base/exception.h:
/home/mml/genode-igb/repos/base/include/base/quota_guard.h:
/home/mml/genode-igb/repos/base/include/base/cache.h:
/home/mml/genode-igb/repos/base/include/dataspace/dataspace.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/mpsc_queue.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/syscall-generic.h:
/home/mml/genode-igb/repos/base-tukija/include/tukija/spinlock.hpp:
/home/mml/genode-igb/repos/base/include/base/affinity.h:
/home/mml/genode-igb/repos/base/include/util/xml_node.h:
/home/mml/genode-igb/repos/base/include/util/token.h:
/home/mml/genode-igb/repos/base/include/base/thread.h:
/home/mml/genode-igb/repos/base/include/base/blockade.h:
/home/mml/genode-igb/repos/base/include/base/trace/logger.h:
/home/mml/genode-igb/repos/base/include/base/trace/buffer.h:
/home/mml/genode-igb/repos/base/include/cpu_session/cpu_session.h:
/home/mml/genode-igb/repos/base/include/cpu_session/capability.h:
/home/mml/genode-igb/repos/base/include/cpu_thread/cpu_thread.h:
/home/mml/genode-igb/repos/base/include/base/thread_state.h:
/home/mml/genode-igb/repos/base/include/spec/x86_64/cpu/cpu_state.h:
/home/mml/genode-igb/repos/base/include/base/signal.h:
/home/mml/genode-igb/repos/base/include/util/list.h:
/home/mml/genode-igb/repos/base/include/base/semaphore.h:
/home/mml/genode-igb/repos/base/include/util/fifo.h:
/home/mml/genode-igb/repos/base/include/dataspace/capability.h:
/home/mml/genode-igb/repos/base/include/base/rpc_args.h:
/home/mml/genode-igb/repos/base/include/session/session.h:
/home/mml/genode-igb/repos/base/include/base/session_label.h:
/home/mml/genode-igb/repos/base/include/util/arg_string.h:
/home/mml/genode-igb/repos/base/include/pd_session/pd_session.h:
/home/mml/genode-igb/repos/base/include/region_map/region_map.h:
/home/mml/genode-igb/repos/base/include/base/allocator.h:
/home/mml/genode-igb/repos/base/include/util/register.h:
/home/mml/genode-igb/repos/base/include/spec/x86/cpu/consts.h:
/home/mml/genode-igb/repos/base/include/base/attached_ram_dataspace.h:
/home/mml/genode-igb/repos/base/include/util/touch.h:
/home/mml/genode-igb/repos/base/include/base/env.h:
/home/mml/genode-igb/repos/base/include/parent/parent.h:
/home/mml/genode-igb/repos/base/include/base/id_space.h:
/home/mml/genode-igb/repos/base/include/util/avl_tree.h:
/home/mml/genode-igb/repos/base/include/session/capability.h:
/home/mml/genode-igb/repos/base/include/root/capability.h:
/home/mml/genode-igb/repos/base/include/root/root.h:
/home/mml/genode-igb/repos/base/include/base/entrypoint.h:
/home/mml/genode-igb/repos/base/include/util/reconstructible.h:
/home/mml/genode-igb/repos/base/include/util/construct_at.h:
/home/mml/genode-igb/repos/base/include/base/rpc_server.h:
/home/mml/genode-igb/repos/base/include/base/ipc.h:
/home/mml/genode-igb/repos/base/include/base/ipc_msgbuf.h:
/home/mml/genode-igb/repos/base/include/base/object_pool.h:
/home/mml/genode-igb/repos/base/include/base/weak_ptr.h:
/home/mml/genode-igb/repos/base/include/base/trace/events.h:
/home/mml/genode-igb/repos/base/include/base/trace/policy.h:
/home/mml/genode-igb/repos/base/include/pd_session/capability.h:
/home/mml/genode-igb/repos/base/include/base/attached_dataspace.h:
/home/mml/genode-igb/repos/base/include/dataspace/client.h:
/home/mml/genode-igb/repos/base/include/base/rpc_client.h:
/home/mml/genode-igb/repos/base/include/base/heap.h:
/home/mml/genode-igb/repos/base/include/base/allocator_avl.h:
/home/mml/genode-igb/repos/base/include/base/tslab.h:
/home/mml/genode-igb/repos/base/include/base/slab.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_distance.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/scheduler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/shared_task_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/bound_mpmc.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdlib:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/global_heap.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/config.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/chrono:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iterator:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/json.hpp:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/ciso646:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/initializer_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/iosfwd:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/memory:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/numeric:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/forward_list:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/tuple:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/type_traits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/unordered_map:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/valarray:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/exception:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/stdexcept:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/cstdio:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/istream:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/clocale:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ios:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ostream:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cache.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/cdefs.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/spec/x86_64/libc/machine/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/x86/endian.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_types.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_pthreadtypes.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_stdint.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/select.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_sigset.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_timeval.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/timespec.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/unistd.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/sys/_null.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/priority_queue.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_squad.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/queue/mpsc.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/worker.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/load.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_counter.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/aligned_t.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/task_tracer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/time.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/optional:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_buffer.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/prefetch_slot.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/ecpp/static_vector.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/compare:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_cycle_sampler.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_map.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_hash.h:
/home/mml/genode-igb/repos/ealanos/include/ealanos/util/tsl/robin_growth_policy.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/c_global/climits:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/ratio:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_execution_time_history.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/cpu.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_pool_occupancy.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/annotation.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/task_queues.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/reclamation/epoch_manager.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/worker_local_dynamic_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/core_set.h:
/home/mml/genode-igb/contrib/stdcxx-4eddc2a55a80ed5d3a50fee3f5c25e7ac42afd72/include/stdcxx/std/set:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/maybe_atomic.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/tasking/profiling/idle_profiler.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/server.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/io/network/config.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/fixed_size_allocator.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/memory/task_allocator_interface.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/resource/builder.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/system/thread.h:
/home/mml/genode-igb/contrib/libc-ec685e91ee80735b4a067fea4582aa7f5d06c192/include/libc/pthread.h:
/home/mml/genode-igb/repos/ealanos/src/lib/mx/util/logger.h:
/home/mml/genode-igb/repos/libports/include/libc/component.h:

View File

@@ -0,0 +1,330 @@
#pragma once
#include "annotation.h"
#include "config.h"
#include "priority.h"
#include "task_stack.h"
#include <bitset>
#include <cstdint>
#include <functional>
#include <mx/queue/list.h>
#include <mx/resource/ptr.h>
#include <variant>
#include <vector>
namespace mx::tasking {
class TaskInterface;
/**
* The TaskResult is returned by every task to tell the
* runtime what happens next. Possibilities are run a
* successor task, remove the returning task or stop
* the entire runtime.
*/
class TaskResult
{
public:
/**
* Let the runtime know that the given task
* should be run as a successor of the current
* task. The runtime will schedule that task.
*
* @param successor_task Task to succeed.
* @return A TaskResult that tells the
* runtime to run the given task.
*/
static TaskResult make_succeed(TaskInterface *successor_task) noexcept { return TaskResult{successor_task, false}; }
/**
* Let the runtime know that the given task
* should be run as a successor of the current
* task. The runtime will schedule that task.
*
* @param successor_task Task to succeed.
* @return A TaskResult that tells the
* runtime to run the given task.
*/
static TaskResult make_succeed(mx::resource::ptr resource) noexcept { return TaskResult{resource, false}; }
/**
* Let the runtime know that the given task
* should be removed after (successfully)
* finishing.
*
* @return A TaskResult that tells the
* runtime to remove the returning task.
*/
static TaskResult make_remove() noexcept { return TaskResult{nullptr, true}; }
/**
* Let the runtime know that the given task
* should be run as a successor of the current
* task and the current task should be removed.
*
* @param successor_task Task to succeed.
* @return A TaskResult that tells the runtime
* to run the given task and remove the
* returning task.
*/
static TaskResult make_succeed_and_remove(TaskInterface *successor_task) noexcept
{
return TaskResult{successor_task, true};
}
/**
* Nothing will happen
*
* @return An empty TaskResult.
*/
static TaskResult make_null() noexcept { return {}; }
/**
* Let the runtime know to stop after
* the returning task.
*
* @param worker_id Id of the current worker.
* @param stop_network If set to true, the network server will also be stopped.
* @return A TaskResult that tells the
* runtime to top.
*/
static TaskResult make_stop(std::uint16_t worker_id, bool stop_network = true) noexcept;
constexpr TaskResult() = default;
~TaskResult() = default;
TaskResult &operator=(const TaskResult &) = default;
explicit operator TaskInterface *() const noexcept { return _successor_task; }
explicit operator mx::resource::ptr() const noexcept { return _resource; }
[[nodiscard]] bool is_remove() const noexcept { return _remove_task; }
[[nodiscard]] bool has_successor() const noexcept { return _successor_task != nullptr; }
[[nodiscard]] bool has_resource() const noexcept { return static_cast<bool>(_resource); }
private:
constexpr TaskResult(TaskInterface *successor_task, const bool remove) noexcept
: _successor_task(successor_task), _remove_task(remove)
{
}
constexpr TaskResult(const mx::resource::ptr resource, const bool remove) noexcept
: _resource(resource), _remove_task(remove)
{
}
TaskInterface *_successor_task{nullptr};
mx::resource::ptr _resource;
bool _remove_task{false};
};
/**
* The task is the central execution unit of mxtasking.
* Every task that should be executed has to derive
* from this class.
*/
class TaskInterface
{
public:
using channel = std::uint16_t;
using node = std::uint8_t;
constexpr TaskInterface() = default;
virtual ~TaskInterface() = default;
/**
* Will be executed by a worker when the task gets CPU time.
*
* @param worker_id Worker ID the task is executed on.
* @return Pointer to the follow up task.
*/
virtual TaskResult execute(std::uint16_t worker_id) = 0;
/**
* @return Trace Id of the task, that will be included into recordings to assign
* time ranges to specific tasks.
*/
[[nodiscard]] virtual std::uint64_t trace_id() const noexcept { return 0U; }
/**
* @return The annotation of the task.
*/
[[nodiscard]] const annotation &annotation() const noexcept { return _annotation; }
/**
* @return The annotation of the task.
*/
[[nodiscard]] class annotation &annotation() noexcept { return _annotation; }
/**
* Annotate the task with a resource the task will work on.
* The size identifies how many bytes will be prefetched.
*
* @param resource Pointer to the resource.
* @param size Size of the resource (that will be prefetched).
*/
void annotate(const mx::resource::ptr resource_, const std::uint16_t size) noexcept
{
annotate(resource_, PrefetchSize::make(PrefetchDescriptor::PrefetchType::Temporal, size));
}
/**
* Annotate the task with a resource the task will work on.
* The object will be used for synchronization and prefetching.
*
* @param resource Pointer to the resource.
* @param prefetch_hint Mask for prefetching the resource.
*/
void annotate(const mx::resource::ptr resource_, const PrefetchDescriptor descriptor) noexcept
{
annotate(resource_);
annotate(PrefetchHint{descriptor, resource_});
}
/**
* Annotate the task with a resource the task will work on.
* The data object will be used for synchronization only.
*
* @param resource Pointer to the resource.
*/
void annotate(const mx::resource::ptr resource_) noexcept { _annotation.set(resource_); }
/**
* Annotate the task with a prefetch hint that will be prefetched.
*
* @param prefetch_hint Hint for prefetching.
*/
void annotate(const PrefetchHint prefetch_hint) noexcept { _annotation.set(prefetch_hint); }
/**
* Annotate the task with a desired channel the task should be executed on.
*
* @param worker_id ID of the channel.
*/
void annotate(const std::uint16_t worker_id) noexcept { _annotation.set(worker_id); }
/**
* Annotate the task with a desired NUMA node id the task should executed on.
*
* @param node_id ID of the NUMA node.
*/
void annotate(const std::uint8_t node_id) noexcept { _annotation.set(node_id); }
/**
* Annotate the task with a run priority (low, normal, high).
*
* @param priority_ Priority the task should run with.
*/
void annotate(const priority priority_) noexcept { _annotation.set(priority_); }
/**
* Copy annotations from other task to this one.
*
* @param other Other task to copy annotations from.
*/
void annotate(TaskInterface *other) noexcept { _annotation = other->_annotation; }
/**
* Copy annotation to this one.
*
* @param annotation
*/
void annotate(const auto &annotation) noexcept { _annotation = annotation; }
/**
* Annotate the task to execute on a specific destination.
*
* @param execution_destination Destination to execute on.
*/
void annotate(const annotation::execution_destination execution_destination) noexcept
{
_annotation.set(execution_destination);
}
/**
* Annotate the task whether it is a reading or writing task.
*
* @param is_readonly True, when the task is read only (false by default).
*/
void annotate(const annotation::access_intention access_intention) noexcept { _annotation.set(access_intention); }
/**
* @return Pointer to the next task in spawn queue.
*/
[[nodiscard]] TaskInterface *next() const noexcept { return _next; }
/**
* Set the next task for scheduling.
* @param next Task scheduled after this task.
*/
void next(TaskInterface *next) noexcept { _next = next; }
private:
/// Pointer for next task in queue.
TaskInterface *_next{nullptr};
/// Tasks annotations.
class annotation _annotation
{
};
};
class LambdaTask : public TaskInterface
{
public:
LambdaTask(std::function<TaskResult(std::uint16_t)> &&callback) noexcept : _callback(std::move(callback)) {}
LambdaTask(std::function<void()> &&callback) noexcept
: LambdaTask([callback = std::move(callback)](const std::uint16_t /*worker_id*/) {
callback();
return TaskResult::make_remove();
})
{
}
~LambdaTask() noexcept override = default;
TaskResult execute(std::uint16_t worker_id) override { return _callback(worker_id); }
private:
std::function<TaskResult(std::uint16_t)> _callback;
};
class TaskLine : public TaskInterface
{
public:
TaskLine() noexcept = default;
~TaskLine() noexcept override = default;
TaskResult execute(std::uint16_t worker_id) override;
void add(TaskInterface *task)
{
if (_next_task == nullptr)
{
_next_task = task;
annotate(task);
}
else
{
_waiting_tasks.push_back(task);
}
}
[[nodiscard]] bool empty() const noexcept { return _next_task == nullptr; }
private:
TaskInterface *_next_task;
queue::List<TaskInterface> _waiting_tasks;
};
class StopTaskingTask final : public TaskInterface
{
public:
constexpr StopTaskingTask(const bool stop_network) noexcept : _stop_network(stop_network) {}
~StopTaskingTask() override = default;
TaskResult execute(std::uint16_t worker_id) override;
private:
const bool _stop_network;
};
} // namespace mx::tasking

View File

@@ -0,0 +1,342 @@
#pragma once
#include "load.h"
#include "prefetch_distance.h"
#include "prefetch_slot.h"
#include "task.h"
#include "task_cycle_sampler.h"
#include "task_execution_time_history.h"
#include <array>
#include <cstdint>
#include <mx/memory/config.h>
#include <mx/system/cache.h>
#include <mx/system/cpu.h>
#include <utility>
namespace mx::tasking {
/**
* The task buffer holds tasks that are ready to execute.
* The buffer is realized as a ring buffer with a fixed size.
* All empty slots are null pointers.
*/
template <std::size_t S> class TaskBuffer
{
public:
class Slot
{
public:
constexpr Slot() noexcept = default;
~Slot() noexcept = default;
/**
* Assigns the task for execution to this slot.
* @param task Task that should be executed when the task buffer reaches this slot.
*/
void task(TaskInterface *task) noexcept { _task = task; }
[[nodiscard]] TaskInterface *task() const noexcept { return _task; }
/**
* Consumes the task of this slot and returns it.
* @return Task that should be executed.
*/
[[nodiscard]] TaskInterface *get() noexcept { return std::exchange(_task, nullptr); }
/**
* Executes the prefetch instructions (the task descriptor and its assigned resource).
*/
void prefetch() noexcept { _prefetch_slot.prefetch(); }
/**
* Sets the given task for prefetching when the task buffer reaches this slot.
* @param task Task that should be prefetched (both task descriptor and resource).
*/
void prefetch(const resource::ptr resource, const PrefetchDescriptor descriptor) noexcept
{
_prefetch_slot.assign(resource, descriptor);
}
bool operator==(std::nullptr_t) const noexcept { return _task == nullptr; }
bool operator!=(std::nullptr_t) const noexcept { return _task != nullptr; }
private:
TaskInterface *_task{nullptr};
PrefetchSlot _prefetch_slot{};
};
public:
constexpr explicit TaskBuffer(const PrefetchDistance prefetch_distance) noexcept
: _prefetch_distance(prefetch_distance)
{
}
~TaskBuffer() noexcept = default;
/**
* @return True, when the buffer is empty.
*/
[[nodiscard]] bool empty() const noexcept { return _buffer[_head] == nullptr; }
/**
* @return Number of tasks in the buffer.
*/
[[nodiscard]] std::uint16_t size() const noexcept
{
return _tail >= _head ? (_tail - _head) : (S - (_head - _tail));
}
/**
* @return Number of maximal tasks of the buffer.
*/
[[nodiscard]] constexpr auto max_size() const noexcept { return S; }
/**
* @return Number of free slots.
*/
[[nodiscard]] std::uint16_t available_slots() const noexcept { return S - size(); }
Slot &next() noexcept
{
auto &slot = this->_buffer[this->_head];
this->_head = TaskBuffer<S>::normalize(this->_head + 1U);
return slot;
}
[[nodiscard]] TaskInterface *task(const std::uint16_t index) const noexcept
{
return this->_buffer[TaskBuffer<S>::normalize(this->_head + index)].task();
}
[[nodiscard]] TaskInterface *head() const noexcept { return this->_buffer[this->_head].task(); }
/**
* Takes out tasks from the given queue and inserts them into the buffer.
* @param from_queue Queue to take tasks from.
* @param count Number of maximal tasks to take out of the queue.
* @return Number of retrieved tasks.
*/
template <class Q> std::uint16_t fill(Q &from_queue, std::uint16_t count) noexcept;
[[nodiscard]] std::uint8_t refill_treshold() const noexcept
{
if (this->_prefetch_distance.is_enabled())
{
return this->_prefetch_distance.is_automatic() ? this->_task_cycles.size()
: this->_prefetch_distance.fixed_distance();
}
return 0U;
}
[[nodiscard]] bool is_prefetching_enabled() const noexcept { return this->_prefetch_distance.is_enabled(); }
[[nodiscard]] TaskCycleSampler &sampler() noexcept { return _task_cycle_sampler; }
private:
/// Prefetch distance.
const PrefetchDistance _prefetch_distance;
/// Index of the first element in the buffer.
std::uint16_t _head{0U};
/// Index of the last element in the buffer.
std::uint16_t _tail{0U};
/// Array with task-slots.
std::array<Slot, S> _buffer{};
/// History of last cycles for the last dispatched tasks.
TaskExecutionTimeHistory _task_cycles;
/// Sample for monitoring task cycles.
TaskCycleSampler _task_cycle_sampler;
/**
* Normalizes the index with respect to the size.
* @param index Index.
* @return Normalized index.
*/
[[nodiscard]] static std::uint16_t normalize(const std::uint16_t index) noexcept { return index & (S - 1U); }
/**
* Normalizes the index backwards with respect to the given offset.
* @param index Index.
* @param offset Offset to index.
* @return Normalized index.
*/
[[nodiscard]] static std::uint16_t normalize_backward(const std::uint16_t index,
const std::uint16_t offset) noexcept
{
const auto diff = std::int32_t(index) - std::int32_t(offset);
return diff + (static_cast<std::uint8_t>(index < offset) * S);
}
/**
* Calculates the number of to-prefeteched cache lines from a given descriptor.
*
* @param descriptor Descriptor describing the prefetch.
* @return Number of cache lines to prefetch.
*/
[[nodiscard]] static std::uint16_t prefetched_cache_lines(const PrefetchDescriptor descriptor) noexcept
{
const auto descriptor_id = descriptor.id();
const auto data = descriptor.data_without_descriptor_bits();
switch (descriptor_id)
{
case PrefetchDescriptor::SizeNonTemporal:
case PrefetchDescriptor::SizeTemporal:
case PrefetchDescriptor::SizeWrite:
return (PrefetchSizeView{data}.get()) / system::cache::line_size();
case PrefetchDescriptor::CallbackAny:
return (PrefetchCallbackView{data}.size()) / system::cache::line_size();
case PrefetchDescriptor::MaskNonTemporal:
case PrefetchDescriptor::MaskTemporal:
case PrefetchDescriptor::MaskWrite:
return PrefetchMaskView{data}.count();
case PrefetchDescriptor::None:
return 0U;
}
}
};
template <std::size_t S>
template <class Q>
std::uint16_t TaskBuffer<S>::fill(Q &from_queue, std::uint16_t count) noexcept
{
if (count == 0U || from_queue.empty())
{
return 0U;
}
const auto size = S - count;
TaskInterface *task;
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value)
{
std::tie(task, count) = from_queue.pop_front(count);
}
/// Prefetching at all.
if (this->_prefetch_distance.is_enabled())
{
/// Prefetching with automatic calculated prefetch distance
/// based on annotated cycles.
if (this->_prefetch_distance.is_automatic())
{
for (auto i = 0U; i < count; ++i)
{
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value == false)
{
task = static_cast<TaskInterface *>(from_queue.pop_front());
if (task == nullptr)
{
return i;
}
}
/// Location where the task will be scheduled.
const auto task_buffer_index = this->_tail;
/// Schedule the task to the end of the task buffer.
this->_buffer[task_buffer_index].task(task);
/// Increment tail for the next task.
this->_tail = TaskBuffer<S>::normalize(task_buffer_index + 1U);
/// Schedule prefetch instruction <prefetch_distance> slots before.
if (task->annotation().has_prefetch_hint())
{
const auto &hint = task->annotation().prefetch_hint();
/// Calculate cycles needed by the prefetch (|lines| * fixed latency).
const auto prefetched_cache_lines = TaskBuffer::prefetched_cache_lines(hint.descriptor());
const auto needed_cycles_latency =
prefetched_cache_lines * memory::config::latency_per_prefetched_cache_line();
/// Go back (towards head) until latency is hidden by task executions.
const auto prefetch_distance = this->_task_cycles.prefetch_distance(needed_cycles_latency);
/// Schedule the prefetch to tail - prefetch distance.
this->_buffer[TaskBuffer<S>::normalize_backward(task_buffer_index, prefetch_distance)].prefetch(
hint.resource(), hint.descriptor());
}
/// Push task cycles (either monitored or annotated) to the history.
const auto task_cycles = this->_task_cycle_sampler.cycles(task);
this->_task_cycles.push(task_cycles);
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value)
{
task = task->next();
}
}
}
/// Prefetching with fixed prefetch distance.
else
{
auto prefetch_tail =
TaskBuffer<S>::normalize_backward(this->_tail, this->_prefetch_distance.fixed_distance());
for (auto i = 0U; i < count; ++i)
{
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value == false)
{
task = static_cast<TaskInterface *>(from_queue.pop_front());
if (task == nullptr)
{
return i;
}
}
/// Schedule task.
this->_buffer[this->_tail].task(task);
/// Increment tail.
this->_tail = TaskBuffer<S>::normalize(this->_tail + 1U);
/// Schedule prefetch instruction <prefetch_distance> slots before.
if (size + i >= this->_prefetch_distance.fixed_distance() && task->annotation().has_prefetch_hint())
{
const auto &hint = task->annotation().prefetch_hint();
this->_buffer[prefetch_tail].prefetch(hint.resource(), hint.descriptor());
}
/// Increment prefetch tail.
prefetch_tail = TaskBuffer<S>::normalize(prefetch_tail + 1U);
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value)
{
task = task->next();
}
}
}
}
else
{
/// No prefetching.
for (auto i = 0U; i < count; ++i)
{
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value == false)
{
task = static_cast<TaskInterface *>(from_queue.pop_front());
if (task == nullptr)
{
return i;
}
}
/// Schedule task.
this->_buffer[this->_tail].task(task);
/// Increment tail.
this->_tail = TaskBuffer<S>::normalize(this->_tail + 1U);
if constexpr (std::is_same<Q, mx::queue::List<TaskInterface>>::value)
{
task = task->next();
}
}
}
return count;
}
} // namespace mx::tasking

View File

@@ -0,0 +1,127 @@
#pragma once
#include "config.h"
#include "task.h"
#include <cstdint>
#include <tsl/robin_map.h>
#include <tuple>
#include <unordered_map>
namespace mx::tasking {
class TaskCycleSampler
{
private:
class Sample
{
public:
constexpr Sample() noexcept = default;
explicit Sample(const std::uint64_t cycles) noexcept : _count(1U), _cycles(cycles), _average_cycles(cycles) {}
Sample(const std::uint32_t count, const std::uint64_t cycles) noexcept
: _count(count), _cycles(cycles), _average_cycles(cycles / count)
{
}
~Sample() noexcept = default;
void add(const std::uint32_t cycles) noexcept
{
++_count;
_cycles += cycles;
_average_cycles = _cycles / _count;
}
[[nodiscard]] std::uint32_t average() const noexcept { return _average_cycles; }
[[nodiscard]] std::uint64_t count() const noexcept { return _count; }
[[nodiscard]] std::uint64_t cycles() const noexcept { return _cycles; }
private:
/// Number of how many times this task was sampled.
std::uint64_t _count{0U};
/// Number of cycles sampled for this task.
std::uint64_t _cycles{0U};
/// Number of cycles in average (_count/_cycles).
std::uint32_t _average_cycles{0U};
};
public:
TaskCycleSampler() { _task_cycles.reserve(16U); }
~TaskCycleSampler() = default;
void add(const std::uint64_t task_id, const std::uint64_t cycles)
{
if (task_id != 0U)
{
if (auto iterator = _task_cycles.find(task_id); iterator != _task_cycles.end())
{
iterator.value().add(cycles);
}
else
{
_task_cycles.insert(std::make_pair(task_id, Sample{cycles}));
}
}
}
[[nodiscard]] std::uint32_t cycles(TaskInterface *task) const
{
if constexpr (config::is_monitor_task_cycles_for_prefetching())
{
const auto trace_id = task->trace_id();
if (const auto iterator = _task_cycles.find(trace_id); iterator != _task_cycles.end())
{
return iterator.value().average();
}
}
return task->annotation().cycles();
}
void dump()
{
for (const auto &task : _task_cycles)
{
std::cout << task.first << " = " << task.second.average() << "(" << task.second.cycles() << "/"
<< task.second.count() << ")" << std::endl;
}
}
[[nodiscard]] std::unordered_map<std::uint64_t, Sample> get() const noexcept
{
auto cycles = std::unordered_map<std::uint64_t, Sample>{};
for (const auto &task : _task_cycles)
{
cycles.insert(std::make_pair(task.first, task.second));
}
return cycles;
}
private:
class Hash
{
public:
std::size_t operator()(std::uint64_t key) const noexcept
{
key ^= key >> 33U;
key *= std::uint64_t(0xff51afd7ed558ccd);
key ^= key >> 33U;
// key *= std::uint64_t(0xc4ceb9fe1a85ec53);
// key ^= key >> 33U;
return static_cast<std::size_t>(key);
}
};
/// List of all tasks, (their average cycles, total number of sampled executions and total cycles during execution).
tsl::robin_map<std::uint64_t, Sample, Hash> _task_cycles;
};
} // namespace mx::tasking

View File

@@ -0,0 +1,92 @@
#pragma once
#include <cstdint>
#ifdef USE_AVX2
#include <immintrin.h>
#else
#include <algorithm>
#include <array>
#endif
namespace mx::tasking {
#ifdef USE_AVX2
class TaskExecutionTimeHistory
{
public:
TaskExecutionTimeHistory() noexcept { _history = _mm256_setzero_si256(); }
~TaskExecutionTimeHistory() noexcept = default;
[[nodiscard]] constexpr std::uint8_t size() const noexcept { return 8U; }
[[nodiscard]] std::uint8_t prefetch_distance(const std::uint32_t needed_cycles) noexcept
{
const auto needed_cycles_vector = _mm256_set1_epi32(std::int32_t(needed_cycles));
/// Compare each item in the histry if the slot covers the needed cycles.
const auto compared_cycles = _mm256_cmpgt_epi32(needed_cycles_vector, _history);
/// Count the slots needed.
const auto mask = _mm256_movemask_epi8(compared_cycles);
const auto pop_count = _mm_popcnt_u32(mask);
const auto prefetch_distance = pop_count >> 2U;
return prefetch_distance;
}
void push(const std::uint32_t cycles) noexcept
{
/// Shift out the last task.
const auto shifted = _mm256_alignr_epi8(_mm256_permute2x128_si256(_history, _history, 0x81), _history, 4);
/// Add the task's cycles to the history.
_history = _mm256_add_epi32(shifted, _mm256_set1_epi32(cycles));
}
private:
/// Last 8 task cycles (summed up = index(0) next task, index(1) = index(0) + next next task, etc.)
alignas(64) __m256i _history;
};
#else
class TaskExecutionTimeHistory
{
public:
TaskExecutionTimeHistory() noexcept = default;
~TaskExecutionTimeHistory() noexcept = default;
[[nodiscard]] constexpr std::uint8_t size() const noexcept { return 8U; }
[[nodiscard]] std::uint8_t prefetch_distance(const std::uint32_t needed_cycles) noexcept
{
auto cycles = _history[7U];
for (auto i = 6; i > 0; --i)
{
if (cycles >= needed_cycles)
{
return 8 - (i + 1);
}
cycles += _history[i];
}
return 8;
}
void push(const std::uint32_t cycles) noexcept
{
/// Shift out the last task.
std::rotate(_history.begin(), _history.begin() + 1, _history.end());
/// Add the task's cycles to the history.
_history[7U] = cycles;
}
private:
/// Last 8 task cycles (summed up = index(0) next task, index(1) = index(0) + next next task, etc.)
alignas(64) std::array<std::uint32_t, 8U> _history{0U};
};
#endif
} // namespace mx::tasking

View File

@@ -0,0 +1,107 @@
#pragma once
#include "config.h"
#include "task.h"
#include "task_buffer.h"
#include "task_pool_occupancy.h"
#include "task_queues.h"
#include <array>
#include <mx/memory/config.h>
#include <mx/queue/list.h>
#include <mx/queue/mpsc.h>
#include <mx/queue/priority_queue.h>
namespace mx::tasking {
class alignas(64) TaskPool
{
public:
explicit TaskPool(const std::uint16_t count_workers, const std::uint16_t worker_id, const std::uint8_t numa_id)
: _queues(worker_id, numa_id, count_workers)
{
}
[[nodiscard]] std::uint64_t withdraw(TaskBuffer<config::task_buffer_size()> &task_buffer) noexcept
{
// Fill with normal prioritized.
const auto size = _queues.fill<priority::normal>(task_buffer, task_buffer.available_slots());
// Fill with low prioritized.
if (task_buffer.empty()) [[unlikely]]
{
return _queues.fill<priority::low>(task_buffer, config::task_buffer_size());
}
return size;
}
/**
* Schedules the task to thread-safe queue with regard to the NUMA region
* of the producer. Producer of different NUMA regions should not share
* a single queue.
* @param task Task to be scheduled.
* @param local_numa_node_id NUMA region of the producer.
* @param local_worker_id Worker ID of the producer.
*/
void push_back_remote(TaskInterface *task, const std::uint8_t local_numa_node_id,
const std::uint16_t local_worker_id) noexcept
{
_queues.push_back_remote(task, local_numa_node_id, local_worker_id);
}
/**
* Schedules a task to the local queue, which is not thread-safe. Only
* the channel owner should spawn tasks this way.
* @param task Task to be scheduled.
*/
void push_back_local(TaskInterface *task) noexcept { _queues.push_back_local(task); }
/**
* Schedules a task to the local queue, which is not thread-safe. Only
* the channel owner should spawn tasks this way.
* @param first First task to be scheduled.
* @param last Last task of the list.
*/
void push_back_local(TaskInterface *first, TaskInterface *last) noexcept { _queues.push_back_local(first, last); }
/**
* Adds usage prediction of a resource to this channel.
* @param usage Predicted usage.
*/
void predict_usage(const mx::resource::expected_access_frequency usage) noexcept { _occupancy.predict(usage); }
/**
* Updates the usage prediction of this channel.
* @param old_prediction So far predicted usage.
* @param new_prediction New predicted usage.
*/
void modify_predicted_usage(const mx::resource::expected_access_frequency old_prediction,
const mx::resource::expected_access_frequency new_prediction) noexcept
{
_occupancy.revoke(old_prediction);
_occupancy.predict(new_prediction);
}
/**
* @return Aggregated predicted usage.
*/
[[nodiscard]] mx::resource::expected_access_frequency predicted_usage() const noexcept
{
return static_cast<mx::resource::expected_access_frequency>(_occupancy);
}
/**
* @return True, whenever min. one prediction was "excessive".
*/
[[nodiscard]] bool has_excessive_usage_prediction() const noexcept
{
return _occupancy.has_excessive_usage_prediction();
}
private:
/// Backend queues.
TaskQueues<config::queue()> _queues;
// Holder of resource predictions of this channel.
alignas(64) TaskPoolOccupancy _occupancy;
};
} // namespace mx::tasking

View File

@@ -0,0 +1,79 @@
#pragma once
#include <array>
#include <atomic>
#include <mx/resource/annotation.h>
#include <mx/resource/ptr.h>
namespace mx::tasking {
/**
* Stores usage predictions.
*/
class TaskPoolOccupancy
{
public:
constexpr TaskPoolOccupancy() = default;
~TaskPoolOccupancy() = default;
/**
* Adds the given predicted usage.
* @param predicted_usage Predicted usage.
*/
void predict(const mx::resource::expected_access_frequency predicted_usage) noexcept
{
_predicted_usage_counter[static_cast<std::uint8_t>(predicted_usage)].fetch_add(1, std::memory_order_relaxed);
}
/**
* Subtracts the given predicted usage.
* @param predicted_usage Predicted usage.
*/
void revoke(const mx::resource::expected_access_frequency predicted_usage) noexcept
{
_predicted_usage_counter[static_cast<std::uint8_t>(predicted_usage)].fetch_sub(1, std::memory_order_relaxed);
}
/**
* @return True, when at least one prediction was "excessive".
*/
[[nodiscard]] bool has_excessive_usage_prediction() const noexcept
{
return has_at_least_one<mx::resource::expected_access_frequency::excessive>();
}
/**
* @return The highest predicted usage.
*/
explicit operator mx::resource::expected_access_frequency() const noexcept
{
if (has_at_least_one<mx::resource::expected_access_frequency::excessive>())
{
return mx::resource::expected_access_frequency::excessive;
}
if (has_at_least_one<mx::resource::expected_access_frequency::high>())
{
return mx::resource::expected_access_frequency::high;
}
if (has_at_least_one<mx::resource::expected_access_frequency::normal>())
{
return mx::resource::expected_access_frequency::normal;
}
return mx::resource::expected_access_frequency::unused;
}
private:
// Counter of predicted usages.
std::array<std::atomic_uint64_t, 4U> _predicted_usage_counter{0U};
/**
* @return True, when at least one usage as given by the template was predicted.
*/
template <mx::resource::expected_access_frequency U> [[nodiscard]] bool has_at_least_one() const noexcept
{
return _predicted_usage_counter[static_cast<std::uint8_t>(U)].load(std::memory_order_relaxed) > 0;
}
};
} // namespace mx::tasking

View File

@@ -0,0 +1,168 @@
#pragma once
#include "config.h"
#include "task.h"
#include "task_buffer.h"
#include <mx/memory/config.h>
#include <mx/queue/list.h>
#include <mx/queue/mpsc.h>
#include <mx/queue/priority_queue.h>
namespace mx::tasking {
template <config::queue_backend M> class TaskQueues
{
};
template <> class TaskQueues<config::queue_backend::Single>
{
public:
TaskQueues(const std::uint16_t /*worker_id*/, const std::uint8_t /*numa_node_id*/,
const std::uint16_t /*count_workers*/)
{
}
~TaskQueues() = default;
void push_back_remote(TaskInterface *task, const std::uint8_t /*numa_node_id*/,
const std::uint16_t /*local_worker_id*/) noexcept
{
_queue.get(task->annotation().priority()).push_back(task);
}
void push_back_local(TaskInterface *task) noexcept { _queue.get(task->annotation().priority()).push_back(task); }
void push_back_local(TaskInterface *first, TaskInterface *last) noexcept
{
_queue.get(first->annotation().priority()).push_back(first, last);
}
template <priority P>
[[nodiscard]] std::uint64_t fill(TaskBuffer<config::task_buffer_size()> &task_buffer,
std::uint64_t available) noexcept
{
available -= task_buffer.template fill(_queue.template get<P>(), available);
return task_buffer.max_size() - available;
}
private:
using MPSC = queue::PriorityQueue<queue::MPSC<TaskInterface>, priority::low, priority::normal>;
/// Single queue per worker.
MPSC _queue;
};
template <> class TaskQueues<config::queue_backend::NUMALocal>
{
public:
TaskQueues(const std::uint16_t /*worker_id*/, const std::uint8_t numa_node_id,
const std::uint16_t /*count_workers*/)
: _numa_node_id(numa_node_id)
{
}
~TaskQueues() = default;
void push_back_remote(TaskInterface *task, const std::uint8_t numa_node_id,
const std::uint16_t /*local_worker_id*/) noexcept
{
_remote_queues[numa_node_id].get(task->annotation().priority()).push_back(task);
}
void push_back_local(TaskInterface *task) noexcept
{
_local_queue.get(task->annotation().priority()).push_back(task);
}
void push_back_local(TaskInterface *first, TaskInterface *last) noexcept
{
_local_queue.get(first->annotation().priority()).push_back(first, last);
}
template <priority P>
[[nodiscard]] std::uint64_t fill(TaskBuffer<config::task_buffer_size()> &task_buffer,
std::uint64_t available) noexcept
{
// 1) Fill up from the local queue.
available -= task_buffer.fill(_local_queue.get<P>(), available);
if (available > 0U)
{
// 2) Fill up from remote queues; start with the NUMA-local one.
for (auto numa_index = 0U; numa_index < memory::config::max_numa_nodes(); ++numa_index)
{
static_assert((memory::config::max_numa_nodes() & (memory::config::max_numa_nodes() - 1U)) == 0U);
const auto numa_id = (_numa_node_id + numa_index) & (memory::config::max_numa_nodes() - 1U);
available -= task_buffer.fill(_remote_queues[numa_id].get<P>(), available);
}
}
return task_buffer.max_size() - available;
}
private:
using List = queue::PriorityQueue<queue::List<TaskInterface>, priority::low, priority::normal>;
using MPSC = queue::PriorityQueue<queue::MPSC<TaskInterface>, priority::low, priority::normal>;
const std::uint8_t _numa_node_id;
// Backend queues for a single producer (owning worker thread) and different priorities.
List _local_queue;
// Backend queues for multiple produces in different NUMA regions and different priorities,
alignas(64) std::array<MPSC, memory::config::max_numa_nodes()> _remote_queues;
};
template <> class TaskQueues<config::queue_backend::WorkerLocal>
{
public:
TaskQueues(const std::uint16_t worker_id, const std::uint8_t /*numa_node_id*/, const std::uint16_t count_workers)
: _worker_id(worker_id), _count_workers(count_workers)
{
}
~TaskQueues() = default;
void push_back_remote(TaskInterface *task, const std::uint8_t /*numa_node_id*/,
const std::uint16_t local_worker_id) noexcept
{
_queues[local_worker_id].get(task->annotation().priority()).push_back(task);
}
void push_back_local(TaskInterface *task) noexcept
{
_queues[_worker_id].get(task->annotation().priority()).push_back(task);
}
void push_back_local(TaskInterface *first, TaskInterface *last) noexcept
{
_queues[_worker_id].get(first->annotation().priority()).push_back(first, last);
}
template <priority P>
[[nodiscard]] std::uint64_t fill(TaskBuffer<config::task_buffer_size()> &task_buffer,
std::uint64_t available) noexcept
{
auto worker_id = _worker_id;
for (auto i = 0U; i < _count_workers; ++i)
{
const auto target_worker_id = (worker_id + i) % _count_workers;
available -= task_buffer.fill(_queues[target_worker_id].get<P>(), available);
if (available == 0U)
{
return task_buffer.max_size();
}
}
return task_buffer.max_size() - available;
}
private:
using MPSC = queue::PriorityQueue<queue::MPSC<TaskInterface>, priority::low, priority::normal>;
const std::uint16_t _worker_id;
const std::uint16_t _count_workers;
// One queue per worker.
std::array<MPSC, config::max_cores()> _queues;
};
} // namespace mx::tasking

View File

@@ -0,0 +1,42 @@
#include "task_squad.h"
#include "runtime.h"
using namespace mx::tasking;
void TaskSquad::flush() noexcept
{
auto [first, last] = this->_remote_queue.pop();
if (first != nullptr)
{
if (last != nullptr)
{
this->_local_queue.push_back(first, last);
}
else
{
this->_local_queue.push_back(first);
}
}
}
TaskResult TaskSquadSpawnTask::execute(std::uint16_t worker_id)
{
this->_task_squad.flush();
/// Get all tasks.
auto [first, last] = this->_task_squad._local_queue.pop();
if (first != nullptr)
{
if (last != nullptr)
{
runtime::spawn(*first, *last, worker_id);
}
else
{
first->annotate(annotation::execution_destination::local);
runtime::spawn(*first, worker_id);
}
}
return TaskResult::make_remove();
}

Some files were not shown because too many files have changed in this diff Show More