Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 372 lines (291 sloc) 8.46 kB
6fdf249 @gregkh initial import from old git tree
authored
1
2 /*
3 * This is the latest version of hackbench.c, that tests scheduler and
4 * unix-socket (or pipe) performance.
5 *
6 * Usage: hackbench [-pipe] <num groups> [process|thread] [loops]
7 *
8 * Build it with:
9 * gcc -g -Wall -O2 -o hackbench hackbench.c -lpthread
10 */
11 #if 0
12
13 Date: Fri, 04 Jan 2008 14:06:26 +0800
14 From: "Zhang, Yanmin" <yanmin_zhang@linux.intel.com>
15 To: LKML <linux-kernel@vger.kernel.org>
16 Subject: Improve hackbench
17 Cc: Ingo Molnar <mingo@elte.hu>, Arjan van de Ven <arjan@infradead.org>
18
19 hackbench tests the Linux scheduler. The original program is at
20 http://devresources.linux-foundation.org/craiger/hackbench/src/hackbench.c
21 Based on this multi-process version, a nice person created a multi-thread
22 version. Pls. see
23 http://www.bullopensource.org/posix/pi-futex/hackbench_pth.c
24
25 When I integrated them into my automation testing system, I found
26 a couple of issues and did some improvements.
27
28 1) Merge hackbench: I integrated hackbench_pth.c into hackbench and added a
29 new parameter which can be used to choose process mode or thread mode. The
30 default mode is process.
31
32 2) It runs too fast and ends in a couple of seconds. Sometimes it's too hard to debug
33 the issues. On my ia64 Montecito machines, the result looks weird when comparing
34 process mode and thread mode.
35 I want a stable result and hope the testing could run for a stable longer time, so I
36 might use performance tools to debug issues.
37 I added another new parameter,`loops`, which can be used to change variable loops,
38 so more messages will be passed from writers to receivers. Parameter 'loops' is equal to
39 100 by default.
40
41 For example on my 8-core x86_64:
42 [ymzhang@lkp-st01-x8664 hackbench]$ uname -a
43 Linux lkp-st01-x8664 2.6.24-rc6 #1 SMP Fri Dec 21 08:32:31 CST 2007 x86_64 x86_64 x86_64 GNU/Linux
44 [ymzhang@lkp-st01-x8664 hackbench]$ ./hackbench
45 Usage: hackbench [-pipe] <num groups> [process|thread] [loops]
46 [ymzhang@lkp-st01-x8664 hackbench]$ ./hackbench 150 process 1000
47 Time: 151.533
48 [ymzhang@lkp-st01-x8664 hackbench]$ ./hackbench 150 thread 1000
49 Time: 153.666
50
51
52 With the same new parameters, I did captured the SLUB issue discussed on LKML recently.
53
54 3) hackbench_pth.c will fail on ia64 machine because pthread_attr_setstacksize always
55 fails if the stack size is less than 196*1024. I moved this statement within a __ia64__ check.
56
57
58 This new program could be compiled with command line:
59 #gcc -g -Wall -o hackbench hackbench.c -lpthread
60
61
62 Thank Ingo for his great comments!
63
64 -yanmin
65
66 ---
67
68 #endif
69
70 /* Test groups of 20 processes spraying to 20 receivers */
71 #include <pthread.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <errno.h>
76 #include <unistd.h>
77 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <sys/wait.h>
80 #include <sys/time.h>
81 #include <sys/poll.h>
82
83 #define DATASIZE 100
84 static unsigned int loops = 100;
85 /*
86 * 0 means thread mode and others mean process (default)
87 */
88 static unsigned int process_mode = 1;
89
90 static int use_pipes = 0;
91
92 struct sender_context {
93 unsigned int num_fds;
94 int ready_out;
95 int wakefd;
96 int out_fds[0];
97 };
98
99 struct receiver_context {
100 unsigned int num_packets;
101 int in_fds[2];
102 int ready_out;
103 int wakefd;
104 };
105
106
107 static void barf(const char *msg)
108 {
109 fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
110 exit(1);
111 }
112
113 static void print_usage_exit()
114 {
115 printf("Usage: hackbench [-pipe] <num groups> [process|thread] [loops]\n");
116 exit(1);
117 }
118
119 static void fdpair(int fds[2])
120 {
121 if (use_pipes) {
122 if (pipe(fds) == 0)
123 return;
124 } else {
125 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
126 return;
127 }
128 barf("Creating fdpair");
129 }
130
131 /* Block until we're ready to go */
132 static void ready(int ready_out, int wakefd)
133 {
134 char dummy;
135 struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
136
137 /* Tell them we're ready. */
138 if (write(ready_out, &dummy, 1) != 1)
139 barf("CLIENT: ready write");
140
141 /* Wait for "GO" signal */
142 if (poll(&pollfd, 1, -1) != 1)
143 barf("poll");
144 }
145
146 /* Sender sprays loops messages down each file descriptor */
147 static void *sender(struct sender_context *ctx)
148 {
149 char data[DATASIZE];
150 unsigned int i, j;
151
152 ready(ctx->ready_out, ctx->wakefd);
153
154 /* Now pump to every receiver. */
155 for (i = 0; i < loops; i++) {
156 for (j = 0; j < ctx->num_fds; j++) {
157 int ret, done = 0;
158
159 again:
160 ret = write(ctx->out_fds[j], data + done, sizeof(data)-done);
161 if (ret < 0)
162 barf("SENDER: write");
163 done += ret;
164 if (done < sizeof(data))
165 goto again;
166 }
167 }
168
169 return NULL;
170 }
171
172
173 /* One receiver per fd */
174 static void *receiver(struct receiver_context* ctx)
175 {
176 unsigned int i;
177
178 if (process_mode)
179 close(ctx->in_fds[1]);
180
181 /* Wait for start... */
182 ready(ctx->ready_out, ctx->wakefd);
183
184 /* Receive them all */
185 for (i = 0; i < ctx->num_packets; i++) {
186 char data[DATASIZE];
187 int ret, done = 0;
188
189 again:
190 ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
191 if (ret < 0)
192 barf("SERVER: read");
193 done += ret;
194 if (done < DATASIZE)
195 goto again;
196 }
197
198 return NULL;
199 }
200
201 pthread_t create_worker(void *ctx, void *(*func)(void *))
202 {
203 pthread_attr_t attr;
204 pthread_t childid;
205 int err;
206
207 if (process_mode) {
208 /* process mode */
209 /* Fork the receiver. */
210 switch (fork()) {
211 case -1: barf("fork()");
212 case 0:
213 (*func) (ctx);
214 exit(0);
215 }
216
217 return (pthread_t) 0;
218 }
219
220 if (pthread_attr_init(&attr) != 0)
221 barf("pthread_attr_init:");
222
223 #ifndef __ia64__
224 if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
225 barf("pthread_attr_setstacksize");
226 #endif
227
228 if ((err=pthread_create(&childid, &attr, func, ctx)) != 0) {
229 fprintf(stderr, "pthread_create failed: %s (%d)\n", strerror(err), err);
230 exit(-1);
231 }
232 return (childid);
233 }
234
235 void reap_worker(pthread_t id)
236 {
237 int status;
238
239 if (process_mode) {
240 /* process mode */
241 wait(&status);
242 if (!WIFEXITED(status))
243 exit(1);
244 } else {
245 void *status;
246
247 pthread_join(id, &status);
248 }
249 }
250
251 /* One group of senders and receivers */
252 static unsigned int group(pthread_t *pth,
253 unsigned int num_fds,
254 int ready_out,
255 int wakefd)
256 {
257 unsigned int i;
258 struct sender_context* snd_ctx = malloc (sizeof(struct sender_context)
259 +num_fds*sizeof(int));
260
261 for (i = 0; i < num_fds; i++) {
262 int fds[2];
263 struct receiver_context* ctx = malloc (sizeof(*ctx));
264
265 if (!ctx)
266 barf("malloc()");
267
268
269 /* Create the pipe between client and server */
270 fdpair(fds);
271
272 ctx->num_packets = num_fds*loops;
273 ctx->in_fds[0] = fds[0];
274 ctx->in_fds[1] = fds[1];
275 ctx->ready_out = ready_out;
276 ctx->wakefd = wakefd;
277
278 pth[i] = create_worker(ctx, (void *)(void *)receiver);
279
280 snd_ctx->out_fds[i] = fds[1];
281 if (process_mode)
282 close(fds[0]);
283 }
284
285 /* Now we have all the fds, fork the senders */
286 for (i = 0; i < num_fds; i++) {
287 snd_ctx->ready_out = ready_out;
288 snd_ctx->wakefd = wakefd;
289 snd_ctx->num_fds = num_fds;
290
291 pth[num_fds+i] = create_worker(snd_ctx, (void *)(void *)sender);
292 }
293
294 /* Close the fds we have left */
295 if (process_mode)
296 for (i = 0; i < num_fds; i++)
297 close(snd_ctx->out_fds[i]);
298
299 /* Return number of children to reap */
300 return num_fds * 2;
301 }
302
303 int main(int argc, char *argv[])
304 {
305 unsigned int i, num_groups = 10, total_children;
306 struct timeval start, stop, diff;
307 unsigned int num_fds = 20;
308 int readyfds[2], wakefds[2];
309 char dummy;
310 pthread_t *pth_tab;
311
312 if (argv[1] && strcmp(argv[1], "-pipe") == 0) {
313 use_pipes = 1;
314 argc--;
315 argv++;
316 }
317
318 if (argc >= 2 && (num_groups = atoi(argv[1])) == 0)
319 print_usage_exit();
320
321 printf("Running with %d*40 (== %d) tasks.\n",
322 num_groups, num_groups*40);
323
324 if (argc > 2) {
325 if ( !strcmp(argv[2], "process") )
326 process_mode = 1;
327 else if ( !strcmp(argv[2], "thread") )
328 process_mode = 0;
329 else
330 print_usage_exit();
331 }
332
333 if (argc > 3)
334 loops = atoi(argv[3]);
335
336 pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
337
338 if (!pth_tab)
339 barf("main:malloc()");
340
341 fdpair(readyfds);
342 fdpair(wakefds);
343
344 total_children = 0;
345 for (i = 0; i < num_groups; i++)
346 total_children += group(pth_tab+total_children, num_fds, readyfds[1], wakefds[0]);
347
348 /* Wait for everyone to be ready */
349 for (i = 0; i < total_children; i++)
350 if (read(readyfds[0], &dummy, 1) != 1)
351 barf("Reading for readyfds");
352
353 gettimeofday(&start, NULL);
354
355 /* Kick them off */
356 if (write(wakefds[1], &dummy, 1) != 1)
357 barf("Writing to start them");
358
359 /* Reap them all */
360 for (i = 0; i < total_children; i++)
361 reap_worker(pth_tab[i]);
362
363 gettimeofday(&stop, NULL);
364
365 /* Print time... */
366 timersub(&stop, &start, &diff);
367 printf("Time: %lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000);
368 exit(0);
369 }
370
371
Something went wrong with that request. Please try again.