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