Back to index

lightning-sunbird  0.9+nobinonly
perf.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nspr.h"
00039 #include "plgetopt.h"
00040 
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 #include <string.h>
00044 
00045 int _debug_on = 0;
00046 #define DPRINTF(arg) if (_debug_on) printf arg
00047 
00048 #ifdef XP_MAC
00049 #include "prlog.h"
00050 #include "prsem.h"
00051 #define printf PR_LogPrint
00052 extern void SetupMacPrintfLog(char *logFile);
00053 #else
00054 #include "obsolete/prsem.h"
00055 #endif
00056 
00057 PRLock *lock;
00058 PRMonitor *mon;
00059 PRMonitor *mon2;
00060 
00061 #define DEFAULT_COUNT    1000
00062 
00063 PRInt32 count;
00064 
00065 static void nop(int a, int b, int c)
00066 {
00067 }
00068 
00069 static void LocalProcedureCall(void)
00070 {
00071     PRInt32 i;
00072 
00073     for (i = 0; i < count; i++) {
00074     nop(i, i, 5);
00075     }
00076 }
00077 
00078 static void DLLProcedureCall(void)
00079 {
00080     PRInt32 i;
00081        PRThreadState state;
00082        PRThread *self = PR_CurrentThread();
00083 
00084     for (i = 0; i < count; i++) {
00085        state = PR_GetThreadState(self);
00086     }
00087 }
00088 
00089 static void Now(void)
00090 {
00091     PRInt32 i;
00092     PRTime time;
00093 
00094     for (i = 0; i < count; i++) {
00095         time = PR_Now();
00096     }
00097 }
00098 
00099 static void Interval(void)
00100 {
00101     PRInt32 i;
00102     PRIntervalTime time;
00103 
00104     for (i = 0; i < count; i++) {
00105         time = PR_IntervalNow();
00106     }
00107 }
00108 
00109 static void IdleLock(void)
00110 {
00111     PRInt32 i;
00112 
00113     for (i = 0; i < count; i++) {
00114     PR_Lock(lock);
00115     PR_Unlock(lock);
00116     }
00117 }
00118 
00119 static void IdleMonitor(void)
00120 {
00121     PRInt32 i;
00122 
00123     for (i = 0; i < count; i++) {
00124     PR_EnterMonitor(mon);
00125     PR_ExitMonitor(mon);
00126     }
00127 }
00128 
00129 static void IdleCMonitor(void)
00130 {
00131     PRInt32 i;
00132 
00133     for (i = 0; i < count; i++) {
00134     PR_CEnterMonitor((void*)7);
00135     PR_CExitMonitor((void*)7);
00136     }
00137 }
00138 
00139 /************************************************************************/
00140 
00141 static void PR_CALLBACK dull(void *arg)
00142 {
00143 }
00144 
00145 static void CDThread(void)
00146 {
00147     PRInt32 i;
00148     int num_threads = count;
00149 
00150     /*
00151      * Cannot create too many threads
00152      */
00153     if (num_threads > 1000)
00154     num_threads = 1000;
00155 
00156     for (i = 0; i < num_threads; i++) {
00157         PRThread *t = PR_CreateThread(PR_USER_THREAD,
00158                       dull, 0, 
00159                       PR_PRIORITY_NORMAL,
00160                       PR_LOCAL_THREAD,
00161                       PR_UNJOINABLE_THREAD,
00162                       0);
00163         if (NULL == t) {
00164             fprintf(stderr, "CDThread: cannot create thread %3d\n", i);
00165         } else {
00166             DPRINTF(("CDThread: created thread %3d \n",i));
00167         }
00168         PR_Sleep(0);
00169     }
00170 }
00171 
00172 static int alive;
00173 static int cxq;
00174 
00175 static void PR_CALLBACK CXReader(void *arg)
00176 {
00177     PRInt32 i, n;
00178 
00179     PR_EnterMonitor(mon);
00180     n = count / 2;
00181     for (i = 0; i < n; i++) {
00182     while (cxq == 0) {
00183             DPRINTF(("CXReader: thread = 0x%lx waiting\n",
00184                     PR_GetCurrentThread()));
00185         PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
00186     }
00187     --cxq;
00188     PR_Notify(mon);
00189     }
00190     PR_ExitMonitor(mon);
00191 
00192     PR_EnterMonitor(mon2);
00193     --alive;
00194     PR_Notify(mon2);
00195     PR_ExitMonitor(mon2);
00196     DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
00197 }
00198 
00199 static void PR_CALLBACK CXWriter(void *arg)
00200 {
00201     PRInt32 i, n;
00202 
00203     PR_EnterMonitor(mon);
00204     n = count / 2;
00205     for (i = 0; i < n; i++) {
00206     while (cxq == 1) {
00207             DPRINTF(("CXWriter: thread = 0x%lx waiting\n",
00208                     PR_GetCurrentThread()));
00209         PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
00210     }
00211     ++cxq;
00212     PR_Notify(mon);
00213     }
00214     PR_ExitMonitor(mon);
00215 
00216     PR_EnterMonitor(mon2);
00217     --alive;
00218     PR_Notify(mon2);
00219     PR_ExitMonitor(mon2);
00220     DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
00221 }
00222 
00223 static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
00224 {
00225     PRThread *t1, *t2;
00226 
00227     PR_EnterMonitor(mon2);
00228     alive = 2;
00229     cxq = 0;
00230 
00231     t1 = PR_CreateThread(PR_USER_THREAD,
00232                       CXReader, 0, 
00233                       PR_PRIORITY_NORMAL,
00234                       scope1,
00235                       PR_UNJOINABLE_THREAD,
00236                       0);
00237     if (NULL == t1) {
00238         fprintf(stderr, "ContextSwitch: cannot create thread\n");
00239     } else {
00240         DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
00241                 (scope1 == PR_GLOBAL_THREAD ?
00242                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
00243                             t1));
00244     }
00245     t2 = PR_CreateThread(PR_USER_THREAD,
00246                       CXWriter, 0, 
00247                       PR_PRIORITY_NORMAL,
00248                       scope2,
00249                       PR_UNJOINABLE_THREAD,
00250                       0);
00251     if (NULL == t2) {
00252         fprintf(stderr, "ContextSwitch: cannot create thread\n");
00253     } else {
00254         DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
00255                 (scope2 == PR_GLOBAL_THREAD ?
00256                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
00257                             t2));
00258     }
00259 
00260     /* Wait for both of the threads to exit */
00261     while (alive) {
00262     PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
00263     }
00264     PR_ExitMonitor(mon2);
00265 }
00266 
00267 static void ContextSwitchUU(void)
00268 {
00269     ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
00270 }
00271 
00272 static void ContextSwitchUK(void)
00273 {
00274     ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
00275 }
00276 
00277 static void ContextSwitchKU(void)
00278 {
00279     ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
00280 }
00281 
00282 static void ContextSwitchKK(void)
00283 {
00284     ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
00285 }
00286 
00287 /************************************************************************/
00288 
00289 static void PR_CALLBACK SemaThread(void *argSema)
00290 {
00291     PRSemaphore **sem = (PRSemaphore **)argSema;
00292     PRInt32 i, n;
00293 
00294     n = count / 2;
00295     for (i = 0; i < n; i++) {
00296         DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n",
00297                 PR_GetCurrentThread(), sem[0]));
00298         PR_WaitSem(sem[0]);
00299         DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n",
00300                 PR_GetCurrentThread(), sem[1]));
00301         PR_PostSem(sem[1]);
00302     }
00303 
00304     PR_EnterMonitor(mon2);
00305     --alive;
00306     PR_Notify(mon2);
00307     PR_ExitMonitor(mon2);
00308     DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
00309 }
00310 
00311 static  PRSemaphore *sem_set1[2];
00312 static  PRSemaphore *sem_set2[2];
00313 
00314 static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
00315 {
00316     PRThread *t1, *t2;
00317     sem_set1[0] = PR_NewSem(1);
00318     sem_set1[1] = PR_NewSem(0);
00319     sem_set2[0] = sem_set1[1];
00320     sem_set2[1] = sem_set1[0];
00321 
00322     PR_EnterMonitor(mon2);
00323     alive = 2;
00324     cxq = 0;
00325 
00326     t1 = PR_CreateThread(PR_USER_THREAD,
00327                       SemaThread, 
00328                       sem_set1, 
00329                       PR_PRIORITY_NORMAL,
00330                       scope1,
00331                       PR_UNJOINABLE_THREAD,
00332                       0);
00333     if (NULL == t1) {
00334         fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
00335     } else {
00336         DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
00337                 (scope1 == PR_GLOBAL_THREAD ?
00338                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
00339                             t1));
00340     }
00341     t2 = PR_CreateThread(PR_USER_THREAD,
00342                       SemaThread, 
00343                       sem_set2, 
00344                       PR_PRIORITY_NORMAL,
00345                       scope2,
00346                       PR_UNJOINABLE_THREAD,
00347                       0);
00348     if (NULL == t2) {
00349         fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
00350     } else {
00351         DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
00352                 (scope2 == PR_GLOBAL_THREAD ?
00353                 "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
00354                             t2));
00355     }
00356 
00357     /* Wait for both of the threads to exit */
00358     while (alive) {
00359         PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
00360     }
00361     PR_ExitMonitor(mon2);
00362 
00363     PR_DestroySem(sem_set1[0]);
00364     PR_DestroySem(sem_set1[1]);
00365 }
00366 
00367 static void SemaContextSwitchUU(void)
00368 {
00369     SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
00370 }
00371 
00372 static void SemaContextSwitchUK(void)
00373 {
00374     SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
00375 }
00376 
00377 static void SemaContextSwitchKU(void)
00378 {
00379     SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
00380 }
00381 
00382 static void SemaContextSwitchKK(void)
00383 {
00384     SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
00385 }
00386 
00387 
00388 /************************************************************************/
00389 
00390 static void Measure(void (*func)(void), const char *msg)
00391 {
00392     PRIntervalTime start, stop;
00393     double d;
00394 
00395     start = PR_IntervalNow();
00396     (*func)();
00397     stop = PR_IntervalNow() - start;
00398     d = (double)PR_IntervalToMicroseconds(stop);
00399 
00400     printf("%40s: %6.2f usec\n", msg, d / count);
00401 }
00402 
00403 int main(int argc, char **argv)
00404 {
00405        PLOptStatus os;
00406        PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
00407        while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00408     {
00409               if (PL_OPT_BAD == os) continue;
00410         switch (opt->option)
00411         {
00412         case 'd':  /* debug mode */
00413                      _debug_on = 1;
00414             break;
00415         case 'c':  /* loop count */
00416             count = atoi(opt->value);
00417             break;
00418          default:
00419             break;
00420         }
00421     }
00422        PL_DestroyOptState(opt);
00423 
00424     if (0 == count) count = DEFAULT_COUNT;
00425     
00426     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
00427        PR_BlockClockInterrupts();
00428        PR_UnblockClockInterrupts();
00429     PR_STDIO_INIT();
00430 
00431 #ifdef XP_MAC
00432     SetupMacPrintfLog("perf.log");
00433 #endif
00434 
00435     lock = PR_NewLock();
00436     mon = PR_NewMonitor();
00437     mon2 = PR_NewMonitor();
00438 
00439     Measure(LocalProcedureCall, "local procedure call overhead");
00440     Measure(DLLProcedureCall, "DLL procedure call overhead");
00441     Measure(Now, "current calendar time");
00442     Measure(Interval, "interval time");
00443     Measure(IdleLock, "idle lock lock/unlock pair");
00444     Measure(IdleMonitor, "idle monitor entry/exit pair");
00445     Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
00446     Measure(CDThread, "create/destroy thread pair");
00447     Measure(ContextSwitchUU, "context switch - user/user");
00448     Measure(ContextSwitchUK, "context switch - user/kernel");
00449     Measure(ContextSwitchKU, "context switch - kernel/user");
00450     Measure(ContextSwitchKK, "context switch - kernel/kernel");
00451     Measure(SemaContextSwitchUU, "sema context switch - user/user");
00452     Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
00453     Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
00454     Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
00455 
00456     printf("--------------\n");
00457     printf("Adding 7 additional CPUs\n");
00458 
00459     PR_SetConcurrency(8);
00460     printf("--------------\n");
00461 
00462     Measure(LocalProcedureCall, "local procedure call overhead");
00463     Measure(DLLProcedureCall, "DLL procedure call overhead");
00464     Measure(Now, "current calendar time");
00465     Measure(Interval, "interval time");
00466     Measure(IdleLock, "idle lock lock/unlock pair");
00467     Measure(IdleMonitor, "idle monitor entry/exit pair");
00468     Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
00469     Measure(CDThread, "create/destroy thread pair");
00470     Measure(ContextSwitchUU, "context switch - user/user");
00471     Measure(ContextSwitchUK, "context switch - user/kernel");
00472     Measure(ContextSwitchKU, "context switch - kernel/user");
00473     Measure(ContextSwitchKK, "context switch - kernel/kernel");
00474     Measure(SemaContextSwitchUU, "sema context switch - user/user");
00475     Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
00476     Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
00477     Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
00478 
00479     PR_DestroyLock(lock);
00480     PR_DestroyMonitor(mon);
00481     PR_DestroyMonitor(mon2);
00482 
00483     PR_Cleanup();
00484     return 0;
00485 }