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 
nop(int a,int b,int c)26 static void nop(int a, int b, int c)
27 {
28 }
29 
LocalProcedureCall(void)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 
DLLProcedureCall(void)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 
Now(void)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 
Interval(void)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 
IdleLock(void)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 
IdleMonitor(void)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 
IdleCMonitor(void)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 
dull(void * arg)102 static void PR_CALLBACK dull(void *arg)
103 {
104 }
105 
CDThread(void)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 
CXReader(void * arg)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 
CXWriter(void * arg)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 
ContextSwitch(PRThreadScope scope1,PRThreadScope scope2)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 
ContextSwitchUU(void)228 static void ContextSwitchUU(void)
229 {
230     ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
231 }
232 
ContextSwitchUK(void)233 static void ContextSwitchUK(void)
234 {
235     ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
236 }
237 
ContextSwitchKU(void)238 static void ContextSwitchKU(void)
239 {
240     ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
241 }
242 
ContextSwitchKK(void)243 static void ContextSwitchKK(void)
244 {
245     ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
246 }
247 
248 /************************************************************************/
249 
SemaThread(void * argSema)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 
SemaContextSwitch(PRThreadScope scope1,PRThreadScope scope2)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 
SemaContextSwitchUU(void)328 static void SemaContextSwitchUU(void)
329 {
330     SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
331 }
332 
SemaContextSwitchUK(void)333 static void SemaContextSwitchUK(void)
334 {
335     SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
336 }
337 
SemaContextSwitchKU(void)338 static void SemaContextSwitchKU(void)
339 {
340     SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
341 }
342 
SemaContextSwitchKK(void)343 static void SemaContextSwitchKK(void)
344 {
345     SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
346 }
347 
348 
349 /************************************************************************/
350 
Measure(void (* func)(void),const char * msg)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 
main(int argc,char ** argv)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 }
443