michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nspr.h" michael@0: #include "plgetopt.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: int _debug_on = 0; michael@0: #define DPRINTF(arg) if (_debug_on) printf arg michael@0: michael@0: #include "obsolete/prsem.h" michael@0: michael@0: PRLock *lock; michael@0: PRMonitor *mon; michael@0: PRMonitor *mon2; michael@0: michael@0: #define DEFAULT_COUNT 1000 michael@0: michael@0: PRInt32 count; michael@0: michael@0: static void nop(int a, int b, int c) michael@0: { michael@0: } michael@0: michael@0: static void LocalProcedureCall(void) michael@0: { michael@0: PRInt32 i; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: nop(i, i, 5); michael@0: } michael@0: } michael@0: michael@0: static void DLLProcedureCall(void) michael@0: { michael@0: PRInt32 i; michael@0: PRThreadState state; michael@0: PRThread *self = PR_GetCurrentThread(); michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: state = PR_GetThreadState(self); michael@0: } michael@0: } michael@0: michael@0: static void Now(void) michael@0: { michael@0: PRInt32 i; michael@0: PRTime time; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: time = PR_Now(); michael@0: } michael@0: } michael@0: michael@0: static void Interval(void) michael@0: { michael@0: PRInt32 i; michael@0: PRIntervalTime time; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: time = PR_IntervalNow(); michael@0: } michael@0: } michael@0: michael@0: static void IdleLock(void) michael@0: { michael@0: PRInt32 i; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: PR_Lock(lock); michael@0: PR_Unlock(lock); michael@0: } michael@0: } michael@0: michael@0: static void IdleMonitor(void) michael@0: { michael@0: PRInt32 i; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: PR_EnterMonitor(mon); michael@0: PR_ExitMonitor(mon); michael@0: } michael@0: } michael@0: michael@0: static void IdleCMonitor(void) michael@0: { michael@0: PRInt32 i; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: PR_CEnterMonitor((void*)7); michael@0: PR_CExitMonitor((void*)7); michael@0: } michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: static void PR_CALLBACK dull(void *arg) michael@0: { michael@0: } michael@0: michael@0: static void CDThread(void) michael@0: { michael@0: PRInt32 i; michael@0: int num_threads = count; michael@0: michael@0: /* michael@0: * Cannot create too many threads michael@0: */ michael@0: if (num_threads > 1000) michael@0: num_threads = 1000; michael@0: michael@0: for (i = 0; i < num_threads; i++) { michael@0: PRThread *t = PR_CreateThread(PR_USER_THREAD, michael@0: dull, 0, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: if (NULL == t) { michael@0: fprintf(stderr, "CDThread: cannot create thread %3d\n", i); michael@0: } else { michael@0: DPRINTF(("CDThread: created thread %3d \n",i)); michael@0: } michael@0: PR_Sleep(0); michael@0: } michael@0: } michael@0: michael@0: static int alive; michael@0: static int cxq; michael@0: michael@0: static void PR_CALLBACK CXReader(void *arg) michael@0: { michael@0: PRInt32 i, n; michael@0: michael@0: PR_EnterMonitor(mon); michael@0: n = count / 2; michael@0: for (i = 0; i < n; i++) { michael@0: while (cxq == 0) { michael@0: DPRINTF(("CXReader: thread = 0x%lx waiting\n", michael@0: PR_GetCurrentThread())); michael@0: PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: --cxq; michael@0: PR_Notify(mon); michael@0: } michael@0: PR_ExitMonitor(mon); michael@0: michael@0: PR_EnterMonitor(mon2); michael@0: --alive; michael@0: PR_Notify(mon2); michael@0: PR_ExitMonitor(mon2); michael@0: DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread())); michael@0: } michael@0: michael@0: static void PR_CALLBACK CXWriter(void *arg) michael@0: { michael@0: PRInt32 i, n; michael@0: michael@0: PR_EnterMonitor(mon); michael@0: n = count / 2; michael@0: for (i = 0; i < n; i++) { michael@0: while (cxq == 1) { michael@0: DPRINTF(("CXWriter: thread = 0x%lx waiting\n", michael@0: PR_GetCurrentThread())); michael@0: PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: ++cxq; michael@0: PR_Notify(mon); michael@0: } michael@0: PR_ExitMonitor(mon); michael@0: michael@0: PR_EnterMonitor(mon2); michael@0: --alive; michael@0: PR_Notify(mon2); michael@0: PR_ExitMonitor(mon2); michael@0: DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread())); michael@0: } michael@0: michael@0: static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2) michael@0: { michael@0: PRThread *t1, *t2; michael@0: michael@0: PR_EnterMonitor(mon2); michael@0: alive = 2; michael@0: cxq = 0; michael@0: michael@0: t1 = PR_CreateThread(PR_USER_THREAD, michael@0: CXReader, 0, michael@0: PR_PRIORITY_NORMAL, michael@0: scope1, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: if (NULL == t1) { michael@0: fprintf(stderr, "ContextSwitch: cannot create thread\n"); michael@0: } else { michael@0: DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n", michael@0: (scope1 == PR_GLOBAL_THREAD ? michael@0: "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), michael@0: t1)); michael@0: } michael@0: t2 = PR_CreateThread(PR_USER_THREAD, michael@0: CXWriter, 0, michael@0: PR_PRIORITY_NORMAL, michael@0: scope2, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: if (NULL == t2) { michael@0: fprintf(stderr, "ContextSwitch: cannot create thread\n"); michael@0: } else { michael@0: DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n", michael@0: (scope2 == PR_GLOBAL_THREAD ? michael@0: "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), michael@0: t2)); michael@0: } michael@0: michael@0: /* Wait for both of the threads to exit */ michael@0: while (alive) { michael@0: PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: PR_ExitMonitor(mon2); michael@0: } michael@0: michael@0: static void ContextSwitchUU(void) michael@0: { michael@0: ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD); michael@0: } michael@0: michael@0: static void ContextSwitchUK(void) michael@0: { michael@0: ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); michael@0: } michael@0: michael@0: static void ContextSwitchKU(void) michael@0: { michael@0: ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); michael@0: } michael@0: michael@0: static void ContextSwitchKK(void) michael@0: { michael@0: ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: static void PR_CALLBACK SemaThread(void *argSema) michael@0: { michael@0: PRSemaphore **sem = (PRSemaphore **)argSema; michael@0: PRInt32 i, n; michael@0: michael@0: n = count / 2; michael@0: for (i = 0; i < n; i++) { michael@0: DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n", michael@0: PR_GetCurrentThread(), sem[0])); michael@0: PR_WaitSem(sem[0]); michael@0: DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n", michael@0: PR_GetCurrentThread(), sem[1])); michael@0: PR_PostSem(sem[1]); michael@0: } michael@0: michael@0: PR_EnterMonitor(mon2); michael@0: --alive; michael@0: PR_Notify(mon2); michael@0: PR_ExitMonitor(mon2); michael@0: DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread())); michael@0: } michael@0: michael@0: static PRSemaphore *sem_set1[2]; michael@0: static PRSemaphore *sem_set2[2]; michael@0: michael@0: static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2) michael@0: { michael@0: PRThread *t1, *t2; michael@0: sem_set1[0] = PR_NewSem(1); michael@0: sem_set1[1] = PR_NewSem(0); michael@0: sem_set2[0] = sem_set1[1]; michael@0: sem_set2[1] = sem_set1[0]; michael@0: michael@0: PR_EnterMonitor(mon2); michael@0: alive = 2; michael@0: cxq = 0; michael@0: michael@0: t1 = PR_CreateThread(PR_USER_THREAD, michael@0: SemaThread, michael@0: sem_set1, michael@0: PR_PRIORITY_NORMAL, michael@0: scope1, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: if (NULL == t1) { michael@0: fprintf(stderr, "SemaContextSwitch: cannot create thread\n"); michael@0: } else { michael@0: DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n", michael@0: (scope1 == PR_GLOBAL_THREAD ? michael@0: "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), michael@0: t1)); michael@0: } michael@0: t2 = PR_CreateThread(PR_USER_THREAD, michael@0: SemaThread, michael@0: sem_set2, michael@0: PR_PRIORITY_NORMAL, michael@0: scope2, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: if (NULL == t2) { michael@0: fprintf(stderr, "SemaContextSwitch: cannot create thread\n"); michael@0: } else { michael@0: DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n", michael@0: (scope2 == PR_GLOBAL_THREAD ? michael@0: "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), michael@0: t2)); michael@0: } michael@0: michael@0: /* Wait for both of the threads to exit */ michael@0: while (alive) { michael@0: PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: PR_ExitMonitor(mon2); michael@0: michael@0: PR_DestroySem(sem_set1[0]); michael@0: PR_DestroySem(sem_set1[1]); michael@0: } michael@0: michael@0: static void SemaContextSwitchUU(void) michael@0: { michael@0: SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD); michael@0: } michael@0: michael@0: static void SemaContextSwitchUK(void) michael@0: { michael@0: SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); michael@0: } michael@0: michael@0: static void SemaContextSwitchKU(void) michael@0: { michael@0: SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); michael@0: } michael@0: michael@0: static void SemaContextSwitchKK(void) michael@0: { michael@0: SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); michael@0: } michael@0: michael@0: michael@0: /************************************************************************/ michael@0: michael@0: static void Measure(void (*func)(void), const char *msg) michael@0: { michael@0: PRIntervalTime start, stop; michael@0: double d; michael@0: michael@0: start = PR_IntervalNow(); michael@0: (*func)(); michael@0: stop = PR_IntervalNow() - start; michael@0: d = (double)PR_IntervalToMicroseconds(stop); michael@0: michael@0: printf("%40s: %6.2f usec\n", msg, d / count); michael@0: } michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "dc:"); michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'd': /* debug mode */ michael@0: _debug_on = 1; michael@0: break; michael@0: case 'c': /* loop count */ michael@0: count = atoi(opt->value); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: if (0 == count) count = DEFAULT_COUNT; michael@0: michael@0: PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); michael@0: PR_BlockClockInterrupts(); michael@0: PR_UnblockClockInterrupts(); michael@0: PR_STDIO_INIT(); michael@0: michael@0: lock = PR_NewLock(); michael@0: mon = PR_NewMonitor(); michael@0: mon2 = PR_NewMonitor(); michael@0: michael@0: Measure(LocalProcedureCall, "local procedure call overhead"); michael@0: Measure(DLLProcedureCall, "DLL procedure call overhead"); michael@0: Measure(Now, "current calendar time"); michael@0: Measure(Interval, "interval time"); michael@0: Measure(IdleLock, "idle lock lock/unlock pair"); michael@0: Measure(IdleMonitor, "idle monitor entry/exit pair"); michael@0: Measure(IdleCMonitor, "idle cache monitor entry/exit pair"); michael@0: Measure(CDThread, "create/destroy thread pair"); michael@0: Measure(ContextSwitchUU, "context switch - user/user"); michael@0: Measure(ContextSwitchUK, "context switch - user/kernel"); michael@0: Measure(ContextSwitchKU, "context switch - kernel/user"); michael@0: Measure(ContextSwitchKK, "context switch - kernel/kernel"); michael@0: Measure(SemaContextSwitchUU, "sema context switch - user/user"); michael@0: Measure(SemaContextSwitchUK, "sema context switch - user/kernel"); michael@0: Measure(SemaContextSwitchKU, "sema context switch - kernel/user"); michael@0: Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel"); michael@0: michael@0: printf("--------------\n"); michael@0: printf("Adding 7 additional CPUs\n"); michael@0: michael@0: PR_SetConcurrency(8); michael@0: printf("--------------\n"); michael@0: michael@0: Measure(LocalProcedureCall, "local procedure call overhead"); michael@0: Measure(DLLProcedureCall, "DLL procedure call overhead"); michael@0: Measure(Now, "current calendar time"); michael@0: Measure(Interval, "interval time"); michael@0: Measure(IdleLock, "idle lock lock/unlock pair"); michael@0: Measure(IdleMonitor, "idle monitor entry/exit pair"); michael@0: Measure(IdleCMonitor, "idle cache monitor entry/exit pair"); michael@0: Measure(CDThread, "create/destroy thread pair"); michael@0: Measure(ContextSwitchUU, "context switch - user/user"); michael@0: Measure(ContextSwitchUK, "context switch - user/kernel"); michael@0: Measure(ContextSwitchKU, "context switch - kernel/user"); michael@0: Measure(ContextSwitchKK, "context switch - kernel/kernel"); michael@0: Measure(SemaContextSwitchUU, "sema context switch - user/user"); michael@0: Measure(SemaContextSwitchUK, "sema context switch - user/kernel"); michael@0: Measure(SemaContextSwitchKU, "sema context switch - kernel/user"); michael@0: Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel"); michael@0: michael@0: PR_DestroyLock(lock); michael@0: PR_DestroyMonitor(mon); michael@0: PR_DestroyMonitor(mon2); michael@0: michael@0: PR_Cleanup(); michael@0: return 0; michael@0: }