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