#ifdef WIN32 #include #define delay(a) Sleep((a)) #else #define INCL_BASE #define INCL_DOSSEMAPHORES #include #define delay(a) DosSleep((a)) #endif #include #include #include #define MAX_THREADS 32 #ifdef WIN32 // // - hStartOfRace is a manual reset event that is signalled when // all of the threads are supposed to cut loose and begin working // // - hEndOfRace is a manual reset event that is signalled once the end time // has been retrieved and it is ok for the threads to exit // HANDLE hStartOfRace; HANDLE hEndOfRace; // // - ThreadReadyDoneEvents are an array of autoclearing events. The threads // initially signal these events once they have reached their start routines // and are ready to being processing. Once they are done processing, they // signal thier event to indicate that they are done processing. // // - ThreadHandles are an array of thread handles to the worker threads. The // main thread waits on these to know when all of the threads have exited. // HANDLE ThreadReadyDoneEvents[MAX_THREADS]; HANDLE ThreadHandles[MAX_THREADS]; DWORD WorkerThread(PVOID ThreadIndex); DWORD ThreadId; DWORD StartTicks, EndTicks; HANDLE IoHandle; #else /* // - hStartOfRace is a manual reset event that is signalled when // all of the threads are supposed to cut loose and begin working // // - hEndOfRace is a manual reset event that is signalled once the end time // has been retrieved and it is ok for the threads to exit */ HEV hStartOfRace; HEV hEndOfRace; /* // - ThreadReadyDoneEvents are an array of autoclearing events. The threads // initially signal these events once they have reached their start routines // and are ready to being processing. Once they are done processing, they // signal thier event to indicate that they are done processing. // // - ThreadHandles are an array of thread handles to the worker threads. The // main thread waits on these to know when all of the threads have exited. */ SEMRECORD ThreadReadyDoneEvents1[MAX_THREADS]; SEMRECORD ThreadReadyDoneEvents2[MAX_THREADS]; TID ThreadHandles[MAX_THREADS]; HMUX MuxWait1; HMUX MuxWait2; VOID WorkerThread(PVOID ThreadIndex); #pragma linkage(WorkerThread,system) #define _CRTAPI1 APIRET rv1, rv2; clock_t StartTicks, EndTicks; #define ExitProcess(a) DosExit(EXIT_PROCESS,(a)) #define GetTickCount clock HFILE IoHandle; HMTX MutexHandle; #endif #define SIXTY_FOUR_K (64*1024) #define SIXTEEN_K (16*1024) unsigned int InitialBuffer[SIXTY_FOUR_K/sizeof(unsigned int)]; #define NUMBER_OF_WRITES ((1024*1024*8)/SIXTY_FOUR_K) #define BUFFER_MAX (64*1024) #define FILE_SIZE ((1024*1024*8)-BUFFER_MAX) /* // Each thread has a THREAD_WORK structure. This contains the address // of the cells that this thread is responsible for, and the number of // cells it is supposed to process. */ typedef struct _THREAD_WORK { unsigned long *CellVector; int NumberOfCells; int RecalcResult; char ReadBuffer[BUFFER_MAX]; char SortedBuffer[BUFFER_MAX]; #ifdef WIN32 OVERLAPPED Overlapped; #else HFILE IoHandle; #endif } THREAD_WORK, *PTHREAD_WORK; THREAD_WORK ThreadWork[MAX_THREADS]; #define ONE_MB (1024*1024) unsigned long Mb = 4; int NumberOfThreads = 1; unsigned long ExpectedRecalcValue; unsigned long ActualRecalcValue; unsigned long ContentionValue; int fMemoryContention; int fIoMode; int fSortToo; int WorkIndex; int BufferSize; void SortTheBuffer( unsigned int *Destination, unsigned int *Source, int DwordCount ); unsigned int Random ( unsigned int nMaxValue ) { return(((2 * rand() * nMaxValue + RAND_MAX) / RAND_MAX - 1) / 2); } int _CRTAPI1 main( int argc, char *argv[], char *envp[] ) { int i; int fShowUsage; char c, *p, *whocares; unsigned long *CellVector; int NumberOfDwords; int CNumberOfDwords; int DwordsPerThread; char *Answer; unsigned long x; fShowUsage = 0; fMemoryContention = 0; fIoMode = 0; fSortToo = 0; WorkIndex = 10; BufferSize = 1024; if (argc <= 1) { goto showUsage; } while (--argc) { p = *++argv; if (*p == '/' || *p == '-') { while (c = *++p) switch (toupper( c )) { case '?': fShowUsage = 1; goto showUsage; break; case 'M': if (!argc--) { fShowUsage = 1; goto showUsage; } argv++; Mb = strtoul(*argv,&whocares,10); if ( Mb == 0 ) { Mb = 1; } break; case 'C': fMemoryContention = 1; break; case 'I': fIoMode = 1; break; case 'S': fSortToo = 1; break; case 'T': if (!argc--) { fShowUsage = 1; goto showUsage; } argv++; NumberOfThreads = strtoul(*argv,&whocares,10); if ( NumberOfThreads > MAX_THREADS ) { fShowUsage = 1; goto showUsage; } break; case 'W': if (!argc--) { fShowUsage = 1; goto showUsage; } argv++; WorkIndex = strtoul(*argv,&whocares,10); break; case 'B': if (!argc--) { fShowUsage = 1; goto showUsage; } argv++; BufferSize = strtoul(*argv,&whocares,10) * 1024; if ( BufferSize > BUFFER_MAX ) { BufferSize = BUFFER_MAX; } break; default: fprintf( stderr, "MTBNCH: Invalid switch - /%c\n", c ); goto showUsage; break; } } } showUsage: if ( fShowUsage ) { fprintf(stderr,"usage: MTBNCH\n" ); fprintf(stderr," [-?] display this message\n" ); fprintf(stderr," [-t n] use n threads for benchmark (less than 32)\n" ); fprintf(stderr," [-m n] use an n Mb spreadsheet size (default 4)\n" ); fprintf(stderr," [-c] cause memory contention on each loop iteration\n" ); fprintf(stderr," [-i] do I/O in each loop iteration\n" ); fprintf(stderr," [-s] after each I/O sort the buffer\n" ); fprintf(stderr," [-w n] The lower the number, the more I/O is done\n" ); ExitProcess(1); } /* // Prepare the race events. These are manual reset events. */ #ifdef WIN32 hStartOfRace = CreateEvent(NULL,TRUE,FALSE,NULL); hEndOfRace = CreateEvent(NULL,TRUE,FALSE,NULL); if ( !hStartOfRace || !hEndOfRace ) { #else rv1 = DosCreateEventSem(NULL,&hStartOfRace,0,FALSE); rv2 = DosCreateEventSem(NULL,&hEndOfRace,0,FALSE); if ( rv1 || rv2 ) { #endif fprintf(stderr,"MTBNCH: Race Event Creation Failed\n"); ExitProcess(1); } if ( fIoMode ) { /* // In I/O mode, we create a large file and fill it with random // values */ srand(1); #ifdef WIN32 DeleteFile("mtbnch.dat"); IoHandle = CreateFile( "mtbnch.dat", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); if ( IoHandle == INVALID_HANDLE_VALUE ) { fprintf(stderr, "mtbnch: Error opening file %d\n",GetLastError()); exit(1); } // // Initialize the file with random data // { DWORD lc; INT i; BOOL b; int WriteCount; ThreadWork[0].Overlapped.Offset = 0; for (WriteCount = 0; WriteCount < NUMBER_OF_WRITES; WriteCount++){ for(lc=0;lc>2); } } else { j++; } if ( MemoryContention ) { #ifdef WIN32 InterlockedIncrement(&ContentionValue); #else DosEnterCritSec(); ContentionValue++; DosExitCritSec(); #endif } } ThreadWork[Me].RecalcResult = MyRecalcValue; /* // Signal that I am done and then wait for further instructions */ #ifdef WIN32 if ( !SetEvent(ThreadReadyDoneEvents[Me]) ) { fprintf(stderr,"MTBNCH: (2) SetEvent(ThreadReadyDoneEvent[%d]) Failed %d\n",Me,GetLastError()); #else rv = DosPostEventSem((HEV)ThreadReadyDoneEvents2[Me].hsemCur); if ( rv ) { fprintf(stderr,"MTBNCH: (1) SetEvent(ThreadReadyDoneEvent2[%d]) Failed %d\n",Me,rv); #endif ExitProcess(1); } #ifdef WIN32 i = WaitForSingleObject(hEndOfRace,INFINITE); if ( i == WAIT_FAILED ) { fprintf(stderr,"MTBNCH: Thread %d Wait for end of recalc Failed %d\n",Me,GetLastError()); #else rv = DosWaitEventSem(hEndOfRace,SEM_INDEFINITE_WAIT); if ( rv ) { fprintf(stderr,"MTBNCH: Thread %d Wait for end of recalc Failed %d\n",Me,rv); #endif ExitProcess(1); } #ifdef WIN32 return MyRecalcValue; #endif } int #ifdef WIN32 _CRTAPI1 #endif DwordComp(const void *e1,const void *e2) { unsigned long *p1; unsigned long *p2; p1 = (unsigned long *)e1; p2 = (unsigned long *)e2; return (*p1 - *p2); } void SortTheBuffer( unsigned int *Destination, unsigned int *Source, int DwordCount ) { int i; for(i=0;i<1;i++){ memcpy(Destination,Source,DwordCount<<2); qsort((void *)Destination,(size_t)DwordCount,(size_t)sizeof(unsigned int),DwordComp); } }