Browse Source
Merge pull request #2265 from FernandoS27/multilevelqueue
Merge pull request #2265 from FernandoS27/multilevelqueue
Replace old Thread Queue for a new Multi Level Queuepull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 484 additions and 19 deletions
-
1src/common/CMakeLists.txt
-
39src/common/bit_util.h
-
337src/common/multi_level_queue.h
-
42src/core/hle/kernel/scheduler.cpp
-
4src/core/hle/kernel/scheduler.h
-
2src/tests/CMakeLists.txt
-
23src/tests/common/bit_utils.cpp
-
55src/tests/common/multi_level_queue.cpp
@ -0,0 +1,337 @@ |
|||||
|
// Copyright 2019 TuxSH |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <array> |
||||
|
#include <iterator> |
||||
|
#include <list> |
||||
|
#include <utility> |
||||
|
|
||||
|
#include "common/bit_util.h" |
||||
|
#include "common/common_types.h" |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
/** |
||||
|
* A MultiLevelQueue is a type of priority queue which has the following characteristics: |
||||
|
* - iteratable through each of its elements. |
||||
|
* - back can be obtained. |
||||
|
* - O(1) add, lookup (both front and back) |
||||
|
* - discrete priorities and a max of 64 priorities (limited domain) |
||||
|
* This type of priority queue is normaly used for managing threads within an scheduler |
||||
|
*/ |
||||
|
template <typename T, std::size_t Depth> |
||||
|
class MultiLevelQueue { |
||||
|
public: |
||||
|
using value_type = T; |
||||
|
using reference = value_type&; |
||||
|
using const_reference = const value_type&; |
||||
|
using pointer = value_type*; |
||||
|
using const_pointer = const value_type*; |
||||
|
|
||||
|
using difference_type = typename std::pointer_traits<pointer>::difference_type; |
||||
|
using size_type = std::size_t; |
||||
|
|
||||
|
template <bool is_constant> |
||||
|
class iterator_impl { |
||||
|
public: |
||||
|
using iterator_category = std::bidirectional_iterator_tag; |
||||
|
using value_type = T; |
||||
|
using pointer = std::conditional_t<is_constant, T*, const T*>; |
||||
|
using reference = std::conditional_t<is_constant, const T&, T&>; |
||||
|
using difference_type = typename std::pointer_traits<pointer>::difference_type; |
||||
|
|
||||
|
friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) { |
||||
|
if (lhs.IsEnd() && rhs.IsEnd()) |
||||
|
return true; |
||||
|
return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it); |
||||
|
} |
||||
|
|
||||
|
friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) { |
||||
|
return !operator==(lhs, rhs); |
||||
|
} |
||||
|
|
||||
|
reference operator*() const { |
||||
|
return *it; |
||||
|
} |
||||
|
|
||||
|
pointer operator->() const { |
||||
|
return it.operator->(); |
||||
|
} |
||||
|
|
||||
|
iterator_impl& operator++() { |
||||
|
if (IsEnd()) { |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
++it; |
||||
|
|
||||
|
if (it == GetEndItForPrio()) { |
||||
|
u64 prios = mlq.used_priorities; |
||||
|
prios &= ~((1ULL << (current_priority + 1)) - 1); |
||||
|
if (prios == 0) { |
||||
|
current_priority = mlq.depth(); |
||||
|
} else { |
||||
|
current_priority = CountTrailingZeroes64(prios); |
||||
|
it = GetBeginItForPrio(); |
||||
|
} |
||||
|
} |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
iterator_impl& operator--() { |
||||
|
if (IsEnd()) { |
||||
|
if (mlq.used_priorities != 0) { |
||||
|
current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities); |
||||
|
it = GetEndItForPrio(); |
||||
|
--it; |
||||
|
} |
||||
|
} else if (it == GetBeginItForPrio()) { |
||||
|
u64 prios = mlq.used_priorities; |
||||
|
prios &= (1ULL << current_priority) - 1; |
||||
|
if (prios != 0) { |
||||
|
current_priority = CountTrailingZeroes64(prios); |
||||
|
it = GetEndItForPrio(); |
||||
|
--it; |
||||
|
} |
||||
|
} else { |
||||
|
--it; |
||||
|
} |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
iterator_impl operator++(int) { |
||||
|
const iterator_impl v{*this}; |
||||
|
++(*this); |
||||
|
return v; |
||||
|
} |
||||
|
|
||||
|
iterator_impl operator--(int) { |
||||
|
const iterator_impl v{*this}; |
||||
|
--(*this); |
||||
|
return v; |
||||
|
} |
||||
|
|
||||
|
// allow implicit const->non-const |
||||
|
iterator_impl(const iterator_impl<false>& other) |
||||
|
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {} |
||||
|
|
||||
|
iterator_impl(const iterator_impl<true>& other) |
||||
|
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {} |
||||
|
|
||||
|
iterator_impl& operator=(const iterator_impl<false>& other) { |
||||
|
mlq = other.mlq; |
||||
|
it = other.it; |
||||
|
current_priority = other.current_priority; |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
friend class iterator_impl<true>; |
||||
|
iterator_impl() = default; |
||||
|
|
||||
|
private: |
||||
|
friend class MultiLevelQueue; |
||||
|
using container_ref = |
||||
|
std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>; |
||||
|
using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator, |
||||
|
typename std::list<T>::iterator>; |
||||
|
|
||||
|
explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority) |
||||
|
: mlq(mlq), it(it), current_priority(current_priority) {} |
||||
|
explicit iterator_impl(container_ref mlq, u32 current_priority) |
||||
|
: mlq(mlq), it(), current_priority(current_priority) {} |
||||
|
|
||||
|
bool IsEnd() const { |
||||
|
return current_priority == mlq.depth(); |
||||
|
} |
||||
|
|
||||
|
list_iterator GetBeginItForPrio() const { |
||||
|
return mlq.levels[current_priority].begin(); |
||||
|
} |
||||
|
|
||||
|
list_iterator GetEndItForPrio() const { |
||||
|
return mlq.levels[current_priority].end(); |
||||
|
} |
||||
|
|
||||
|
container_ref mlq; |
||||
|
list_iterator it; |
||||
|
u32 current_priority; |
||||
|
}; |
||||
|
|
||||
|
using iterator = iterator_impl<false>; |
||||
|
using const_iterator = iterator_impl<true>; |
||||
|
|
||||
|
void add(const T& element, u32 priority, bool send_back = true) { |
||||
|
if (send_back) |
||||
|
levels[priority].push_back(element); |
||||
|
else |
||||
|
levels[priority].push_front(element); |
||||
|
used_priorities |= 1ULL << priority; |
||||
|
} |
||||
|
|
||||
|
void remove(const T& element, u32 priority) { |
||||
|
auto it = ListIterateTo(levels[priority], element); |
||||
|
if (it == levels[priority].end()) |
||||
|
return; |
||||
|
levels[priority].erase(it); |
||||
|
if (levels[priority].empty()) { |
||||
|
used_priorities &= ~(1ULL << priority); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) { |
||||
|
remove(element, old_priority); |
||||
|
add(element, new_priority, !adjust_front); |
||||
|
} |
||||
|
void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) { |
||||
|
adjust(*it, old_priority, new_priority, adjust_front); |
||||
|
} |
||||
|
|
||||
|
void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) { |
||||
|
ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority], |
||||
|
ListIterateTo(levels[priority], element)); |
||||
|
|
||||
|
other.used_priorities |= 1ULL << priority; |
||||
|
|
||||
|
if (levels[priority].empty()) { |
||||
|
used_priorities &= ~(1ULL << priority); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) { |
||||
|
transfer_to_front(*it, priority, other); |
||||
|
} |
||||
|
|
||||
|
void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) { |
||||
|
ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority], |
||||
|
ListIterateTo(levels[priority], element)); |
||||
|
|
||||
|
other.used_priorities |= 1ULL << priority; |
||||
|
|
||||
|
if (levels[priority].empty()) { |
||||
|
used_priorities &= ~(1ULL << priority); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) { |
||||
|
transfer_to_back(*it, priority, other); |
||||
|
} |
||||
|
|
||||
|
void yield(u32 priority, std::size_t n = 1) { |
||||
|
ListShiftForward(levels[priority], n); |
||||
|
} |
||||
|
|
||||
|
std::size_t depth() const { |
||||
|
return Depth; |
||||
|
} |
||||
|
|
||||
|
std::size_t size(u32 priority) const { |
||||
|
return levels[priority].size(); |
||||
|
} |
||||
|
|
||||
|
std::size_t size() const { |
||||
|
u64 priorities = used_priorities; |
||||
|
std::size_t size = 0; |
||||
|
while (priorities != 0) { |
||||
|
const u64 current_priority = CountTrailingZeroes64(priorities); |
||||
|
size += levels[current_priority].size(); |
||||
|
priorities &= ~(1ULL << current_priority); |
||||
|
} |
||||
|
return size; |
||||
|
} |
||||
|
|
||||
|
bool empty() const { |
||||
|
return used_priorities == 0; |
||||
|
} |
||||
|
|
||||
|
bool empty(u32 priority) const { |
||||
|
return (used_priorities & (1ULL << priority)) == 0; |
||||
|
} |
||||
|
|
||||
|
u32 highest_priority_set(u32 max_priority = 0) const { |
||||
|
const u64 priorities = |
||||
|
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1)); |
||||
|
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities)); |
||||
|
} |
||||
|
|
||||
|
u32 lowest_priority_set(u32 min_priority = Depth - 1) const { |
||||
|
const u64 priorities = min_priority >= Depth - 1 |
||||
|
? used_priorities |
||||
|
: (used_priorities & ((1ULL << (min_priority + 1)) - 1)); |
||||
|
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities); |
||||
|
} |
||||
|
|
||||
|
const_iterator cbegin(u32 max_prio = 0) const { |
||||
|
const u32 priority = highest_priority_set(max_prio); |
||||
|
return priority == Depth ? cend() |
||||
|
: const_iterator{*this, levels[priority].cbegin(), priority}; |
||||
|
} |
||||
|
const_iterator begin(u32 max_prio = 0) const { |
||||
|
return cbegin(max_prio); |
||||
|
} |
||||
|
iterator begin(u32 max_prio = 0) { |
||||
|
const u32 priority = highest_priority_set(max_prio); |
||||
|
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority}; |
||||
|
} |
||||
|
|
||||
|
const_iterator cend(u32 min_prio = Depth - 1) const { |
||||
|
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1); |
||||
|
} |
||||
|
const_iterator end(u32 min_prio = Depth - 1) const { |
||||
|
return cend(min_prio); |
||||
|
} |
||||
|
iterator end(u32 min_prio = Depth - 1) { |
||||
|
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1); |
||||
|
} |
||||
|
|
||||
|
T& front(u32 max_priority = 0) { |
||||
|
const u32 priority = highest_priority_set(max_priority); |
||||
|
return levels[priority == Depth ? 0 : priority].front(); |
||||
|
} |
||||
|
const T& front(u32 max_priority = 0) const { |
||||
|
const u32 priority = highest_priority_set(max_priority); |
||||
|
return levels[priority == Depth ? 0 : priority].front(); |
||||
|
} |
||||
|
|
||||
|
T back(u32 min_priority = Depth - 1) { |
||||
|
const u32 priority = lowest_priority_set(min_priority); // intended |
||||
|
return levels[priority == Depth ? 63 : priority].back(); |
||||
|
} |
||||
|
const T& back(u32 min_priority = Depth - 1) const { |
||||
|
const u32 priority = lowest_priority_set(min_priority); // intended |
||||
|
return levels[priority == Depth ? 63 : priority].back(); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
using const_list_iterator = typename std::list<T>::const_iterator; |
||||
|
|
||||
|
static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) { |
||||
|
if (shift >= list.size()) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const auto begin_range = list.begin(); |
||||
|
const auto end_range = std::next(begin_range, shift); |
||||
|
list.splice(list.end(), list, begin_range, end_range); |
||||
|
} |
||||
|
|
||||
|
static void ListSplice(std::list<T>& in_list, const_list_iterator position, |
||||
|
std::list<T>& out_list, const_list_iterator element) { |
||||
|
in_list.splice(position, out_list, element); |
||||
|
} |
||||
|
|
||||
|
static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) { |
||||
|
auto it = list.cbegin(); |
||||
|
while (it != list.cend() && *it != element) { |
||||
|
++it; |
||||
|
} |
||||
|
return it; |
||||
|
} |
||||
|
|
||||
|
std::array<std::list<T>, Depth> levels; |
||||
|
u64 used_priorities = 0; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Common |
||||
@ -0,0 +1,23 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <catch2/catch.hpp>
|
||||
|
#include <math.h>
|
||||
|
#include "common/bit_util.h"
|
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") { |
||||
|
REQUIRE(Common::CountTrailingZeroes32(0) == 32); |
||||
|
REQUIRE(Common::CountTrailingZeroes64(0) == 64); |
||||
|
REQUIRE(Common::CountTrailingZeroes32(9) == 0); |
||||
|
REQUIRE(Common::CountTrailingZeroes32(8) == 3); |
||||
|
REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12); |
||||
|
REQUIRE(Common::CountTrailingZeroes64(9) == 0); |
||||
|
REQUIRE(Common::CountTrailingZeroes64(8) == 3); |
||||
|
REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12); |
||||
|
REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36); |
||||
|
} |
||||
|
|
||||
|
} // namespace Common
|
||||
@ -0,0 +1,55 @@ |
|||||
|
// Copyright 2019 Yuzu Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <catch2/catch.hpp>
|
||||
|
#include <math.h>
|
||||
|
#include "common/common_types.h"
|
||||
|
#include "common/multi_level_queue.h"
|
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
TEST_CASE("MultiLevelQueue", "[common]") { |
||||
|
std::array<f32, 8> values = {0.0, 5.0, 1.0, 9.0, 8.0, 2.0, 6.0, 7.0}; |
||||
|
Common::MultiLevelQueue<f32, 64> mlq; |
||||
|
REQUIRE(mlq.empty()); |
||||
|
mlq.add(values[2], 2); |
||||
|
mlq.add(values[7], 7); |
||||
|
mlq.add(values[3], 3); |
||||
|
mlq.add(values[4], 4); |
||||
|
mlq.add(values[0], 0); |
||||
|
mlq.add(values[5], 5); |
||||
|
mlq.add(values[6], 6); |
||||
|
mlq.add(values[1], 1); |
||||
|
u32 index = 0; |
||||
|
bool all_set = true; |
||||
|
for (auto& f : mlq) { |
||||
|
all_set &= (f == values[index]); |
||||
|
index++; |
||||
|
} |
||||
|
REQUIRE(all_set); |
||||
|
REQUIRE(!mlq.empty()); |
||||
|
f32 v = 8.0; |
||||
|
mlq.add(v, 2); |
||||
|
v = -7.0; |
||||
|
mlq.add(v, 2, false); |
||||
|
REQUIRE(mlq.front(2) == -7.0); |
||||
|
mlq.yield(2); |
||||
|
REQUIRE(mlq.front(2) == values[2]); |
||||
|
REQUIRE(mlq.back(2) == -7.0); |
||||
|
REQUIRE(mlq.empty(8)); |
||||
|
v = 10.0; |
||||
|
mlq.add(v, 8); |
||||
|
mlq.adjust(v, 8, 9); |
||||
|
REQUIRE(mlq.front(9) == v); |
||||
|
REQUIRE(mlq.empty(8)); |
||||
|
REQUIRE(!mlq.empty(9)); |
||||
|
mlq.adjust(values[0], 0, 9); |
||||
|
REQUIRE(mlq.highest_priority_set() == 1); |
||||
|
REQUIRE(mlq.lowest_priority_set() == 9); |
||||
|
mlq.remove(values[1], 1); |
||||
|
REQUIRE(mlq.highest_priority_set() == 2); |
||||
|
REQUIRE(mlq.empty(1)); |
||||
|
} |
||||
|
|
||||
|
} // namespace Common
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue