91248eb2260a3a56686c0163fd48172c83659c18
[platform/upstream/nspr.git] / nspr / pr / tests / perf.c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nspr.h"
7 #include "plgetopt.h"
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 int _debug_on = 0;
14 #define DPRINTF(arg) if (_debug_on) printf arg
15
16 #include "obsolete/prsem.h"
17
18 PRLock *lock;
19 PRMonitor *mon;
20 PRMonitor *mon2;
21
22 #define DEFAULT_COUNT    1000
23
24 PRInt32 count;
25
26 static void nop(int a, int b, int c)
27 {
28 }
29
30 static void LocalProcedureCall(void)
31 {
32     PRInt32 i;
33
34     for (i = 0; i < count; i++) {
35     nop(i, i, 5);
36     }
37 }
38
39 static void DLLProcedureCall(void)
40 {
41     PRInt32 i;
42         PRThreadState state;
43         PRThread *self = PR_GetCurrentThread();
44
45     for (i = 0; i < count; i++) {
46         state = PR_GetThreadState(self);
47     }
48 }
49
50 static void Now(void)
51 {
52     PRInt32 i;
53     PRTime time;
54
55     for (i = 0; i < count; i++) {
56         time = PR_Now();
57     }
58 }
59
60 static void Interval(void)
61 {
62     PRInt32 i;
63     PRIntervalTime time;
64
65     for (i = 0; i < count; i++) {
66         time = PR_IntervalNow();
67     }
68 }
69
70 static void IdleLock(void)
71 {
72     PRInt32 i;
73
74     for (i = 0; i < count; i++) {
75     PR_Lock(lock);
76     PR_Unlock(lock);
77     }
78 }
79
80 static void IdleMonitor(void)
81 {
82     PRInt32 i;
83
84     for (i = 0; i < count; i++) {
85     PR_EnterMonitor(mon);
86     PR_ExitMonitor(mon);
87     }
88 }
89
90 static void IdleCMonitor(void)
91 {
92     PRInt32 i;
93
94     for (i = 0; i < count; i++) {
95     PR_CEnterMonitor((void*)7);
96     PR_CExitMonitor((void*)7);
97     }
98 }
99
100 /************************************************************************/
101
102 static void PR_CALLBACK dull(void *arg)
103 {
104 }
105
106 static void CDThread(void)
107 {
108     PRInt32 i;
109     int num_threads = count;
110
111     /*
112      * Cannot create too many threads
113      */
114     if (num_threads > 1000)
115     num_threads = 1000;
116
117     for (i = 0; i < num_threads; i++) {
118         PRThread *t = PR_CreateThread(PR_USER_THREAD,
119                       dull, 0, 
120                       PR_PRIORITY_NORMAL,
121                       PR_LOCAL_THREAD,
122                       PR_UNJOINABLE_THREAD,
123                       0);
124         if (NULL == t) {
125             fprintf(stderr, "CDThread: cannot create thread %3d\n", i);
126         } else {
127             DPRINTF(("CDThread: created thread %3d \n",i));
128         }
129         PR_Sleep(0);
130     }
131 }
132
133 static int alive;
134 static int cxq;
135
136 static void PR_CALLBACK CXReader(void *arg)
137 {
138     PRInt32 i, n;
139
140     PR_EnterMonitor(mon);
141     n = count / 2;
142     for (i = 0; i < n; i++) {
143     while (cxq == 0) {
144             DPRINTF(("CXReader: thread = 0x%lx waiting\n",
145                     PR_GetCurrentThread()));
146         PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
147     }
148     --cxq;
149     PR_Notify(mon);
150     }
151     PR_ExitMonitor(mon);
152
153     PR_EnterMonitor(mon2);
154     --alive;
155     PR_Notify(mon2);
156     PR_ExitMonitor(mon2);
157     DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
158 }
159
160 static void PR_CALLBACK CXWriter(void *arg)
161 {
162     PRInt32 i, n;
163
164     PR_EnterMonitor(mon);
165     n = count / 2;
166     for (i = 0; i < n; i++) {
167     while (cxq == 1) {
168             DPRINTF(("CXWriter: thread = 0x%lx waiting\n",
169                     PR_GetCurrentThread()));
170         PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
171     }
172     ++cxq;
173     PR_Notify(mon);
174     }
175     PR_ExitMonitor(mon);
176
177     PR_EnterMonitor(mon2);
178     --alive;
179     PR_Notify(mon2);
180     PR_ExitMonitor(mon2);
181     DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
182 }
183
184 static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
185 {
186     PRThread *t1, *t2;
187
188     PR_EnterMonitor(mon2);
189     alive = 2;
190     cxq = 0;
191
192     t1 = PR_CreateThread(PR_USER_THREAD,
193                       CXReader, 0, 
194                       PR_PRIORITY_NORMAL,
195                       scope1,
196                       PR_UNJOINABLE_THREAD,
197                       0);
198     if (NULL == t1) {
199         fprintf(stderr, "ContextSwitch: cannot create thread\n");
200     } else {
201         DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
202                 (scope1 == PR_GLOBAL_THREAD ?
203                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
204                             t1));
205     }
206     t2 = PR_CreateThread(PR_USER_THREAD,
207                       CXWriter, 0, 
208                       PR_PRIORITY_NORMAL,
209                       scope2,
210                       PR_UNJOINABLE_THREAD,
211                       0);
212     if (NULL == t2) {
213         fprintf(stderr, "ContextSwitch: cannot create thread\n");
214     } else {
215         DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
216                 (scope2 == PR_GLOBAL_THREAD ?
217                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
218                             t2));
219     }
220
221     /* Wait for both of the threads to exit */
222     while (alive) {
223     PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
224     }
225     PR_ExitMonitor(mon2);
226 }
227
228 static void ContextSwitchUU(void)
229 {
230     ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
231 }
232
233 static void ContextSwitchUK(void)
234 {
235     ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
236 }
237
238 static void ContextSwitchKU(void)
239 {
240     ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
241 }
242
243 static void ContextSwitchKK(void)
244 {
245     ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
246 }
247
248 /************************************************************************/
249
250 static void PR_CALLBACK SemaThread(void *argSema)
251 {
252     PRSemaphore **sem = (PRSemaphore **)argSema;
253     PRInt32 i, n;
254
255     n = count / 2;
256     for (i = 0; i < n; i++) {
257         DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n",
258                 PR_GetCurrentThread(), sem[0]));
259         PR_WaitSem(sem[0]);
260         DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n",
261                 PR_GetCurrentThread(), sem[1]));
262         PR_PostSem(sem[1]);
263     }
264
265     PR_EnterMonitor(mon2);
266     --alive;
267     PR_Notify(mon2);
268     PR_ExitMonitor(mon2);
269     DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
270 }
271
272 static  PRSemaphore *sem_set1[2];
273 static  PRSemaphore *sem_set2[2];
274
275 static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
276 {
277     PRThread *t1, *t2;
278     sem_set1[0] = PR_NewSem(1);
279     sem_set1[1] = PR_NewSem(0);
280     sem_set2[0] = sem_set1[1];
281     sem_set2[1] = sem_set1[0];
282
283     PR_EnterMonitor(mon2);
284     alive = 2;
285     cxq = 0;
286
287     t1 = PR_CreateThread(PR_USER_THREAD,
288                       SemaThread, 
289                       sem_set1, 
290                       PR_PRIORITY_NORMAL,
291                       scope1,
292                       PR_UNJOINABLE_THREAD,
293                       0);
294     if (NULL == t1) {
295         fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
296     } else {
297         DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
298                 (scope1 == PR_GLOBAL_THREAD ?
299                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
300                             t1));
301     }
302     t2 = PR_CreateThread(PR_USER_THREAD,
303                       SemaThread, 
304                       sem_set2, 
305                       PR_PRIORITY_NORMAL,
306                       scope2,
307                       PR_UNJOINABLE_THREAD,
308                       0);
309     if (NULL == t2) {
310         fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
311     } else {
312         DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
313                 (scope2 == PR_GLOBAL_THREAD ?
314                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
315                             t2));
316     }
317
318     /* Wait for both of the threads to exit */
319     while (alive) {
320         PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
321     }
322     PR_ExitMonitor(mon2);
323
324     PR_DestroySem(sem_set1[0]);
325     PR_DestroySem(sem_set1[1]);
326 }
327
328 static void SemaContextSwitchUU(void)
329 {
330     SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
331 }
332
333 static void SemaContextSwitchUK(void)
334 {
335     SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
336 }
337
338 static void SemaContextSwitchKU(void)
339 {
340     SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
341 }
342
343 static void SemaContextSwitchKK(void)
344 {
345     SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
346 }
347
348
349 /************************************************************************/
350
351 static void Measure(void (*func)(void), const char *msg)
352 {
353     PRIntervalTime start, stop;
354     double d;
355
356     start = PR_IntervalNow();
357     (*func)();
358     stop = PR_IntervalNow() - start;
359     d = (double)PR_IntervalToMicroseconds(stop);
360
361     printf("%40s: %6.2f usec\n", msg, d / count);
362 }
363
364 int main(int argc, char **argv)
365 {
366         PLOptStatus os;
367         PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
368         while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
369     {
370                 if (PL_OPT_BAD == os) continue;
371         switch (opt->option)
372         {
373         case 'd':  /* debug mode */
374                         _debug_on = 1;
375             break;
376         case 'c':  /* loop count */
377             count = atoi(opt->value);
378             break;
379          default:
380             break;
381         }
382     }
383         PL_DestroyOptState(opt);
384
385     if (0 == count) count = DEFAULT_COUNT;
386     
387     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
388         PR_BlockClockInterrupts();
389         PR_UnblockClockInterrupts();
390     PR_STDIO_INIT();
391
392     lock = PR_NewLock();
393     mon = PR_NewMonitor();
394     mon2 = PR_NewMonitor();
395
396     Measure(LocalProcedureCall, "local procedure call overhead");
397     Measure(DLLProcedureCall, "DLL procedure call overhead");
398     Measure(Now, "current calendar time");
399     Measure(Interval, "interval time");
400     Measure(IdleLock, "idle lock lock/unlock pair");
401     Measure(IdleMonitor, "idle monitor entry/exit pair");
402     Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
403     Measure(CDThread, "create/destroy thread pair");
404     Measure(ContextSwitchUU, "context switch - user/user");
405     Measure(ContextSwitchUK, "context switch - user/kernel");
406     Measure(ContextSwitchKU, "context switch - kernel/user");
407     Measure(ContextSwitchKK, "context switch - kernel/kernel");
408     Measure(SemaContextSwitchUU, "sema context switch - user/user");
409     Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
410     Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
411     Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
412
413     printf("--------------\n");
414     printf("Adding 7 additional CPUs\n");
415
416     PR_SetConcurrency(8);
417     printf("--------------\n");
418
419     Measure(LocalProcedureCall, "local procedure call overhead");
420     Measure(DLLProcedureCall, "DLL procedure call overhead");
421     Measure(Now, "current calendar time");
422     Measure(Interval, "interval time");
423     Measure(IdleLock, "idle lock lock/unlock pair");
424     Measure(IdleMonitor, "idle monitor entry/exit pair");
425     Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
426     Measure(CDThread, "create/destroy thread pair");
427     Measure(ContextSwitchUU, "context switch - user/user");
428     Measure(ContextSwitchUK, "context switch - user/kernel");
429     Measure(ContextSwitchKU, "context switch - kernel/user");
430     Measure(ContextSwitchKK, "context switch - kernel/kernel");
431     Measure(SemaContextSwitchUU, "sema context switch - user/user");
432     Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
433     Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
434     Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
435
436     PR_DestroyLock(lock);
437     PR_DestroyMonitor(mon);
438     PR_DestroyMonitor(mon2);
439
440     PR_Cleanup();
441     return 0;
442 }