Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
10 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. //
  5. // - hStartOfRace is a manual reset event that is signalled when
  6. // all of the threads are supposed to cut loose and begin working
  7. //
  8. // - hEndOfRace is a manual reset event that is signalled once the end time
  9. // has been retrieved and it is ok for the threads to exit
  10. //
  11. HANDLE hStartOfRace;
  12. HANDLE hEndOfRace;
  13. #define MAX_THREADS 32
  14. //
  15. // - ThreadReadyDoneEvents are an array of autoclearing events. The threads
  16. // initially signal these events once they have reached their start routines
  17. // and are ready to being processing. Once they are done processing, they
  18. // signal thier event to indicate that they are done processing.
  19. //
  20. // - ThreadHandles are an array of thread handles to the worker threads. The
  21. // main thread waits on these to know when all of the threads have exited.
  22. //
  23. HANDLE ThreadReadyDoneEvents[MAX_THREADS];
  24. HANDLE ThreadHandles[MAX_THREADS];
  25. //
  26. // Each thread has a THREAD_WORK structure. This contains the address
  27. // of the cells that this thread is responsible for, and the number of
  28. // cells it is supposed to process.
  29. //
  30. typedef struct _THREAD_WORK {
  31. PDWORD CellVector;
  32. DWORD NumberOfCells;
  33. DWORD RecalcResult;
  34. } THREAD_WORK, *PTHREAD_WORK;
  35. THREAD_WORK ThreadWork[MAX_THREADS];
  36. #define ONE_MB (1024*1024)
  37. DWORD Mb = 4;
  38. DWORD NumberOfThreads = 1;
  39. DWORD ExpectedRecalcValue;
  40. DWORD ActualRecalcValue;
  41. DWORD ContentionValue;
  42. BOOL fMemoryContention;
  43. DWORD WorkerThread(PVOID ThreadIndex);
  44. int __cdecl
  45. main(
  46. int argc,
  47. char *argv[],
  48. char *envp[]
  49. )
  50. {
  51. DWORD StartTicks, EndTicks;
  52. DWORD i;
  53. BOOL fShowUsage;
  54. char c, *p, *whocares;
  55. PDWORD CellVector;
  56. DWORD NumberOfDwords;
  57. DWORD DwordsPerThread;
  58. DWORD ThreadId;
  59. LPSTR Answer;
  60. fShowUsage = FALSE;
  61. fMemoryContention = FALSE;
  62. if (argc <= 1) {
  63. goto showUsage;
  64. }
  65. while (--argc) {
  66. p = *++argv;
  67. if (*p == '/' || *p == '-') {
  68. while (c = *++p)
  69. switch (toupper( c )) {
  70. case '?':
  71. fShowUsage = TRUE;
  72. goto showUsage;
  73. break;
  74. case 'M':
  75. if (!argc--) {
  76. fShowUsage = TRUE;
  77. goto showUsage;
  78. }
  79. argv++;
  80. Mb = strtoul(*argv,&whocares,10);
  81. break;
  82. case 'C':
  83. fMemoryContention = TRUE;
  84. break;
  85. case 'T':
  86. if (!argc--) {
  87. fShowUsage = TRUE;
  88. goto showUsage;
  89. }
  90. argv++;
  91. NumberOfThreads = strtoul(*argv,&whocares,10);
  92. if ( NumberOfThreads > MAX_THREADS ) {
  93. fShowUsage = TRUE;
  94. goto showUsage;
  95. }
  96. break;
  97. default:
  98. fprintf( stderr, "MTBNCH: Invalid switch - /%c\n", c );
  99. goto showUsage;
  100. break;
  101. }
  102. }
  103. }
  104. showUsage:
  105. if ( fShowUsage ) {
  106. fprintf(stderr,"usage: MTBNCH\n" );
  107. fprintf(stderr," [-?] display this message\n" );
  108. fprintf(stderr," [-t n] use n threads for benchmark (less than 32)\n" );
  109. fprintf(stderr," [-m n] use an n Mb spreadsheet size (default 4)\n" );
  110. fprintf(stderr," [-c] cause memory contention on each loop iteration\n" );
  111. ExitProcess(1);
  112. }
  113. //
  114. // Prepare the race events. These are manual reset events.
  115. //
  116. hStartOfRace = CreateEvent(NULL,TRUE,FALSE,NULL);
  117. hEndOfRace = CreateEvent(NULL,TRUE,FALSE,NULL);
  118. if ( !hStartOfRace || !hEndOfRace ) {
  119. fprintf(stderr,"MTBNCH: Race Event Creation Failed\n");
  120. ExitProcess(1);
  121. }
  122. //
  123. // Prepare the ready done events. These are auto clearing events
  124. //
  125. for(i=0; i<NumberOfThreads; i++ ) {
  126. ThreadReadyDoneEvents[i] = CreateEvent(NULL,FALSE,FALSE,NULL);
  127. if ( !ThreadReadyDoneEvents[i] ) {
  128. fprintf(stderr,"MTBNCH: Ready Done Event Creation Failed %d\n",GetLastError());
  129. ExitProcess(1);
  130. }
  131. }
  132. //
  133. // Allocate and initialize the CellVector
  134. //
  135. CellVector = (PDWORD)VirtualAlloc(NULL,Mb*ONE_MB,MEM_COMMIT,PAGE_READWRITE);
  136. if ( !CellVector ) {
  137. fprintf(stderr,"MTBNCH: Cell Vector Allocation Failed %d\n",GetLastError());
  138. ExitProcess(1);
  139. }
  140. NumberOfDwords = (Mb*ONE_MB) / sizeof(DWORD);
  141. DwordsPerThread = NumberOfDwords / NumberOfThreads;
  142. //
  143. // Initialize the Cell Vector
  144. //
  145. for(i=0, ExpectedRecalcValue; i<NumberOfDwords; i++ ){
  146. ExpectedRecalcValue += i;
  147. CellVector[i] = i;
  148. }
  149. //
  150. // Partition the work to the worker threads
  151. //
  152. for(i=0; i<NumberOfThreads; i++ ){
  153. ThreadWork[i].CellVector = &CellVector[i*DwordsPerThread];
  154. ThreadWork[i].NumberOfCells = DwordsPerThread;
  155. NumberOfDwords -= DwordsPerThread;
  156. //
  157. // If we have a remainder, give the remaining work to the last thread
  158. //
  159. if ( NumberOfDwords < DwordsPerThread ) {
  160. ThreadWork[i].NumberOfCells += NumberOfDwords;
  161. }
  162. }
  163. //
  164. // Create the worker threads
  165. //
  166. for(i=0; i<NumberOfThreads; i++ ) {
  167. ThreadHandles[i] = CreateThread(
  168. NULL,
  169. 0,
  170. WorkerThread,
  171. (PVOID)i,
  172. 0,
  173. &ThreadId
  174. );
  175. if ( !ThreadHandles[i] ) {
  176. fprintf(stderr,"MTBNCH: Worker Thread Creation Failed %d\n",GetLastError());
  177. ExitProcess(1);
  178. }
  179. }
  180. //
  181. // All of the worker threads will signal thier ready done event
  182. // when they are idle and ready to proceed. Once all events have been
  183. // set, then setting the hStartOfRaceEvent will begin the recalc
  184. //
  185. i = WaitForMultipleObjects(
  186. NumberOfThreads,
  187. ThreadReadyDoneEvents,
  188. TRUE,
  189. INFINITE
  190. );
  191. if ( i == WAIT_FAILED ) {
  192. fprintf(stderr,"MTBNCH: Wait for threads to stabalize Failed %d\n",GetLastError());
  193. ExitProcess(1);
  194. }
  195. //
  196. // Everthing is set to begin the recalc operation
  197. //
  198. StartTicks = GetTickCount();
  199. if ( !SetEvent(hStartOfRace) ) {
  200. fprintf(stderr,"MTBNCH: SetEvent(hStartOfRace) Failed %d\n",GetLastError());
  201. ExitProcess(1);
  202. }
  203. //
  204. // Now just wait for the recalc to complete
  205. //
  206. i = WaitForMultipleObjects(
  207. NumberOfThreads,
  208. ThreadReadyDoneEvents,
  209. TRUE,
  210. INFINITE
  211. );
  212. if ( i == WAIT_FAILED ) {
  213. fprintf(stderr,"MTBNCH: Wait for threads to complete Failed %d\n",GetLastError());
  214. ExitProcess(1);
  215. }
  216. //
  217. // Now pick up the individual recalc values
  218. //
  219. for(i=0, ActualRecalcValue = 0; i<NumberOfThreads; i++ ){
  220. ActualRecalcValue += ThreadWork[i].RecalcResult;
  221. }
  222. EndTicks = GetTickCount();
  223. if ( fMemoryContention ) {
  224. if ( ContentionValue == (Mb*ONE_MB) / sizeof(DWORD) ) {
  225. if ( ActualRecalcValue == ExpectedRecalcValue ) {
  226. Answer = "Correct";
  227. }
  228. else {
  229. Answer = "Recalc Failure";
  230. }
  231. }
  232. else {
  233. Answer = "Contention Failure";
  234. }
  235. }
  236. else {
  237. if ( ActualRecalcValue == ExpectedRecalcValue ) {
  238. Answer = "Correct";
  239. }
  240. else {
  241. Answer = "Recalc Failure";
  242. }
  243. }
  244. fprintf(stdout,"MTBNCH: %d Thread Recalc complete in %dms, Answer = %s\n",
  245. NumberOfThreads,
  246. EndTicks-StartTicks,
  247. Answer
  248. );
  249. ExitProcess(2);
  250. }
  251. //
  252. // The worker threads perform the recalc operation on their
  253. // assigned cells. They begin by setting their ready done event
  254. // to indicate that they are ready to begin the recalc. Then they
  255. // wait until the hStartOfRace event is signaled. Once this occurs, they
  256. // do their part of the recalc and when done they signal their ready done
  257. // event and then wait on the hEndOfRaceEvent
  258. //
  259. DWORD
  260. WorkerThread(
  261. PVOID ThreadIndex
  262. )
  263. {
  264. DWORD Me;
  265. PDWORD MyCellVectorBase;
  266. PDWORD CurrentCellVector;
  267. DWORD MyRecalcValue;
  268. DWORD MyNumberOfCells;
  269. DWORD i;
  270. BOOL MemoryContention;
  271. Me = (DWORD)ThreadIndex;
  272. MyRecalcValue = 0;
  273. MyCellVectorBase = ThreadWork[Me].CellVector;
  274. MyNumberOfCells = ThreadWork[Me].NumberOfCells;
  275. MemoryContention = fMemoryContention;
  276. //
  277. // Signal that I am ready to go
  278. //
  279. if ( !SetEvent(ThreadReadyDoneEvents[Me]) ) {
  280. fprintf(stderr,"MTBNCH: (1) SetEvent(ThreadReadyDoneEvent[%d]) Failed %d\n",Me,GetLastError());
  281. ExitProcess(1);
  282. }
  283. //
  284. // Wait for the master to release us to do the recalc
  285. //
  286. i = WaitForSingleObject(hStartOfRace,INFINITE);
  287. if ( i == WAIT_FAILED ) {
  288. fprintf(stderr,"MTBNCH: Thread %d Wait for start of recalc Failed %d\n",Me,GetLastError());
  289. ExitProcess(1);
  290. }
  291. //
  292. // perform the recalc operation
  293. //
  294. for (i=0, CurrentCellVector = MyCellVectorBase; i<MyNumberOfCells; i++ ) {
  295. MyRecalcValue += *CurrentCellVector++;
  296. if ( MemoryContention ) {
  297. InterlockedIncrement(&ContentionValue);
  298. }
  299. }
  300. ThreadWork[Me].RecalcResult = MyRecalcValue;
  301. //
  302. // Signal that I am done and then wait for further instructions
  303. //
  304. if ( !SetEvent(ThreadReadyDoneEvents[Me]) ) {
  305. fprintf(stderr,"MTBNCH: (2) SetEvent(ThreadReadyDoneEvent[%d]) Failed %d\n",Me,GetLastError());
  306. ExitProcess(1);
  307. }
  308. i = WaitForSingleObject(hEndOfRace,INFINITE);
  309. if ( i == WAIT_FAILED ) {
  310. fprintf(stderr,"MTBNCH: Thread %d Wait for end of recalc Failed %d\n",Me,GetLastError());
  311. ExitProcess(1);
  312. }
  313. return MyRecalcValue;
  314. }