]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/common/test_weighted_priority_queue.cc
update sources to v12.1.1
[ceph.git] / ceph / src / test / common / test_weighted_priority_queue.cc
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/Formatter.h"
6 #include "common/WeightedPriorityQueue.h"
7
8 #include <numeric>
9 #include <vector>
10 #include <map>
11 #include <list>
12 #include <tuple>
13
14 #define CEPH_OP_CLASS_STRICT 0
15 #define CEPH_OP_CLASS_NORMAL 0
16 #define CEPH_OP_QUEUE_BACK 0
17 #define CEPH_OP_QUEUE_FRONT 0
18
19 class WeightedPriorityQueueTest : public testing::Test
20 {
21 protected:
22 typedef unsigned Klass;
23 // tuple<Prio, Klass, OpID> so that we can verfiy the op
24 typedef std::tuple<unsigned, unsigned, unsigned> Item;
25 typedef unsigned Prio;
26 typedef unsigned Kost;
27 typedef WeightedPriorityQueue<Item, Klass> WQ;
28 // Simulate queue structure
29 typedef std::list<std::pair<Kost, Item> > ItemList;
30 typedef std::map<Klass, ItemList> KlassItem;
31 typedef std::map<Prio, KlassItem> LQ;
32 typedef std::list<Item> Removed;
33 const unsigned max_prios = 5; // (0-4) * 64
34 const unsigned klasses = 37; // Make prime to help get good coverage
35
36 void fill_queue(WQ &wq, LQ &strictq, LQ &normq,
37 unsigned item_size, bool randomize = false) {
38 unsigned p, k, c, o, op_queue, fob;
39 for (unsigned i = 1; i <= item_size; ++i) {
40 // Choose priority, class, cost and 'op' for this op.
41 if (randomize) {
42 p = (rand() % max_prios) * 64;
43 k = rand() % klasses;
44 c = rand() % (1<<22); // 4M cost
45 // Make some of the costs 0, but make sure small costs
46 // still work ok.
47 if (c > (1<<19) && c < (1<<20)) {
48 c = 0;
49 }
50 op_queue = rand() % 10;
51 fob = rand() % 10;
52 } else {
53 p = (i % max_prios) * 64;
54 k = i % klasses;
55 c = (i % 8 == 0 || i % 16 == 0) ? 0 : 1 << (i % 23);
56 op_queue = i % 7; // Use prime numbers to
57 fob = i % 11; // get better coverage
58 }
59 o = rand() % (1<<16);
60 // Choose how to enqueue this op.
61 switch (op_queue) {
62 case 6 :
63 // Strict Queue
64 if (fob == 4) {
65 // Queue to the front.
66 strictq[p][k].push_front(std::make_pair(
67 c, std::make_tuple(p, k, o)));
68 wq.enqueue_strict_front(Klass(k), p, std::make_tuple(p, k, o));
69 } else {
70 //Queue to the back.
71 strictq[p][k].push_back(std::make_pair(
72 c, std::make_tuple(p, k, o)));
73 wq.enqueue_strict(Klass(k), p, std::make_tuple(p, k, o));
74 }
75 break;
76 default:
77 // Normal queue
78 if (fob == 4) {
79 // Queue to the front.
80 normq[p][k].push_front(std::make_pair(
81 c, std::make_tuple(p, k, o)));
82 wq.enqueue_front(Klass(k), p, c, std::make_tuple(p, k, o));
83 } else {
84 //Queue to the back.
85 normq[p][k].push_back(std::make_pair(
86 c, std::make_tuple(p, k, o)));
87 wq.enqueue(Klass(k), p, c, std::make_tuple(p, k, o));
88 }
89 break;
90 }
91 }
92 }
93 void test_queue(unsigned item_size, bool randomize = false) {
94 // Due to the WRR queue having a lot of probabilistic logic
95 // we can't determine the exact order OPs will be dequeued.
96 // However, the queue should not dequeue a priority out of
97 // order. It should also dequeue the strict priority queue
98 // first and in order. In both the strict and normal queues
99 // push front and back should be respected. Here we keep
100 // track of the ops queued and make sure they dequeue
101 // correctly.
102
103 // Set up local tracking queues
104 WQ wq(0, 0);
105 LQ strictq, normq;
106 fill_queue(wq, strictq, normq, item_size, randomize);
107 // Test that the queue is dequeuing properly.
108 typedef std::map<unsigned, unsigned> LastKlass;
109 LastKlass last_strict, last_norm;
110 while (!(wq.empty())) {
111 Item r = wq.dequeue();
112 if (!(strictq.empty())) {
113 // Check that there are no higher priorities
114 // in the strict queue.
115 LQ::reverse_iterator ri = strictq.rbegin();
116 EXPECT_EQ(std::get<0>(r), ri->first);
117 // Check that if there are multiple classes in a priority
118 // that it is not dequeueing the same class each time.
119 LastKlass::iterator si = last_strict.find(std::get<0>(r));
120 if (strictq[std::get<0>(r)].size() > 1 && si != last_strict.end()) {
121 EXPECT_NE(std::get<1>(r), si->second);
122 }
123 last_strict[std::get<0>(r)] = std::get<1>(r);
124
125 Item t = strictq[std::get<0>(r)][std::get<1>(r)].front().second;
126 EXPECT_EQ(std::get<2>(r), std::get<2>(t));
127 strictq[std::get<0>(r)][std::get<1>(r)].pop_front();
128 if (strictq[std::get<0>(r)][std::get<1>(r)].empty()) {
129 strictq[std::get<0>(r)].erase(std::get<1>(r));
130 }
131 if (strictq[std::get<0>(r)].empty()) {
132 strictq.erase(std::get<0>(r));
133 }
134 } else {
135 // Check that if there are multiple classes in a priority
136 // that it is not dequeueing the same class each time.
137 LastKlass::iterator si = last_norm.find(std::get<0>(r));
138 if (normq[std::get<0>(r)].size() > 1 && si != last_norm.end()) {
139 EXPECT_NE(std::get<1>(r), si->second);
140 }
141 last_norm[std::get<0>(r)] = std::get<1>(r);
142
143 Item t = normq[std::get<0>(r)][std::get<1>(r)].front().second;
144 EXPECT_EQ(std::get<2>(r), std::get<2>(t));
145 normq[std::get<0>(r)][std::get<1>(r)].pop_front();
146 if (normq[std::get<0>(r)][std::get<1>(r)].empty()) {
147 normq[std::get<0>(r)].erase(std::get<1>(r));
148 }
149 if (normq[std::get<0>(r)].empty()) {
150 normq.erase(std::get<0>(r));
151 }
152 }
153 }
154 }
155
156 void SetUp() override {
157 srand(time(0));
158 }
159 void TearDown() override {
160 }
161 };
162
163 TEST_F(WeightedPriorityQueueTest, wpq_size){
164 WQ wq(0, 0);
165 EXPECT_TRUE(wq.empty());
166 EXPECT_EQ(0u, wq.length());
167
168 // Test the strict queue size.
169 for (unsigned i = 1; i < 5; ++i) {
170 wq.enqueue_strict(Klass(i),i, std::make_tuple(i, i, i));
171 EXPECT_FALSE(wq.empty());
172 EXPECT_EQ(i, wq.length());
173 }
174 // Test the normal queue size.
175 for (unsigned i = 5; i < 10; ++i) {
176 wq.enqueue(Klass(i), i, i, std::make_tuple(i, i, i));
177 EXPECT_FALSE(wq.empty());
178 EXPECT_EQ(i, wq.length());
179 }
180 // Test that as both queues are emptied
181 // the size is correct.
182 for (unsigned i = 8; i >0; --i) {
183 wq.dequeue();
184 EXPECT_FALSE(wq.empty());
185 EXPECT_EQ(i, wq.length());
186 }
187 wq.dequeue();
188 EXPECT_TRUE(wq.empty());
189 EXPECT_EQ(0u, wq.length());
190 }
191
192 TEST_F(WeightedPriorityQueueTest, wpq_test_static) {
193 test_queue(1000);
194 }
195
196 TEST_F(WeightedPriorityQueueTest, wpq_test_random) {
197 test_queue(rand() % 500 + 500, true);
198 }
199
200 TEST_F(WeightedPriorityQueueTest, wpq_test_remove_by_class_null) {
201 WQ wq(0, 0);
202 LQ strictq, normq;
203 unsigned num_items = 10;
204 fill_queue(wq, strictq, normq, num_items);
205 Removed wq_removed;
206 // Pick a klass that was not enqueued
207 wq.remove_by_class(klasses + 1, &wq_removed);
208 EXPECT_EQ(0u, wq_removed.size());
209 }
210
211 TEST_F(WeightedPriorityQueueTest, wpq_test_remove_by_class) {
212 WQ wq(0, 0);
213 LQ strictq, normq;
214 unsigned num_items = 1000;
215 fill_queue(wq, strictq, normq, num_items);
216 unsigned num_to_remove = 0;
217 const Klass k = 5;
218 // Find how many ops are in the class
219 for (LQ::iterator it = strictq.begin();
220 it != strictq.end(); ++it) {
221 num_to_remove += it->second[k].size();
222 }
223 for (LQ::iterator it = normq.begin();
224 it != normq.end(); ++it) {
225 num_to_remove += it->second[k].size();
226 }
227 Removed wq_removed;
228 wq.remove_by_class(k, &wq_removed);
229 // Check that the right ops were removed.
230 EXPECT_EQ(num_to_remove, wq_removed.size());
231 EXPECT_EQ(num_items - num_to_remove, wq.length());
232 for (Removed::iterator it = wq_removed.begin();
233 it != wq_removed.end(); ++it) {
234 EXPECT_EQ(k, std::get<1>(*it));
235 }
236 // Check that none were missed
237 while (!(wq.empty())) {
238 EXPECT_NE(k, std::get<1>(wq.dequeue()));
239 }
240 }