]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/common/test_prioritized_queue.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / test / common / test_prioritized_queue.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "gtest/gtest.h"
5#include "common/PrioritizedQueue.h"
6
7#include <numeric>
8#include <vector>
9#include <algorithm>
10
11using std::vector;
12
13class PrioritizedQueueTest : public testing::Test
14{
15protected:
16 typedef int Klass;
17 typedef unsigned Item;
18 typedef PrioritizedQueue<Item, Klass> PQ;
19 enum { item_size = 100, };
20 vector<Item> items;
21
22 void SetUp() override {
23 for (int i = 0; i < item_size; i++) {
24 items.push_back(Item(i));
25 }
26 std::random_shuffle(items.begin(), items.end());
27 }
28 void TearDown() override {
29 items.clear();
30 }
31};
32
33TEST_F(PrioritizedQueueTest, capacity) {
34 const unsigned min_cost = 10;
35 const unsigned max_tokens_per_subqueue = 50;
36 PQ pq(max_tokens_per_subqueue, min_cost);
37 EXPECT_TRUE(pq.empty());
38 EXPECT_EQ(0u, pq.length());
39
40 pq.enqueue_strict(Klass(1), 0, Item(0));
41 EXPECT_FALSE(pq.empty());
42 EXPECT_EQ(1u, pq.length());
43
44 for (int i = 0; i < 3; i++) {
45 pq.enqueue(Klass(1), 0, 10, Item(0));
46 }
47 for (unsigned i = 4; i > 0; i--) {
48 EXPECT_FALSE(pq.empty());
49 EXPECT_EQ(i, pq.length());
50 pq.dequeue();
51 }
52 EXPECT_TRUE(pq.empty());
53 EXPECT_EQ(0u, pq.length());
54}
55
56TEST_F(PrioritizedQueueTest, strict_pq) {
57 const unsigned min_cost = 1;
58 const unsigned max_tokens_per_subqueue = 50;
59 PQ pq(max_tokens_per_subqueue, min_cost);
60 // 0 .. item_size-1
61 for (unsigned i = 0; i < item_size; i++) {
62 unsigned priority = items[i];
63 pq.enqueue_strict(Klass(0), priority, items[i]);
64 }
65 // item_size-1 .. 0
66 for (unsigned i = item_size; i > 0; i--) {
67 Item item = pq.dequeue();
68 unsigned priority = item;
69 EXPECT_EQ(i - 1, priority);
70 }
71}
72
73TEST_F(PrioritizedQueueTest, lowest_among_eligible_otherwise_highest) {
74 // to minimize the effect of `distribute_tokens()`
75 // all eligible items will be assigned with cost of min_cost
76 const unsigned min_cost = 0;
77 const unsigned max_tokens_per_subqueue = 100;
78 PQ pq(max_tokens_per_subqueue, min_cost);
79
80#define ITEM_TO_COST(item_) (item_ % 5 ? min_cost : max_tokens_per_subqueue)
81 unsigned num_low_cost = 0, num_high_cost = 0;
82 for (int i = 0; i < item_size; i++) {
83 const Item& item = items[i];
84 unsigned cost = ITEM_TO_COST(item);
85 unsigned priority = item;
86 if (cost == min_cost) {
87 num_low_cost++;
88 } else {
89 num_high_cost++;
90 }
91 pq.enqueue(Klass(0), priority, cost, item);
92 }
93 // the token in all buckets is 0 at the beginning, so dequeue() should pick
94 // the first one with the highest priority.
95 unsigned highest_priority;
96 {
97 Item item = pq.dequeue();
98 unsigned cost = ITEM_TO_COST(item);
99 unsigned priority = item;
100 if (cost == min_cost) {
101 num_low_cost--;
102 } else {
103 num_high_cost--;
104 }
105 EXPECT_EQ(item_size - 1u, priority);
106 highest_priority = priority;
107 }
108 unsigned lowest_priority = 0;
109 for (unsigned i = 0; i < num_low_cost; i++) {
110 Item item = pq.dequeue();
111 unsigned cost = ITEM_TO_COST(item);
112 unsigned priority = item;
113 EXPECT_EQ(min_cost, cost);
114 EXPECT_GT(priority, lowest_priority);
115 lowest_priority = priority;
116 }
117 for (unsigned i = 0; i < num_high_cost; i++) {
118 Item item = pq.dequeue();
119 unsigned cost = ITEM_TO_COST(item);
120 unsigned priority = item;
121 EXPECT_EQ(max_tokens_per_subqueue, cost);
122 EXPECT_LT(priority, highest_priority);
123 highest_priority = priority;
124 }
125#undef ITEM_TO_COST
126}
127
128static const unsigned num_classes = 4;
129// just a determinitic number
130#define ITEM_TO_CLASS(item_) Klass((item_ + 43) % num_classes)
131
132TEST_F(PrioritizedQueueTest, fairness_by_class) {
133 // dequeue should be fair to all classes in a certain bucket
134 const unsigned min_cost = 1;
135 const unsigned max_tokens_per_subqueue = 50;
136 PQ pq(max_tokens_per_subqueue, min_cost);
137
138 for (int i = 0; i < item_size; i++) {
139 const Item& item = items[i];
140 Klass k = ITEM_TO_CLASS(item);
141 unsigned priority = 0;
142 unsigned cost = 1;
143 pq.enqueue(k, priority, cost, item);
144 }
145 // just sample first 1/2 of the items
146 // if i pick too small a dataset, the result won't be statisitcally
147 // significant. if the sample dataset is too large, it will converge to the
148 // distribution of the full set.
149 vector<unsigned> num_picked_in_class(num_classes, 0u);
150 for (int i = 0; i < item_size / 2; i++) {
151 Item item = pq.dequeue();
152 Klass k = ITEM_TO_CLASS(item);
153 num_picked_in_class[k]++;
154 }
155 unsigned total = std::accumulate(num_picked_in_class.begin(),
156 num_picked_in_class.end(),
157 0);
158 float avg = float(total) / num_classes;
159 for (unsigned i = 0; i < num_classes; i++) {
160 EXPECT_NEAR(avg, num_picked_in_class[i], 0.5);
161 }
162}
163
164template <typename T>
165struct Greater {
166 const T rhs;
167 std::list<T> *removed;
168 explicit Greater(const T& v, std::list<T> *removed) : rhs(v), removed(removed)
169 {}
170 bool operator()(const T& lhs) {
171 if (lhs > rhs) {
172 if (removed)
173 removed->push_back(lhs);
174 return true;
175 } else {
176 return false;
177 }
178 }
179};
180
181TEST_F(PrioritizedQueueTest, remove_by_filter) {
182 const unsigned min_cost = 1;
183 const unsigned max_tokens_per_subqueue = 50;
184 PQ pq(max_tokens_per_subqueue, min_cost);
185
186 Greater<Item> pred(item_size/2, nullptr);
187 unsigned num_to_remove = 0;
188 for (unsigned i = 0; i < item_size; i++) {
189 const Item& item = items[i];
190 pq.enqueue(Klass(1), 0, 10, item);
191 if (pred(item)) {
192 num_to_remove++;
193 }
194 }
195 std::list<Item> removed;
196 Greater<Item> pred2(item_size/2, &removed);
197 pq.remove_by_filter(pred2);
198
199 // see if the removed items are expected ones.
200 for (std::list<Item>::iterator it = removed.begin();
201 it != removed.end();
202 ++it) {
203 const Item& item = *it;
204 EXPECT_TRUE(pred(item));
205 items.erase(remove(items.begin(), items.end(), item), items.end());
206 }
207 EXPECT_EQ(num_to_remove, removed.size());
208 EXPECT_EQ(item_size - num_to_remove, pq.length());
209 EXPECT_EQ(item_size - num_to_remove, items.size());
210 // see if the remainder are expeceted also.
211 while (!pq.empty()) {
212 const Item item = pq.dequeue();
213 EXPECT_FALSE(pred(item));
214 items.erase(remove(items.begin(), items.end(), item), items.end());
215 }
216 EXPECT_TRUE(items.empty());
217}
218
219TEST_F(PrioritizedQueueTest, remove_by_class) {
220 const unsigned min_cost = 1;
221 const unsigned max_tokens_per_subqueue = 50;
222 PQ pq(max_tokens_per_subqueue, min_cost);
223 const Klass class_to_remove(2);
224 unsigned num_to_remove = 0;
225 for (int i = 0; i < item_size; i++) {
226 const Item& item = items[i];
227 Klass k = ITEM_TO_CLASS(item);
228 pq.enqueue(k, 0, 0, item);
229 if (k == class_to_remove) {
230 num_to_remove++;
231 }
232 }
233 std::list<Item> removed;
234 pq.remove_by_class(class_to_remove, &removed);
235
236 // see if the removed items are expected ones.
237 for (std::list<Item>::iterator it = removed.begin();
238 it != removed.end();
239 ++it) {
240 const Item& item = *it;
241 Klass k = ITEM_TO_CLASS(item);
242 EXPECT_EQ(class_to_remove, k);
243 items.erase(remove(items.begin(), items.end(), item), items.end());
244 }
245 EXPECT_EQ(num_to_remove, removed.size());
246 EXPECT_EQ(item_size - num_to_remove, pq.length());
247 EXPECT_EQ(item_size - num_to_remove, items.size());
248 // see if the remainder are expeceted also.
249 while (!pq.empty()) {
250 const Item item = pq.dequeue();
251 Klass k = ITEM_TO_CLASS(item);
252 EXPECT_NE(class_to_remove, k);
253 items.erase(remove(items.begin(), items.end(), item), items.end());
254 }
255 EXPECT_TRUE(items.empty());
256}