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 
118     for (i = 0; i < num_threads; i++) {
119         PRThread *t = PR_CreateThread(PR_USER_THREAD,
120                                       dull, 0,
121                                       PR_PRIORITY_NORMAL,
122                                       PR_LOCAL_THREAD,
123                                       PR_UNJOINABLE_THREAD,
124                                       0);
125         if (NULL == t) {
126             fprintf(stderr, "CDThread: cannot create thread %3d\n", i);
127         } else {
128             DPRINTF(("CDThread: created thread %3d \n",i));
129         }
130         PR_Sleep(0);
131     }
132 }
133 
134 static int alive;
135 static int cxq;
136 
CXReader(void * arg)137 static void PR_CALLBACK CXReader(void *arg)
138 {
139     PRInt32 i, n;
140 
141     PR_EnterMonitor(mon);
142     n = count / 2;
143     for (i = 0; i < n; i++) {
144         while (cxq == 0) {
145             DPRINTF(("CXReader: thread = %p waiting\n",
146                      PR_GetCurrentThread()));
147             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
148         }
149         --cxq;
150         PR_Notify(mon);
151     }
152     PR_ExitMonitor(mon);
153 
154     PR_EnterMonitor(mon2);
155     --alive;
156     PR_Notify(mon2);
157     PR_ExitMonitor(mon2);
158     DPRINTF(("CXReader: thread = %p exiting\n", PR_GetCurrentThread()));
159 }
160 
CXWriter(void * arg)161 static void PR_CALLBACK CXWriter(void *arg)
162 {
163     PRInt32 i, n;
164 
165     PR_EnterMonitor(mon);
166     n = count / 2;
167     for (i = 0; i < n; i++) {
168         while (cxq == 1) {
169             DPRINTF(("CXWriter: thread = %p waiting\n",
170                      PR_GetCurrentThread()));
171             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
172         }
173         ++cxq;
174         PR_Notify(mon);
175     }
176     PR_ExitMonitor(mon);
177 
178     PR_EnterMonitor(mon2);
179     --alive;
180     PR_Notify(mon2);
181     PR_ExitMonitor(mon2);
182     DPRINTF(("CXWriter: thread = %p exiting\n", PR_GetCurrentThread()));
183 }
184 
ContextSwitch(PRThreadScope scope1,PRThreadScope scope2)185 static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
186 {
187     PRThread *t1, *t2;
188 
189     PR_EnterMonitor(mon2);
190     alive = 2;
191     cxq = 0;
192 
193     t1 = PR_CreateThread(PR_USER_THREAD,
194                          CXReader, 0,
195                          PR_PRIORITY_NORMAL,
196                          scope1,
197                          PR_UNJOINABLE_THREAD,
198                          0);
199     if (NULL == t1) {
200         fprintf(stderr, "ContextSwitch: cannot create thread\n");
201     } else {
202         DPRINTF(("ContextSwitch: created %s thread = %p\n",
203                  (scope1 == PR_GLOBAL_THREAD ?
204                   "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
205                  t1));
206     }
207     t2 = PR_CreateThread(PR_USER_THREAD,
208                          CXWriter, 0,
209                          PR_PRIORITY_NORMAL,
210                          scope2,
211                          PR_UNJOINABLE_THREAD,
212                          0);
213     if (NULL == t2) {
214         fprintf(stderr, "ContextSwitch: cannot create thread\n");
215     } else {
216         DPRINTF(("ContextSwitch: created %s thread = %p\n",
217                  (scope2 == PR_GLOBAL_THREAD ?
218                   "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
219                  t2));
220     }
221 
222     /* Wait for both of the threads to exit */
223     while (alive) {
224         PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
225     }
226     PR_ExitMonitor(mon2);
227 }
228 
ContextSwitchUU(void)229 static void ContextSwitchUU(void)
230 {
231     ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
232 }
233 
ContextSwitchUK(void)234 static void ContextSwitchUK(void)
235 {
236     ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
237 }
238 
ContextSwitchKU(void)239 static void ContextSwitchKU(void)
240 {
241     ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
242 }
243 
ContextSwitchKK(void)244 static void ContextSwitchKK(void)
245 {
246     ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
247 }
248 
249 /************************************************************************/
250 
SemaThread(void * argSema)251 static void PR_CALLBACK SemaThread(void *argSema)
252 {
253     PRSemaphore **sem = (PRSemaphore **)argSema;
254     PRInt32 i, n;
255 
256     n = count / 2;
257     for (i = 0; i < n; i++) {
258         DPRINTF(("SemaThread: thread = %p waiting on sem = %p\n",
259                  PR_GetCurrentThread(), sem[0]));
260         PR_WaitSem(sem[0]);
261         DPRINTF(("SemaThread: thread = %p posting on sem = %p\n",
262                  PR_GetCurrentThread(), sem[1]));
263         PR_PostSem(sem[1]);
264     }
265 
266     PR_EnterMonitor(mon2);
267     --alive;
268     PR_Notify(mon2);
269     PR_ExitMonitor(mon2);
270     DPRINTF(("SemaThread: thread = %p exiting\n", PR_GetCurrentThread()));
271 }
272 
273 static  PRSemaphore *sem_set1[2];
274 static  PRSemaphore *sem_set2[2];
275 
SemaContextSwitch(PRThreadScope scope1,PRThreadScope scope2)276 static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
277 {
278     PRThread *t1, *t2;
279     sem_set1[0] = PR_NewSem(1);
280     sem_set1[1] = PR_NewSem(0);
281     sem_set2[0] = sem_set1[1];
282     sem_set2[1] = sem_set1[0];
283 
284     PR_EnterMonitor(mon2);
285     alive = 2;
286     cxq = 0;
287 
288     t1 = PR_CreateThread(PR_USER_THREAD,
289                          SemaThread,
290                          sem_set1,
291                          PR_PRIORITY_NORMAL,
292                          scope1,
293                          PR_UNJOINABLE_THREAD,
294                          0);
295     if (NULL == t1) {
296         fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
297     } else {
298         DPRINTF(("SemaContextSwitch: created %s thread = %p\n",
299                  (scope1 == PR_GLOBAL_THREAD ?
300                   "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
301                  t1));
302     }
303     t2 = PR_CreateThread(PR_USER_THREAD,
304                          SemaThread,
305                          sem_set2,
306                          PR_PRIORITY_NORMAL,
307                          scope2,
308                          PR_UNJOINABLE_THREAD,
309                          0);
310     if (NULL == t2) {
311         fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
312     } else {
313         DPRINTF(("SemaContextSwitch: created %s thread = %p\n",
314                  (scope2 == PR_GLOBAL_THREAD ?
315                   "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
316                  t2));
317     }
318 
319     /* Wait for both of the threads to exit */
320     while (alive) {
321         PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
322     }
323     PR_ExitMonitor(mon2);
324 
325     PR_DestroySem(sem_set1[0]);
326     PR_DestroySem(sem_set1[1]);
327 }
328 
SemaContextSwitchUU(void)329 static void SemaContextSwitchUU(void)
330 {
331     SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
332 }
333 
SemaContextSwitchUK(void)334 static void SemaContextSwitchUK(void)
335 {
336     SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
337 }
338 
SemaContextSwitchKU(void)339 static void SemaContextSwitchKU(void)
340 {
341     SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
342 }
343 
SemaContextSwitchKK(void)344 static void SemaContextSwitchKK(void)
345 {
346     SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
347 }
348 
349 
350 /************************************************************************/
351 
Measure(void (* func)(void),const char * msg)352 static void Measure(void (*func)(void), const char *msg)
353 {
354     PRIntervalTime start, stop;
355     double d;
356 
357     start = PR_IntervalNow();
358     (*func)();
359     stop = PR_IntervalNow() - start;
360     d = (double)PR_IntervalToMicroseconds(stop);
361 
362     printf("%40s: %6.2f usec\n", msg, d / count);
363 }
364 
main(int argc,char ** argv)365 int main(int argc, char **argv)
366 {
367     PLOptStatus os;
368     PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
369     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
370     {
371         if (PL_OPT_BAD == os) {
372             continue;
373         }
374         switch (opt->option)
375         {
376             case 'd':  /* debug mode */
377                 _debug_on = 1;
378                 break;
379             case 'c':  /* loop count */
380                 count = atoi(opt->value);
381                 break;
382             default:
383                 break;
384         }
385     }
386     PL_DestroyOptState(opt);
387 
388     if (0 == count) {
389         count = DEFAULT_COUNT;
390     }
391 
392     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
393     PR_BlockClockInterrupts();
394     PR_UnblockClockInterrupts();
395     PR_STDIO_INIT();
396 
397     lock = PR_NewLock();
398     mon = PR_NewMonitor();
399     mon2 = PR_NewMonitor();
400 
401     Measure(LocalProcedureCall, "local procedure call overhead");
402     Measure(DLLProcedureCall, "DLL procedure call overhead");
403     Measure(Now, "current calendar time");
404     Measure(Interval, "interval time");
405     Measure(IdleLock, "idle lock lock/unlock pair");
406     Measure(IdleMonitor, "idle monitor entry/exit pair");
407     Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
408     Measure(CDThread, "create/destroy thread pair");
409     Measure(ContextSwitchUU, "context switch - user/user");
410     Measure(ContextSwitchUK, "context switch - user/kernel");
411     Measure(ContextSwitchKU, "context switch - kernel/user");
412     Measure(ContextSwitchKK, "context switch - kernel/kernel");
413     Measure(SemaContextSwitchUU, "sema context switch - user/user");
414     Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
415     Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
416     Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
417 
418     printf("--------------\n");
419     printf("Adding 7 additional CPUs\n");
420 
421     PR_SetConcurrency(8);
422     printf("--------------\n");
423 
424     Measure(LocalProcedureCall, "local procedure call overhead");
425     Measure(DLLProcedureCall, "DLL procedure call overhead");
426     Measure(Now, "current calendar time");
427     Measure(Interval, "interval time");
428     Measure(IdleLock, "idle lock lock/unlock pair");
429     Measure(IdleMonitor, "idle monitor entry/exit pair");
430     Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
431     Measure(CDThread, "create/destroy thread pair");
432     Measure(ContextSwitchUU, "context switch - user/user");
433     Measure(ContextSwitchUK, "context switch - user/kernel");
434     Measure(ContextSwitchKU, "context switch - kernel/user");
435     Measure(ContextSwitchKK, "context switch - kernel/kernel");
436     Measure(SemaContextSwitchUU, "sema context switch - user/user");
437     Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
438     Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
439     Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
440 
441     PR_DestroyLock(lock);
442     PR_DestroyMonitor(mon);
443     PR_DestroyMonitor(mon2);
444 
445     PR_Cleanup();
446     return 0;
447 }
448