msgQ test: Test performance along two axes...
[charm.git] / tests / charm++ / queue / pgm.C
1 #include <iostream>
2 #include <cstdlib>
3 #include <cstdio>
4 #include <vector>
5
6 using std::cerr;
7 using std::endl;
8 using std::sprintf;
9
10 #include "queueing.h"
11 #include "msgq.h"
12 #include "main.decl.h"
13
14 #define CmiFree free
15
16 #define RUN_TEST(f) do { \
17   ++tests; \
18   if (f()) { \
19     ++success; \
20   } else { \
21     ++fail; \
22     cerr << "Test \"" #f "\" failed" << endl; \
23   } \
24 } while (0)
25
26 // A newly created Queue should be empty, which corresponds to a
27 // length of 0
28 bool test_empty()
29 {
30   Queue q = CqsCreate();
31   bool result = (0 == CqsLength(q)) && (1 == CqsEmpty(q));
32   CqsDelete(q);
33   return result;
34 }
35
36 // Enqueueing an element should show that there is an element
37 // present. We should get the same thing back when we dequeue
38 //
39 // The queue is not allowed to dereference the void* we give it
40 bool test_one()
41 {
42   Queue q = CqsCreate();
43   void *p = 0;
44   CqsEnqueue(q, p);
45   bool result = (1 == CqsLength(q)) && (0 == CqsEmpty(q));
46   void *r;
47   CqsDequeue(q, &r);
48   result &= (r == p) 
49     && (0 == CqsLength(q)) 
50     && (1 == CqsEmpty(q));
51   CqsDelete(q);
52   return result;
53 }
54
55 // Put two in, see if we get the same two back. Order unspecified
56 bool test_two()
57 {
58   Queue q = CqsCreate();
59   void *i = 0, *j = (char *)1;
60   void *r, *s;
61   CqsEnqueue(q, i);
62   CqsEnqueue(q, j);
63   bool result = (2 == CqsLength(q));
64   CqsDequeue(q, &r);
65   CqsDequeue(q, &s);
66   result &= (r == i && s == j) || (r == j && s == i);
67   result &= 1 == CqsEmpty(q);
68   CqsDelete(q);
69   return result;
70 }
71
72 bool test_fifo()
73 {
74   Queue q = CqsCreate();
75   void *i = (char *)1, *j = (char *)2, *k = (char *)3;
76   void *r, *s, *t;
77   CqsEnqueueFifo(q, i);
78   CqsEnqueueFifo(q, j);
79   CqsEnqueueFifo(q, k);
80   CqsDequeue(q, &r);
81   CqsDequeue(q, &s);
82   CqsDequeue(q, &t);
83   bool result = (r == i) && (s == j) && (t == k);
84   CqsDelete(q);
85   return result;
86 }
87
88 bool test_lifo()
89 {
90   Queue q = CqsCreate();
91   void *i = (char *)1, *j = (char *)2, *k = (char *)3;
92   void *r, *s, *t;
93   CqsEnqueueLifo(q, i);
94   CqsEnqueueLifo(q, j);
95   CqsEnqueueLifo(q, k);
96   CqsDequeue(q, &r);
97   CqsDequeue(q, &s);
98   CqsDequeue(q, &t);
99   bool result = (r == k) && (s == j) && (t == i);
100   CqsDelete(q);
101   return result;
102 }
103
104 bool test_enqueue_mixed()
105 {
106   Queue q = CqsCreate();
107   void *i = (char *)1, *j = (char *)2, *k = (char *)3;
108   void *r, *s, *t;
109   CqsEnqueueFifo(q, i);
110   CqsEnqueueFifo(q, j);
111   CqsEnqueueLifo(q, k);
112   CqsDequeue(q, &r);
113   CqsDequeue(q, &s);
114   CqsDequeue(q, &t);
115   bool result = (r == k) && (s == i) && (t == j);
116   CqsDelete(q);
117   return result;
118 }
119
120 static bool findEntry(void ** const e, int num, void * const t)
121 {
122     for (int i = 0; i < num; ++i)
123     {
124         if (e[i] == t)
125             return true;
126     }
127     return false;
128 }
129
130 bool test_enumerate()
131 {
132   Queue q = CqsCreate();
133   void *i = (char *)1, *j = (char *)2, *k = (char *)3;
134   void **e;
135   CqsEnqueueFifo(q, i);
136   CqsEnqueueFifo(q, j);
137   CqsEnqueueLifo(q, k);
138   CqsEnumerateQueue(q, &e);
139   int n = CqsLength(q);
140   bool result = findEntry(e, n, i) && findEntry(e, n, j) && findEntry(e, n, k);
141   CmiFree(e);
142   CqsDelete(q);
143   return result;
144 }
145
146 bool test_general_fifo()
147 {
148   Queue q = CqsCreate();
149   void *i = (char *)1, *j = (char *)2, *k = (char *)3;
150   CqsEnqueueGeneral(q, i, CQS_QUEUEING_FIFO, 1, 0);
151   CqsEnqueueGeneral(q, j, CQS_QUEUEING_FIFO, 2, 0);
152   CqsEnqueueGeneral(q, k, CQS_QUEUEING_FIFO, 42, 0);
153   void *r, *s, *t;
154   CqsDequeue(q, &r);
155   CqsDequeue(q, &s);
156   CqsDequeue(q, &t);
157   bool result = (r == i) && (s == j) && (t == k);
158   CqsDelete(q);
159   return result;
160 }
161
162 bool test_general_ififo()
163 {
164   Queue q = CqsCreate();
165   void *i = (char *)1, *j = (char *)2, *k = (char *)3;
166   unsigned int a = -1, b = 0, c = 1;
167   CqsEnqueueGeneral(q, i, CQS_QUEUEING_IFIFO, 8*sizeof(int), &c);
168   CqsEnqueueGeneral(q, j, CQS_QUEUEING_IFIFO, 8*sizeof(int), &b);
169   CqsEnqueueGeneral(q, k, CQS_QUEUEING_IFIFO, 8*sizeof(int), &a);
170   void *r, *s, *t;
171   CqsDequeue(q, &r);
172   CqsDequeue(q, &s);
173   CqsDequeue(q, &t);
174   bool result = (r == k) && (s == j) && (t == i);
175   CqsDelete(q);
176   return result;
177 }
178
179
180 const int qSizeMin   = 1<<4;
181 const int qSizeMax   = 1<<12;
182 const int qBatchSize = 1<<4;
183 const int numIters   = 1<<16;
184 const int numMsgs    = 1<<7;
185
186 std::vector<char> msgs(qSizeMax + numMsgs);
187 std::vector<unsigned int> prios(qSizeMax + numMsgs);
188
189
190 double timePerOp_general_ififo(int qBaseSize = 256)
191 {
192   Queue q = CqsCreate();
193
194   for (int i = 0; i < qBaseSize; i++)
195       CqsEnqueueGeneral(q, (void*)&msgs[i], CQS_QUEUEING_IFIFO, 8*sizeof(int), &prios[i]);
196
197   double startTime = CmiWallTimer();
198   for (int i = 0; i < numIters; i++)
199   {
200     for (int strt = qBaseSize; strt < qBaseSize + numMsgs; strt += qBatchSize)
201     {
202       for (int j = strt; j < strt + qBatchSize; j++)
203         CqsEnqueueGeneral(q, (void*)&msgs[j], CQS_QUEUEING_IFIFO, 8*sizeof(int), &prios[j]);
204       void *m;
205       for (int j = 0; j < qBatchSize; j++)
206         CqsDequeue(q, &m);
207     }
208   }
209
210   CqsDelete(q);
211   return 1000000 * (CmiWallTimer() - startTime) / (numIters * numMsgs * 2);
212 }
213
214
215 double timePerOp_stlQ(int qBaseSize = 256)
216 {
217   msgQ<int> q;
218
219   for (int i = 0; i < qBaseSize; i++)
220       q.enq((msg_t*)&msgs[i], prios[i]);
221
222   double startTime = CmiWallTimer();
223   for (int i = 0; i < numIters; i++)
224   {
225     for (int strt = qBaseSize; strt < qBaseSize + numMsgs; strt += qBatchSize)
226     {
227       for (int j = strt; j < strt + qBatchSize; j++)
228         q.enq((msg_t*)&msgs[j], prios[j]);
229       void *m;
230       for (int j = 0; j < qBatchSize; j++)
231         q.deq();
232     }
233   }
234
235   return 1000000 * (CmiWallTimer() - startTime) / (numIters * numMsgs * 2);
236 }
237
238
239 bool perftest_general_ififo()
240 {
241   #if CMK_HAS_STD_UNORDERED_MAP
242   CkPrintf("The STL variant of the msg q is using a std::unordered_map\n");
243   #else
244   CkPrintf("The STL variant of the msg q is using a std::map\n");
245   #endif
246
247   CkPrintf("Reporting time per enqueue / dequeue operation (us) for charm's underlying mixed priority queue\n"
248            "Nprios (row) is the number of different priority values that are used.\n"
249            "Qlen (col) is the base length of the queue on which the enq/deq operations are times\n"
250           );
251
252   CkPrintf("\nversion  Nprios");
253   for (int i = qSizeMin; i <= qSizeMax; i*=2)
254     CkPrintf("%10d", i);
255
256   // Charm applications typically have a small/moderate number of different message priorities
257   for (int hl = 16; hl < 128; hl *=2)
258   {
259     std::srand(42);
260     for (int i = 0; i < qSizeMax + numMsgs; i++)
261       prios[i] = std::rand() % hl;
262
263     CkPrintf("\n  charm %7d", hl);
264     for (int i = qSizeMin; i <= qSizeMax; i *= 2)
265       CkPrintf("%10.4f", i, timePerOp_general_ififo(i));
266     CkPrintf("\n    stl %7d", hl);
267     for (int i = qSizeMin; i <= qSizeMax; i *= 2)
268       CkPrintf("%10.4f", i, timePerOp_stlQ(i));
269   }
270
271   CkPrintf("\n");
272   return true;
273 }
274
275
276 #if 0
277 // Template for new harness-driven tests
278 bool test_foo()
279 {
280   Queue q = CqsCreate();
281   
282   bool result = ;
283   CqsDelete(q);
284   return result;
285 }
286 #endif
287
288 struct main : public CBase_main
289 {
290   main(CkArgMsg *)
291   {
292     int tests = 0, success = 0, fail = 0;
293     char message[100];
294
295     RUN_TEST(test_empty);
296     RUN_TEST(test_one);
297     RUN_TEST(test_two);
298     RUN_TEST(test_fifo);
299     RUN_TEST(test_lifo);
300     RUN_TEST(test_enqueue_mixed);
301     RUN_TEST(test_enumerate);
302     RUN_TEST(test_general_fifo);
303     RUN_TEST(test_general_ififo);
304     RUN_TEST(perftest_general_ififo);
305
306     if (fail) {
307       sprintf(message, "%d/%d tests failed\n", fail, tests);
308       CkAbort(message);
309     }
310     else {
311       CkPrintf("All %d tests passed\n", tests);
312       CkExit();
313     }
314   }
315
316 };
317
318 #include "main.def.h"