1 //========================================================================
2 // Multithreading benchmark program, based on the GLFW multi threading
3 // support.
4 //
5 // This program can be used to get an idea of what to expect in terms of
6 // multithreading granularity performance.
7 //
8 // As a "bonus", this program demonstrates how to create a signal
9 // primitive using the GLFW mutex and condition variable primitives.
10 //
11 // Here are some benchmark results:
12 // (Note: these are not exact measurments, since they are subject to
13 // varying CPU-loads etc. Some tested systems are multi-user systems
14 // which were running under anything but optimal conditions)
15 //
16 // +------------+-------+-------------+-------------------+------------+
17 // | Processor | CPUs | OS | Context switches | Mean sleep |
18 // | | | | per second | time (ms) |
19 // +------------+-------+-------------+-------------------+------------+
20 // |Athlon | 1 | Linux | 161942 | 20.000 |
21 // |710 MHz | | 2.4.3 | | |
22 // +------------+-------+-------------+-------------------+------------+
23 // |Athlon | 1 | MS Win2k | 525230 | 10.014 |
24 // |710 MHz | | | | |
25 // +------------+-------+-------------+-------------------+------------+
26 // |Athlon | 1 | MS Win 98 | 23564 | 4.947 |
27 // |710 MHz | | | | |
28 // +------------+-------+-------------+-------------------+------------+
29 // |Pentium III | 1 | MS NT 4.0 | 304694 | 10.014 |
30 // |500 MHz | | | | |
31 // +------------+-------+-------------+-------------------+------------+
32 // |UltraSPARC2 | 6 | SunOS 5.6 | 120867 | 19.355 |
33 // |400 MHz | | | | |
34 // +------------+-------+-------------+-------------------+------------+
35 // |Alpha 21264 | 1 | OSF1 | 131993 | 3.097 |
36 // |500 MHz | | | | |
37 // +------------+-------+-------------+-------------------+------------+
38 // |Alpha 21264 | 2 | OSF1 | 40836 | 1.397 |
39 // |500 MHz | | | | |
40 // +------------+-------+-------------+-------------------+------------+
41 // |68020 (emu) | 1 | AmigaOS 3.1 | 50425 | 40.060 |
42 // |~200 MHz | | (WinUAE) | | |
43 // +------------+-------+-------------+-------------------+------------+
44 //
45 //========================================================================
46
47 import glfw;
48 import std.c.stdio;
49
50 struct signal_t {
51 GLFWcond cond;
52 GLFWmutex mutex;
53 int flag;
54 }
55
56 signal_t gotoA, gotoB;
57
58 GLFWcond threadDone;
59 GLFWmutex doneMutex;
60 int doneCount = 0;
61 int gotoACount = 0;
62 int gotoBCount = 0;
63
64 const int MAX_COUNT = 10000;
65
66
67 //------------------------------------------------------------------------
68 // InitSignal()
69 //------------------------------------------------------------------------
70
InitSignal(signal_t * s)71 void InitSignal( signal_t *s )
72 {
73 s.cond = glfwCreateCond();
74 s.mutex = glfwCreateMutex();
75 s.flag = 0;
76 }
77
78
79 //------------------------------------------------------------------------
80 // KillSignal()
81 //------------------------------------------------------------------------
82
KillSignal(signal_t * s)83 void KillSignal( signal_t *s )
84 {
85 glfwDestroyCond( s.cond );
86 glfwDestroyMutex( s.mutex );
87 s.flag = 0;
88 }
89
90
91 //------------------------------------------------------------------------
92 // WaitSignal()
93 //------------------------------------------------------------------------
94
WaitSignal(signal_t * s)95 void WaitSignal( signal_t *s )
96 {
97 glfwLockMutex( s.mutex );
98 while( !s.flag )
99 {
100 glfwWaitCond( s.cond, s.mutex, GLFW_INFINITY );
101 }
102 s.flag = 0;
103 glfwUnlockMutex( s.mutex );
104 }
105
106
107 //------------------------------------------------------------------------
108 // SetSignal()
109 //------------------------------------------------------------------------
110
SetSignal(signal_t * s)111 void SetSignal( signal_t *s )
112 {
113 glfwLockMutex( s.mutex );
114 s.flag = 1;
115 glfwUnlockMutex( s.mutex );
116 glfwSignalCond( s.cond );
117 }
118
119
120 //------------------------------------------------------------------------
121 // main()
122 //------------------------------------------------------------------------
123
main()124 int main( )
125 {
126 GLFWthread threadA, threadB;
127 double t1, t2, csps;
128 int done, count, i;
129
130 gotoACount = gotoBCount = doneCount = 0;
131
132 // Initialize GLFW
133 if( !glfwInit() )
134 {
135 return 0;
136 }
137
138 // Print some program information
139 printf( "\nMultithreading benchmarking program\n" );
140 printf( "-----------------------------------\n\n" );
141 printf( "This program consists of two tests. In the first test " );
142 printf( "two threads are created,\n" );
143 printf( "which continously signal/wait each other. This forces " );
144 printf( "the execution to\n" );
145 printf( "alternate between the two threads, and gives a measure " );
146 printf( "of the thread\n" );
147 printf( "synchronization granularity. In the second test, the " );
148 printf( "main thread is repeatedly\n" );
149 printf( "put to sleep for a very short interval using glfwSleep. " );
150 printf( "The average sleep time\n" );
151 printf( "is measured, which tells the minimum supported sleep " );
152 printf( "interval.\n\n" );
153 printf( "Results:\n" );
154 printf( "--------\n\n" );
155 printf( "Number of CPUs: %d\n\n", glfwGetNumberOfProcessors() );
156 fflush( stdout );
157
158
159 //------------------------------------------------------------------------
160 // 1) Benchmark thread synchronization granularity
161 //------------------------------------------------------------------------
162
163 // Init mutexes and conditions
164 doneMutex = glfwCreateMutex();
165 threadDone = glfwCreateCond();
166 InitSignal( &gotoA );
167 InitSignal( &gotoB );
168
169 // Create threads A & B
170 threadA = glfwCreateThread( &threadAfun, null );
171 threadB = glfwCreateThread( &threadBfun, null );
172 if( threadA == -1 || threadB == -1 )
173 {
174 glfwLockMutex( doneMutex );
175 doneCount = 2;
176 glfwUnlockMutex( doneMutex );
177 }
178
179 // Wait for both threads to be done
180 t1 = glfwGetTime();
181 glfwLockMutex( doneMutex );
182 do
183 {
184 done = (doneCount == 2);
185 if( !done )
186 {
187 glfwWaitCond( threadDone, doneMutex, GLFW_INFINITY );
188 }
189 }
190 while( !done );
191 glfwUnlockMutex( doneMutex );
192 t2 = glfwGetTime();
193
194 // Display results
195 count = gotoACount + gotoBCount;
196 csps = cast(double)count / (t2-t1);
197 printf( "Test 1: %.0f context switches / second (%.3f us/switch)\n",
198 csps, 1e6/csps );
199 fflush( stdout );
200
201 // Wait for threads to die
202 glfwWaitThread( threadA, GLFW_WAIT );
203 glfwWaitThread( threadB, GLFW_WAIT );
204
205 // Destroy mutexes and conditions
206 glfwDestroyMutex( doneMutex );
207 glfwDestroyCond( threadDone );
208 KillSignal( &gotoA );
209 KillSignal( &gotoB );
210
211
212 //------------------------------------------------------------------------
213 // 2) Benchmark thread sleep granularity
214 //------------------------------------------------------------------------
215
216 // Find an initial estimate
217 t1 = glfwGetTime();
218 for( i = 0; i < 10; i ++ )
219 {
220 glfwSleep( 0.0001 );
221 }
222 t2 = glfwGetTime();
223
224 // Sleep for roughly 1 s
225 count = cast(int)(1.0 / ((t2-t1)/10.0));
226 t1 = glfwGetTime();
227 for( i = 0; i < count; i ++ )
228 {
229 glfwSleep( 0.0001 );
230 }
231 t2 = glfwGetTime();
232
233 // Display results
234 printf( "Test 2: %.3f ms / sleep (mean)\n\n",
235 1000.0 * (t2-t1) / cast(double)count );
236
237 // Terminate GLFW
238 glfwTerminate();
239
240 return 0;
241 }
242
243
version(Windows)244 version(Windows) {
245 extern(Windows):
246 } else {
247 extern(C):
248 }
249 //------------------------------------------------------------------------
250 // threadAfun()
251 //------------------------------------------------------------------------
252
threadAfun(void * arg)253 void threadAfun( void * arg )
254 {
255 int done;
256
257 do
258 {
259 done = (gotoACount >= MAX_COUNT);
260 if( !done )
261 {
262 gotoACount ++;
263 SetSignal( &gotoB );
264 WaitSignal( &gotoA );
265 }
266 }
267 while( !done );
268
269 SetSignal( &gotoB );
270
271 glfwLockMutex( doneMutex );
272 doneCount ++;
273 glfwUnlockMutex( doneMutex );
274 glfwSignalCond( threadDone );
275 }
276
277
278 //------------------------------------------------------------------------
279 // threadBfun()
280 //------------------------------------------------------------------------
281
threadBfun(void * arg)282 void threadBfun( void * arg )
283 {
284 int done;
285
286 do
287 {
288 done = (gotoBCount >= MAX_COUNT);
289 if( !done )
290 {
291 gotoBCount ++;
292 SetSignal( &gotoA );
293 WaitSignal( &gotoB );
294 }
295 }
296 while( !done );
297
298 SetSignal( &gotoA );
299
300 glfwLockMutex( doneMutex );
301 doneCount ++;
302 glfwUnlockMutex( doneMutex );
303 glfwSignalCond( threadDone );
304 }
305