pjs/nsprpub/pr/tests/perf.c

443 строки
11 KiB
C

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nspr.h"
#include "plgetopt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int _debug_on = 0;
#define DPRINTF(arg) if (_debug_on) printf arg
#ifdef XP_MAC
#include "prlog.h"
#include "prsem.h"
#define printf PR_LogPrint
extern void SetupMacPrintfLog(char *logFile);
#else
#include "obsolete/prsem.h"
#endif
PRLock *lock;
PRMonitor *mon;
PRMonitor *mon2;
#define DEFAULT_COUNT 1000
PRInt32 count;
static void nop(int a, int b, int c)
{
}
static void LocalProcedureCall(void)
{
PRInt32 i;
for (i = 0; i < count; i++) {
nop(i, i, 5);
}
}
static void DLLProcedureCall(void)
{
PRInt32 i;
PRThreadState state;
PRThread *self = PR_CurrentThread();
for (i = 0; i < count; i++) {
state = PR_GetThreadState(self);
}
}
static void IdleLock(void)
{
PRInt32 i;
for (i = 0; i < count; i++) {
PR_Lock(lock);
PR_Unlock(lock);
}
}
static void IdleMonitor(void)
{
PRInt32 i;
for (i = 0; i < count; i++) {
PR_EnterMonitor(mon);
PR_ExitMonitor(mon);
}
}
static void IdleCMonitor(void)
{
PRInt32 i;
for (i = 0; i < count; i++) {
PR_CEnterMonitor((void*)7);
PR_CExitMonitor((void*)7);
}
}
/************************************************************************/
static void PR_CALLBACK dull(void *arg)
{
}
static void CDThread(void)
{
PRInt32 i;
int num_threads = count;
/*
* Cannot create too many threads
*/
if (num_threads > 1000)
num_threads = 1000;
for (i = 0; i < num_threads; i++) {
PRThread *t = PR_CreateThread(PR_USER_THREAD,
dull, 0,
PR_PRIORITY_NORMAL,
PR_LOCAL_THREAD,
PR_UNJOINABLE_THREAD,
0);
if (NULL == t) {
fprintf(stderr, "CDThread: cannot create thread %3d\n", i);
} else {
DPRINTF(("CDThread: created thread %3d \n",i));
}
PR_Sleep(0);
}
}
static int alive;
static int cxq;
static void PR_CALLBACK CXReader(void *arg)
{
PRInt32 i, n;
PR_EnterMonitor(mon);
n = count / 2;
for (i = 0; i < n; i++) {
while (cxq == 0) {
DPRINTF(("CXReader: thread = 0x%lx waiting\n",
PR_GetCurrentThread()));
PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
}
--cxq;
PR_Notify(mon);
}
PR_ExitMonitor(mon);
PR_EnterMonitor(mon2);
--alive;
PR_Notify(mon2);
PR_ExitMonitor(mon2);
DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
}
static void PR_CALLBACK CXWriter(void *arg)
{
PRInt32 i, n;
PR_EnterMonitor(mon);
n = count / 2;
for (i = 0; i < n; i++) {
while (cxq == 1) {
DPRINTF(("CXWriter: thread = 0x%lx waiting\n",
PR_GetCurrentThread()));
PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
}
++cxq;
PR_Notify(mon);
}
PR_ExitMonitor(mon);
PR_EnterMonitor(mon2);
--alive;
PR_Notify(mon2);
PR_ExitMonitor(mon2);
DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
}
static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
{
PRThread *t1, *t2;
PR_EnterMonitor(mon2);
alive = 2;
cxq = 0;
t1 = PR_CreateThread(PR_USER_THREAD,
CXReader, 0,
PR_PRIORITY_NORMAL,
scope1,
PR_UNJOINABLE_THREAD,
0);
if (NULL == t1) {
fprintf(stderr, "ContextSwitch: cannot create thread\n");
} else {
DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
(scope1 == PR_GLOBAL_THREAD ?
"PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
t1));
}
t2 = PR_CreateThread(PR_USER_THREAD,
CXWriter, 0,
PR_PRIORITY_NORMAL,
scope2,
PR_UNJOINABLE_THREAD,
0);
if (NULL == t2) {
fprintf(stderr, "ContextSwitch: cannot create thread\n");
} else {
DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
(scope2 == PR_GLOBAL_THREAD ?
"PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
t2));
}
/* Wait for both of the threads to exit */
while (alive) {
PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
}
PR_ExitMonitor(mon2);
}
static void ContextSwitchUU(void)
{
ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
}
static void ContextSwitchUK(void)
{
ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
}
static void ContextSwitchKU(void)
{
ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
}
static void ContextSwitchKK(void)
{
ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
}
/************************************************************************/
static void PR_CALLBACK SemaThread(void *argSema)
{
PRSemaphore **sem = (PRSemaphore **)argSema;
PRInt32 i, n;
n = count / 2;
for (i = 0; i < n; i++) {
DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n",
PR_GetCurrentThread(), sem[0]));
PR_WaitSem(sem[0]);
DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n",
PR_GetCurrentThread(), sem[1]));
PR_PostSem(sem[1]);
}
PR_EnterMonitor(mon2);
--alive;
PR_Notify(mon2);
PR_ExitMonitor(mon2);
DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
}
static PRSemaphore *sem_set1[2];
static PRSemaphore *sem_set2[2];
static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
{
PRThread *t1, *t2;
sem_set1[0] = PR_NewSem(1);
sem_set1[1] = PR_NewSem(0);
sem_set2[0] = sem_set1[1];
sem_set2[1] = sem_set1[0];
PR_EnterMonitor(mon2);
alive = 2;
cxq = 0;
t1 = PR_CreateThread(PR_USER_THREAD,
SemaThread,
sem_set1,
PR_PRIORITY_NORMAL,
scope1,
PR_UNJOINABLE_THREAD,
0);
if (NULL == t1) {
fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
} else {
DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
(scope1 == PR_GLOBAL_THREAD ?
"PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
t1));
}
t2 = PR_CreateThread(PR_USER_THREAD,
SemaThread,
sem_set2,
PR_PRIORITY_NORMAL,
scope2,
PR_UNJOINABLE_THREAD,
0);
if (NULL == t2) {
fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
} else {
DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
(scope2 == PR_GLOBAL_THREAD ?
"PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
t2));
}
/* Wait for both of the threads to exit */
while (alive) {
PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
}
PR_ExitMonitor(mon2);
PR_DestroySem(sem_set1[0]);
PR_DestroySem(sem_set1[1]);
}
static void SemaContextSwitchUU(void)
{
SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
}
static void SemaContextSwitchUK(void)
{
SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
}
static void SemaContextSwitchKU(void)
{
SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
}
static void SemaContextSwitchKK(void)
{
SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
}
/************************************************************************/
static void Measure(void (*func)(void), const char *msg)
{
PRIntervalTime start, stop;
double d;
start = PR_IntervalNow();
(*func)();
stop = PR_IntervalNow() - start;
d = (double)PR_IntervalToMicroseconds(stop);
printf("%40s: %6.2f usec\n", msg, d / count);
}
int main(int argc, char **argv)
{
PLOptStatus os;
PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
{
if (PL_OPT_BAD == os) continue;
switch (opt->option)
{
case 'd': /* debug mode */
_debug_on = 1;
break;
case 'c': /* loop count */
count = atoi(opt->value);
break;
default:
break;
}
}
PL_DestroyOptState(opt);
if (0 == count) count = DEFAULT_COUNT;
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
PR_BlockClockInterrupts();
PR_UnblockClockInterrupts();
PR_STDIO_INIT();
#ifdef XP_MAC
SetupMacPrintfLog("perf.log");
#endif
lock = PR_NewLock();
mon = PR_NewMonitor();
mon2 = PR_NewMonitor();
Measure(LocalProcedureCall, "local procedure call overhead");
Measure(DLLProcedureCall, "DLL procedure call overhead");
Measure(IdleLock, "idle lock lock/unlock pair");
Measure(IdleMonitor, "idle monitor entry/exit pair");
Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
Measure(CDThread, "create/destroy thread pair");
Measure(ContextSwitchUU, "context switch - user/user");
Measure(ContextSwitchUK, "context switch - user/kernel");
Measure(ContextSwitchKU, "context switch - kernel/user");
Measure(ContextSwitchKK, "context switch - kernel/kernel");
Measure(SemaContextSwitchUU, "sema context switch - user/user");
Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
printf("--------------\n");
printf("Adding 7 additional CPUs\n");
PR_SetConcurrency(8);
printf("--------------\n");
Measure(LocalProcedureCall, "local procedure call overhead");
Measure(DLLProcedureCall, "DLL procedure call overhead");
Measure(IdleLock, "idle lock lock/unlock pair");
Measure(IdleMonitor, "idle monitor entry/exit pair");
Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
Measure(CDThread, "create/destroy thread pair");
Measure(ContextSwitchUU, "context switch - user/user");
Measure(ContextSwitchUK, "context switch - user/kernel");
Measure(ContextSwitchKU, "context switch - kernel/user");
Measure(ContextSwitchKK, "context switch - kernel/kernel");
Measure(SemaContextSwitchUU, "sema context switch - user/user");
Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
PR_DestroyLock(lock);
PR_DestroyMonitor(mon);
PR_DestroyMonitor(mon2);
PR_Cleanup();
return 0;
}