data_structures_test.cpp 7.36 KB
Newer Older
1 2 3 4 5
#include <catch.hpp>

#include <pls/internal/base/system_details.h>

#include <pls/internal/data_structures/aligned_stack.h>
6
#include <pls/internal/data_structures/locking_deque.h>
7
#include <pls/internal/data_structures/work_stealing_deque.h>
8 9 10 11 12 13 14 15

#include <vector>
#include <mutex>

using namespace pls::internal::data_structures;
using namespace pls::internal::base;
using namespace std;

16 17 18 19 20 21 22 23 24 25
TEST_CASE("aligned stack stores objects correctly", "[internal/data_structures/aligned_stack.h]") {
  constexpr long data_size = 1024;
  char data[data_size];
  aligned_stack stack{data, data_size};

  SECTION("stack correctly pushes sub linesize objects") {
    std::array<char, 5> small_data_one{'a', 'b', 'c', 'd', 'e'};
    std::array<char, 64> small_data_two{};
    std::array<char, 1> small_data_three{'A'};

26 27 28
    auto pointer_one = stack.push<decltype(small_data_one)>(small_data_one);
    auto pointer_two = stack.push<decltype(small_data_two)>(small_data_two);
    auto pointer_three = stack.push<decltype(small_data_three)>(small_data_three);
29 30 31 32 33 34 35 36 37 38

    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_one) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_two) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_three) % system_details::CACHE_LINE_SIZE == 0);
  }

  SECTION("stack correctly pushes above linesize objects") {
    std::array<char, 5> small_data_one{'a', 'b', 'c', 'd', 'e'};
    std::array<char, system_details::CACHE_LINE_SIZE + 10> big_data_one{};

39 40
    auto big_pointer_one = stack.push<decltype(big_data_one)>(big_data_one);
    auto small_pointer_one = stack.push<decltype(small_data_one)>(small_data_one);
41 42 43 44 45 46 47 48

    REQUIRE(reinterpret_cast<std::uintptr_t>(big_pointer_one) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(small_pointer_one) % system_details::CACHE_LINE_SIZE == 0);
  }

  SECTION("stack correctly stores and retrieves objects") {
    std::array<char, 5> data_one{'a', 'b', 'c', 'd', 'e'};

49
    stack.push<decltype(data_one)>(data_one);
50 51 52 53 54 55 56 57 58 59
    auto retrieved_data = stack.pop<std::array<char, 5>>();

    REQUIRE(retrieved_data == std::array<char, 5>{'a', 'b', 'c', 'd', 'e'});
  }

  SECTION("stack can push and pop multiple times with correct alignment") {
    std::array<char, 5> small_data_one{'a', 'b', 'c', 'd', 'e'};
    std::array<char, 64> small_data_two{};
    std::array<char, 1> small_data_three{'A'};

60 61 62 63 64 65 66
    auto pointer_one = stack.push<decltype(small_data_one)>(small_data_one);
    auto pointer_two = stack.push<decltype(small_data_two)>(small_data_two);
    auto pointer_three = stack.push<decltype(small_data_three)>(small_data_three);
    stack.pop<decltype(small_data_three)>();
    stack.pop<decltype(small_data_two)>();
    auto pointer_four = stack.push<decltype(small_data_two)>(small_data_two);
    auto pointer_five = stack.push<decltype(small_data_three)>(small_data_three);
67 68 69 70 71 72 73 74 75 76

    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_one) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_two) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_three) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_four) % system_details::CACHE_LINE_SIZE == 0);
    REQUIRE(reinterpret_cast<std::uintptr_t>(pointer_five) % system_details::CACHE_LINE_SIZE == 0);

    REQUIRE(pointer_four == pointer_two);
    REQUIRE(pointer_five == pointer_three);
  }
77 78
}

79
TEST_CASE("deque stores objects correctly", "[internal/data_structures/deque.h]") {
80
  class my_item {
81

82
  };
83

84 85 86 87 88 89
  constexpr long data_size = 2 << 14;
  char data[data_size];
  aligned_stack stack{data, data_size};

  locking_deque<int> deque{&stack};
  int one = 1, two = 2, three = 3;
90

91
  SECTION("add and remove items form the tail") {
92 93 94
    deque.push_tail(one);
    deque.push_tail(two);
    deque.push_tail(three);
95

96 97 98
    REQUIRE(*deque.pop_tail() == three);
    REQUIRE(*deque.pop_tail() == two);
    REQUIRE(*deque.pop_tail() == one);
99
  }
100

101
  SECTION("handles getting empty by popping the tail correctly") {
102 103
    deque.push_tail(one);
    REQUIRE(*deque.pop_tail() == one);
104

105 106
    deque.push_tail(two);
    REQUIRE(*deque.pop_tail() == two);
107
  }
108

109
  SECTION("remove items form the head") {
110 111 112
    deque.push_tail(one);
    deque.push_tail(two);
    deque.push_tail(three);
113

114 115 116
    REQUIRE(*deque.pop_head() == one);
    REQUIRE(*deque.pop_head() == two);
    REQUIRE(*deque.pop_head() == three);
117
  }
118

119
  SECTION("handles getting empty by popping the head correctly") {
120 121
    deque.push_tail(one);
    REQUIRE(*deque.pop_head() == one);
122

123 124
    deque.push_tail(two);
    REQUIRE(*deque.pop_head() == two);
125
  }
126

127
  SECTION("handles getting empty by popping the head and tail correctly") {
128 129
    deque.push_tail(one);
    REQUIRE(*deque.pop_tail() == one);
130

131 132
    deque.push_tail(two);
    REQUIRE(*deque.pop_head() == two);
133

134 135
    deque.push_tail(three);
    REQUIRE(*deque.pop_tail() == three);
136
  }
137
}
138 139 140 141 142 143 144 145

TEST_CASE("work stealing deque stores objects correctly", "[internal/data_structures/aligned_stack.h]") {
  constexpr long data_size = 2 << 14;
  char data[data_size];
  aligned_stack stack{data, data_size};
  work_stealing_deque<int> deque{&stack};

  int one = 1, two = 2, three = 3, four = 4;
146
  auto no_op = [](int *) {}; // Empty-Callback
147 148

  SECTION("add and remove items form the tail") {
149 150 151
    deque.push_tail<int>(no_op, one);
    deque.push_tail<int>(no_op, two);
    deque.push_tail<int>(no_op, three);
152 153 154 155 156 157 158

    REQUIRE(*deque.pop_tail() == three);
    REQUIRE(*deque.pop_tail() == two);
    REQUIRE(*deque.pop_tail() == one);
  }

  SECTION("handles getting empty by popping the tail correctly") {
159
    deque.push_tail<int>(no_op, one);
160 161
    REQUIRE(*deque.pop_tail() == one);

162
    deque.push_tail<int>(no_op, two);
163 164 165 166
    REQUIRE(*deque.pop_tail() == two);
  }

  SECTION("remove items form the head") {
167 168 169
    deque.push_tail<int>(no_op, one);
    deque.push_tail<int>(no_op, two);
    deque.push_tail<int>(no_op, three);
170 171 172 173 174 175 176

    REQUIRE(*deque.pop_head() == one);
    REQUIRE(*deque.pop_head() == two);
    REQUIRE(*deque.pop_head() == three);
  }

  SECTION("handles getting empty by popping the head correctly") {
177
    deque.push_tail<int>(no_op, one);
178 179
    REQUIRE(*deque.pop_head() == one);

180
    deque.push_tail<int>(no_op, two);
181 182 183 184
    REQUIRE(*deque.pop_head() == two);
  }

  SECTION("handles getting empty by popping the head and tail correctly") {
185
    deque.push_tail<int>(no_op, one);
186 187
    REQUIRE(*deque.pop_tail() == one);

188
    deque.push_tail<int>(no_op, two);
189 190
    REQUIRE(*deque.pop_head() == two);

191
    deque.push_tail<int>(no_op, three);
192 193 194 195
    REQUIRE(*deque.pop_tail() == three);
  }

  SECTION("handles jumps bigger 1 correctly") {
196 197
    deque.push_tail<int>(no_op, one);
    deque.push_tail<int>(no_op, two);
198 199
    REQUIRE(*deque.pop_tail() == two);

200 201
    deque.push_tail<int>(no_op, three);
    deque.push_tail<int>(no_op, four);
202 203 204 205 206 207
    REQUIRE(*deque.pop_head() == one);
    REQUIRE(*deque.pop_head() == three);
    REQUIRE(*deque.pop_head() == four);
  }

  SECTION("handles stack reset 1 correctly when emptied by tail") {
208
    deque.push_tail<int>(no_op, one);
209
    auto state = deque.save_state();
210
    deque.push_tail<int>(no_op, two);
211
    REQUIRE(*deque.pop_tail() == two);
212

213
    deque.release_memory_until(state);
214 215
    REQUIRE(*deque.pop_tail() == one);

216 217
    deque.push_tail<int>(no_op, three);
    deque.push_tail<int>(no_op, four);
218 219 220 221
    REQUIRE(*deque.pop_head() == three);
    REQUIRE(*deque.pop_tail() == four);
  }
}