2009-11-01 04:10:26

by Hitoshi Mitake

[permalink] [raw]
Subject: [PATCH][RFC] Adding hackbench.c to tools/


Hi Ingo,

It seems that hackbench is the de-fact standard benchmarking program for scheduler of Linux.
But when I ask google where hackbench.c is,
some answers are replied. Like these, (in order of google result)

http://devresources.linux-foundation.org/craiger/hackbench/ (this page containts link to hackbench.c)
http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c

And it seems that second one is newer.

I think this situation is very confusable.
So I wrote this patch to add hackbench to tools/ of kernel tree.
This may help hackbench users like me.

And hackbench this patch contains has little improvements.
1) Output
Output is single float value, with out "Time: " string.
I believe this is more friendly form for processing by script languages.

2) Option processing
I don't like original option processing of hackbench,
because it requires -pipe when I want to specify num of groups.
I rewrote option processing with getopt_long().

3) checkpatch.pl friendly

Signed-off-by: Hitoshi Mitake <[email protected]>


diff --git a/tools/hackbench/Makefile b/tools/hackbench/Makefile
new file mode 100644
index 0000000..11da1b6
--- /dev/null
+++ b/tools/hackbench/Makefile
@@ -0,0 +1,3 @@
+
+hackbench: hackbench.c
+ gcc -o hackbench hackbench.c -lpthread -Wall -O2
diff --git a/tools/hackbench/hackbench.c b/tools/hackbench/hackbench.c
new file mode 100644
index 0000000..3790d36
--- /dev/null
+++ b/tools/hackbench/hackbench.c
@@ -0,0 +1,337 @@
+/*
+ * hackbench.c
+ *
+ * Test program for measuring performance of
+ * scheduler and unix-socket (or pipe).
+ */
+
+/* Test groups of 20 processes spraying to 20 receivers */
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <limits.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#define DATASIZE 100
+static unsigned int loops = 100;
+/*
+ * 0 means thread mode and others mean process (default)
+ */
+static unsigned int process_mode = 1;
+
+static int use_pipes; /* default:0 */
+
+struct sender_context {
+ unsigned int num_fds;
+ int ready_out;
+ int wakefd;
+ int out_fds[0];
+};
+
+struct receiver_context {
+ unsigned int num_packets;
+ int in_fds[2];
+ int ready_out;
+ int wakefd;
+};
+
+
+static void barf(const char *msg)
+{
+ fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
+ exit(1);
+}
+
+static void print_usage_exit(void)
+{
+ printf("Usage: hackbench <option list>\n");
+ printf("\tlist of available options...\n");
+ printf("\t-p | --pipe\t\t: Use pipe() instead of socketpair()\n");
+ printf("\t-t | --thread\t\t: Be multi thread instead of"
+ "multi process\n");
+ printf("\t-g | --group <num>\t: Specify number of groups\n");
+ printf("\t-l | --loops <num>\t: Specify number of loops\n");
+ printf("\t-h | --help\t\t: Print this help\n");
+
+ exit(1);
+}
+
+static void fdpair(int fds[2])
+{
+ if (use_pipes) {
+ if (pipe(fds) == 0)
+ return;
+ } else {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
+ return;
+ }
+ barf("Creating fdpair");
+}
+
+/* Block until we're ready to go */
+static void ready(int ready_out, int wakefd)
+{
+ char dummy;
+ struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
+
+ /* Tell them we're ready. */
+ if (write(ready_out, &dummy, 1) != 1)
+ barf("CLIENT: ready write");
+
+ /* Wait for "GO" signal */
+ if (poll(&pollfd, 1, -1) != 1)
+ barf("poll");
+}
+
+/* Sender sprays loops messages down each file descriptor */
+static void *sender(struct sender_context *ctx)
+{
+ char data[DATASIZE];
+ unsigned int i, j;
+
+ ready(ctx->ready_out, ctx->wakefd);
+
+ /* Now pump to every receiver. */
+ for (i = 0; i < loops; i++) {
+ for (j = 0; j < ctx->num_fds; j++) {
+ int ret, done = 0;
+
+again:
+ ret = write(ctx->out_fds[j], data + done,
+ sizeof(data)-done);
+ if (ret < 0)
+ barf("SENDER: write");
+ done += ret;
+ if (done < sizeof(data))
+ goto again;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* One receiver per fd */
+static void *receiver(struct receiver_context* ctx)
+{
+ unsigned int i;
+
+ if (process_mode)
+ close(ctx->in_fds[1]);
+
+ /* Wait for start... */
+ ready(ctx->ready_out, ctx->wakefd);
+
+ /* Receive them all */
+ for (i = 0; i < ctx->num_packets; i++) {
+ char data[DATASIZE];
+ int ret, done = 0;
+
+again:
+ ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
+ if (ret < 0)
+ barf("SERVER: read");
+ done += ret;
+ if (done < DATASIZE)
+ goto again;
+ }
+
+ return NULL;
+}
+
+pthread_t create_worker(void *ctx, void *(*func)(void *))
+{
+ pthread_attr_t attr;
+ pthread_t childid;
+ int err;
+
+ if (process_mode) {
+ /* process mode */
+ /* Fork the receiver. */
+ switch (fork()) {
+ case -1:
+ barf("fork()");
+ break;
+ case 0:
+ (*func) (ctx);
+ exit(0);
+ break;
+ }
+
+ return (pthread_t) 0;
+ }
+
+ if (pthread_attr_init(&attr) != 0)
+ barf("pthread_attr_init:");
+
+#ifndef __ia64__
+ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
+ barf("pthread_attr_setstacksize");
+#endif
+
+ err = pthread_create(&childid, &attr, func, ctx);
+ if (err != 0) {
+ fprintf(stderr, "pthread_create failed: %s (%d)\n",
+ strerror(err), err);
+ exit(-1);
+ }
+ return childid;
+}
+
+void reap_worker(pthread_t id)
+{
+ int status;
+
+ if (process_mode) {
+ /* process mode */
+ wait(&status);
+ if (!WIFEXITED(status))
+ exit(1);
+ } else {
+ void *status;
+
+ pthread_join(id, &status);
+ }
+}
+
+/* One group of senders and receivers */
+static unsigned int group(pthread_t *pth,
+ unsigned int num_fds,
+ int ready_out,
+ int wakefd)
+{
+ unsigned int i;
+ struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
+ + num_fds * sizeof(int));
+
+ for (i = 0; i < num_fds; i++) {
+ int fds[2];
+ struct receiver_context *ctx = malloc(sizeof(*ctx));
+
+ if (!ctx)
+ barf("malloc()");
+
+
+ /* Create the pipe between client and server */
+ fdpair(fds);
+
+ ctx->num_packets = num_fds*loops;
+ ctx->in_fds[0] = fds[0];
+ ctx->in_fds[1] = fds[1];
+ ctx->ready_out = ready_out;
+ ctx->wakefd = wakefd;
+
+ pth[i] = create_worker(ctx, (void *)(void *)receiver);
+
+ snd_ctx->out_fds[i] = fds[1];
+ if (process_mode)
+ close(fds[0]);
+ }
+
+ /* Now we have all the fds, fork the senders */
+ for (i = 0; i < num_fds; i++) {
+ snd_ctx->ready_out = ready_out;
+ snd_ctx->wakefd = wakefd;
+ snd_ctx->num_fds = num_fds;
+
+ pth[num_fds+i] = create_worker(snd_ctx, (void *)(void *)sender);
+ }
+
+ /* Close the fds we have left */
+ if (process_mode)
+ for (i = 0; i < num_fds; i++)
+ close(snd_ctx->out_fds[i]);
+
+ /* Return number of children to reap */
+ return num_fds * 2;
+}
+
+struct option longopts[] = {
+ { "pipe", no_argument, NULL, 'p'},
+ { "thread", no_argument, NULL, 't'},
+ { "group", required_argument, NULL, 'g'},
+ { "loop", required_argument, NULL, 'l'},
+ { "help", no_argument, NULL, 'h'},
+ { NULL, 0, NULL, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ unsigned int i, num_groups = 10, total_children;
+ struct timeval start, stop, diff;
+ unsigned int num_fds = 20;
+ int readyfds[2], wakefds[2];
+ char dummy;
+ pthread_t *pth_tab;
+
+ int opt;
+ extern char *optarg;
+
+ while ((opt = getopt_long(argc, argv, "ptg:l:h", longopts, NULL))
+ != -1) {
+ switch (opt) {
+ case 'p':
+ use_pipes = 1;
+ break;
+ case 't':
+ process_mode = 0;
+ break;
+ case 'g':
+ num_groups = atoi(optarg);
+ break;
+ case 'l':
+ loops = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ print_usage_exit();
+ exit(1);
+ break;
+ }
+ }
+
+ pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
+
+ if (!pth_tab)
+ barf("main:malloc()");
+
+ fdpair(readyfds);
+ fdpair(wakefds);
+
+ total_children = 0;
+ for (i = 0; i < num_groups; i++)
+ total_children += group(pth_tab+total_children, num_fds,
+ readyfds[1], wakefds[0]);
+
+ /* Wait for everyone to be ready */
+ for (i = 0; i < total_children; i++)
+ if (read(readyfds[0], &dummy, 1) != 1)
+ barf("Reading for readyfds");
+
+ gettimeofday(&start, NULL);
+
+ /* Kick them off */
+ if (write(wakefds[1], &dummy, 1) != 1)
+ barf("Writing to start them");
+
+ /* Reap them all */
+ for (i = 0; i < total_children; i++)
+ reap_worker(pth_tab[i]);
+
+ gettimeofday(&stop, NULL);
+
+ /* Print time... */
+ timersub(&stop, &start, &diff);
+ printf("%lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000);
+ exit(0);
+}


2009-11-02 10:41:44

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH][RFC] Adding hackbench.c to tools/


* Hitoshi Mitake <[email protected]> wrote:

> Hi Ingo,
>
> It seems that hackbench is the de-fact standard benchmarking program
> for scheduler of Linux. But when I ask google where hackbench.c is,
> some answers are replied. Like these, (in order of google result)
>
> http://devresources.linux-foundation.org/craiger/hackbench/ (this page
> containts link to hackbench.c)
> http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c
>
> And it seems that second one is newer.
>
> I think this situation is very confusable. So I wrote this patch to
> add hackbench to tools/ of kernel tree. This may help hackbench users
> like me.

I think we can do something nicer: would you be interested in adding it
as a 'perf bench hackbench' sub-command of tools/perf/?

We already have the tools to measure scheduling behavior under 'perf
sched', so having a 'perf bench' array of common tests would be nice to
have.

( I've Cc:-ed Rusty, the original author of hackbench.c. Rusty, the file
has no explicit GPLv2 compatible license - is it fine to be put
into GPLv2 code? )

Adding a new perf sub-command is easy:

create a new tools/perf/builtin-bench.c file and copy hackbench.c to it,
add it to command-list.txt and to the Makefile - add it to perf.c's
array of built-in commands and [optional] add a
Documentation/perf-bench.txt file to generate manpages and usage strings
for it.

Change the 'main' function to cmd_bench() and add option parsing like
you can see in the other builtin-*.c files. This should get you going.

Eventually we'd add more scheduler (and other) benchmarks too, not just
hackbench. I'd also suggest to name it not 'hackbench' but something
more generic, like:

perf bench sched messaging

So that we can have subsystem identifier first, then the type of
benchmark.

Ingo

2009-11-02 13:27:36

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH][RFC] Adding hackbench.c to tools/

On Mon, 2 Nov 2009 09:11:24 pm Ingo Molnar wrote:
>
> * Hitoshi Mitake <[email protected]> wrote:
>
> > Hi Ingo,
> >
> > It seems that hackbench is the de-fact standard benchmarking program
> > for scheduler of Linux. But when I ask google where hackbench.c is,
> > some answers are replied. Like these, (in order of google result)
> >
> > http://devresources.linux-foundation.org/craiger/hackbench/ (this page
> > containts link to hackbench.c)
> > http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c
> >
> > And it seems that second one is newer.
> >
> > I think this situation is very confusable. So I wrote this patch to
> > add hackbench to tools/ of kernel tree. This may help hackbench users
> > like me.
>
> I think we can do something nicer: would you be interested in adding it
> as a 'perf bench hackbench' sub-command of tools/perf/?
>
> We already have the tools to measure scheduling behavior under 'perf
> sched', so having a 'perf bench' array of common tests would be nice to
> have.
>
> ( I've Cc:-ed Rusty, the original author of hackbench.c. Rusty, the file
> has no explicit GPLv2 compatible license - is it fine to be put
> into GPLv2 code? )

Yep, like all my code it's standard v2 "or later".

Sounds like a good plan, thanks!
Rusty.

2009-11-02 13:51:30

by Hitoshi Mitake

[permalink] [raw]
Subject: [PATCH][RFC] Adding benchmark subsystem to perf

From: Rusty Russell <[email protected]>
Subject: Re: [PATCH][RFC] Adding hackbench.c to tools/
Date: Mon, 2 Nov 2009 23:57:36 +1030

> ,
> Arnaldo Carvalho de Melo <[email protected]>,
> Fr?d?ric_Weisbecker <[email protected]>,
> Mike Galbraith <[email protected]>
> Date: Mon, 2 Nov 2009 23:57:36 +1030
> User-Agent: KMail/1.12.2 (Linux/2.6.31-14-generic; KDE/4.3.2; i686; ; )
>
> On Mon, 2 Nov 2009 09:11:24 pm Ingo Molnar wrote:
> >
> > * Hitoshi Mitake <[email protected]> wrote:
> >
> > > Hi Ingo,
> > >
> > > It seems that hackbench is the de-fact standard benchmarking program
> > > for scheduler of Linux. But when I ask google where hackbench.c is,
> > > some answers are replied. Like these, (in order of google result)
> > >
> > > http://devresources.linux-foundation.org/craiger/hackbench/ (this page
> > > containts link to hackbench.c)
> > > http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c
> > >
> > > And it seems that second one is newer.
> > >
> > > I think this situation is very confusable. So I wrote this patch to
> > > add hackbench to tools/ of kernel tree. This may help hackbench users
> > > like me.
> >
> > I think we can do something nicer: would you be interested in adding it
> > as a 'perf bench hackbench' sub-command of tools/perf/?
> >
> > We already have the tools to measure scheduling behavior under 'perf
> > sched', so having a 'perf bench' array of common tests would be nice to
> > have.
> >
> > ( I've Cc:-ed Rusty, the original author of hackbench.c. Rusty, the file
> > has no explicit GPLv2 compatible license - is it fine to be put
> > into GPLv2 code? )

I should send the mail to Rusty too. Sorry.

>
> Yep, like all my code it's standard v2 "or later".
>
> Sounds like a good plan, thanks!
> Rusty.
>

Thanks for your permission!
I wrote a patch. This adds builtin-bench.c and its document
perf-bench.txt to tools/perf/Documentation/.

How about this?

--- below is the patch and description ---

Adding general performance benchmarking subsystem and its document to perf.
This provides benchmark for scheduler based on Rusty Russel's hackbench.

New hackbench this patch containts has little improvements.
1) Output
Output is single float value, with out "Time: " string.
I believe this is more friendly form for processing by script languages.

2) Option processing
I don't like original option processing of hackbench,
because it requires -pipe when I want to specify num of groups.
I rewrote option processing with getopt_long().

Example of usage:
% perf bench sched -t -l 1000
2.863

Signed-off-by: Hitoshi Mitake <[email protected]>
Cc: Rusty Russell <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Peter Zijlstra <[email protected]>

diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
new file mode 100644
index 0000000..88f9451
--- /dev/null
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -0,0 +1,49 @@
+perf-bench(1)
+==============
+
+NAME
+----
+perf-bench - General benchmarking subsystem provided by perf
+
+SYNOPSIS
+--------
+[verse]
+'perf bench' <subsystem> [<options>]
+
+DESCRIPTION
+-----------
+This command measures performance of various kernel subsystems.
+
+This is the list of supported subsystems:
+
+'sched': Scheduler and IPC mechanisms. Result is time and printed to
+standard output. This benchmark is based on hackbench.
+
+OPTIONS
+-------
+
+<subsystem>::
+ Specify subsystem.
+
+'sched' specific
+----------------
+
+-p::
+--pipe::
+ Use pipe() instead of socketpair().
+
+-t::
+--thread::
+ Be multi thread instead of multi process.
+
+-g <number of groups>::
+--group=<number of groups>::
+ Specify number of groups.
+
+-l <number of loops>::
+--loop=<number of loops>::
+ Specify number of loops.
+
+SEE ALSO
+--------
+linkperf:perf-stat[1]
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 742a32e..6786f0f 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -392,6 +392,7 @@ BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-timechart.o
BUILTIN_OBJS += builtin-top.o
BUILTIN_OBJS += builtin-trace.o
+BUILTIN_OBJS += builtin-bench.o

PERFLIBS = $(LIB_FILE)

diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
new file mode 100644
index 0000000..8d139d1
--- /dev/null
+++ b/tools/perf/builtin-bench.c
@@ -0,0 +1,355 @@
+/*
+ *
+ * builtin-bench.c
+ *
+ * General benchmarking subsystem provided by perf
+ *
+ * Based on hackbench by Rusty Russell <[email protected]>
+ * Ported to perf by Hitoshi Mitake <[email protected]>
+ *
+ */
+
+/*
+ *
+ * Available benchmarking list:
+ * sched ... evaluating performance of scheduler and IPC mechanism
+ *
+ */
+
+#include "perf.h"
+#include "util/util.h"
+#include "util/parse-options.h"
+#include "builtin.h"
+
+/* Test groups of 20 processes spraying to 20 receivers */
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include <assert.h>
+
+#define DATASIZE 100
+
+static int use_pipes = 0;
+static unsigned int loops = 100;
+static unsigned int thread_mode = 0;
+static unsigned int num_groups = 10;
+
+struct sender_context {
+ unsigned int num_fds;
+ int ready_out;
+ int wakefd;
+ int out_fds[0];
+};
+
+struct receiver_context {
+ unsigned int num_packets;
+ int in_fds[2];
+ int ready_out;
+ int wakefd;
+};
+
+static void barf(const char *msg)
+{
+ fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
+ exit(1);
+}
+
+static void fdpair(int fds[2])
+{
+ if (use_pipes) {
+ if (pipe(fds) == 0)
+ return;
+ } else {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
+ return;
+ }
+
+ barf(use_pipes ? "pipe()" : "socketpair()");
+}
+
+/* Block until we're ready to go */
+static void ready(int ready_out, int wakefd)
+{
+ char dummy;
+ struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
+
+ /* Tell them we're ready. */
+ if (write(ready_out, &dummy, 1) != 1)
+ barf("CLIENT: ready write");
+
+ /* Wait for "GO" signal */
+ if (poll(&pollfd, 1, -1) != 1)
+ barf("poll");
+}
+
+/* Sender sprays loops messages down each file descriptor */
+static void *sender(struct sender_context *ctx)
+{
+ char data[DATASIZE];
+ unsigned int i, j;
+
+ ready(ctx->ready_out, ctx->wakefd);
+
+ /* Now pump to every receiver. */
+ for (i = 0; i < loops; i++) {
+ for (j = 0; j < ctx->num_fds; j++) {
+ int ret, done = 0;
+
+again:
+ ret = write(ctx->out_fds[j], data + done,
+ sizeof(data)-done);
+ if (ret < 0)
+ barf("SENDER: write");
+ done += ret;
+ if (done < DATASIZE)
+ goto again;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* One receiver per fd */
+static void *receiver(struct receiver_context* ctx)
+{
+ unsigned int i;
+
+ if (!thread_mode)
+ close(ctx->in_fds[1]);
+
+ /* Wait for start... */
+ ready(ctx->ready_out, ctx->wakefd);
+
+ /* Receive them all */
+ for (i = 0; i < ctx->num_packets; i++) {
+ char data[DATASIZE];
+ int ret, done = 0;
+
+again:
+ ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
+ if (ret < 0)
+ barf("SERVER: read");
+ done += ret;
+ if (done < DATASIZE)
+ goto again;
+ }
+
+ return NULL;
+}
+
+static pthread_t create_worker(void *ctx, void *(*func)(void *))
+{
+ pthread_attr_t attr;
+ pthread_t childid;
+ int err;
+
+ if (!thread_mode) {
+ /* process mode */
+ /* Fork the receiver. */
+ switch (fork()) {
+ case -1:
+ barf("fork()");
+ break;
+ case 0:
+ (*func) (ctx);
+ exit(0);
+ break;
+ default:
+ break;
+ }
+
+ return (pthread_t)0;
+ }
+
+ if (pthread_attr_init(&attr) != 0)
+ barf("pthread_attr_init:");
+
+#ifndef __ia64__
+ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
+ barf("pthread_attr_setstacksize");
+#endif
+
+ err = pthread_create(&childid, &attr, func, ctx);
+ if (err != 0) {
+ fprintf(stderr, "pthread_create failed: %s (%d)\n",
+ strerror(err), err);
+ exit(-1);
+ }
+ return childid;
+}
+
+static void reap_worker(pthread_t id)
+{
+ int proc_status;
+ void *thread_status;
+
+ if (!thread_mode) {
+ /* process mode */
+ wait(&proc_status);
+ if (!WIFEXITED(proc_status))
+ exit(1);
+ } else {
+ pthread_join(id, &thread_status);
+ }
+}
+
+/* One group of senders and receivers */
+static unsigned int group(pthread_t *pth,
+ unsigned int num_fds,
+ int ready_out,
+ int wakefd)
+{
+ unsigned int i;
+ struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
+ + num_fds * sizeof(int));
+
+ for (i = 0; i < num_fds; i++) {
+ int fds[2];
+ struct receiver_context *ctx = malloc(sizeof(*ctx));
+
+ if (!ctx)
+ barf("malloc()");
+
+
+ /* Create the pipe between client and server */
+ fdpair(fds);
+
+ ctx->num_packets = num_fds * loops;
+ ctx->in_fds[0] = fds[0];
+ ctx->in_fds[1] = fds[1];
+ ctx->ready_out = ready_out;
+ ctx->wakefd = wakefd;
+
+ pth[i] = create_worker(ctx, (void *)receiver);
+
+ snd_ctx->out_fds[i] = fds[1];
+ if (!thread_mode)
+ close(fds[0]);
+ }
+
+ /* Now we have all the fds, fork the senders */
+ for (i = 0; i < num_fds; i++) {
+ snd_ctx->ready_out = ready_out;
+ snd_ctx->wakefd = wakefd;
+ snd_ctx->num_fds = num_fds;
+
+ pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
+ }
+
+ /* Close the fds we have left */
+ if (!thread_mode)
+ for (i = 0; i < num_fds; i++)
+ close(snd_ctx->out_fds[i]);
+
+ /* Return number of children to reap */
+ return num_fds * 2;
+}
+
+static const struct option options[] = {
+ OPT_BOOLEAN('p', "pipe", &use_pipes,
+ "Use pipe() instead of socketpair()"),
+ OPT_BOOLEAN('t', "thread", &thread_mode,
+ "Be multi thread instead of multi process"),
+ OPT_INTEGER('g', "group", &num_groups,
+ "Specify number of groups"),
+ OPT_INTEGER('l', "loop", &loops,
+ "Specify number of loops"),
+ OPT_END()
+};
+
+static const char * const bench_sched_usage[] = {
+ "perf bench sched <options>",
+ NULL
+};
+
+static int cmd_bench_sched(int argc, const char **argv,
+ const char *prefix __used)
+{
+ unsigned int i, total_children;
+ struct timeval start, stop, diff;
+ unsigned int num_fds = 20;
+ int readyfds[2], wakefds[2];
+ char dummy;
+ pthread_t *pth_tab;
+
+ argc = parse_options(argc, argv, options, bench_sched_usage, 0);
+
+ pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
+ if (!pth_tab)
+ barf("main:malloc()");
+
+ fdpair(readyfds);
+ fdpair(wakefds);
+
+ total_children = 0;
+ for (i = 0; i < num_groups; i++)
+ total_children += group(pth_tab+total_children, num_fds,
+ readyfds[1], wakefds[0]);
+
+ /* Wait for everyone to be ready */
+ for (i = 0; i < total_children; i++)
+ if (read(readyfds[0], &dummy, 1) != 1)
+ barf("Reading for readyfds");
+
+ gettimeofday(&start, NULL);
+
+ /* Kick them off */
+ if (write(wakefds[1], &dummy, 1) != 1)
+ barf("Writing to start them");
+
+ /* Reap them all */
+ for (i = 0; i < total_children; i++)
+ reap_worker(pth_tab[i]);
+
+ gettimeofday(&stop, NULL);
+
+ /* Print time... */
+ timersub(&stop, &start, &diff);
+ printf("%lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000);
+
+ return 0;
+}
+
+struct bench_subsys {
+ const char *name;
+ int (*fn)(int, const char **, const char *);
+};
+
+static struct bench_subsys subsys_array[] = {
+ { "sched", cmd_bench_sched },
+ { NULL, NULL }
+};
+
+int cmd_bench(int argc, const char **argv, const char *prefix __used)
+{
+ int i, status = 0;
+
+ if (argc < 2) {
+ printf("Usage: perf bench <subsystem> [<options>]\n");
+ goto end;
+ }
+
+ for (i = 0; subsys_array[i].name; i++) {
+ if (!strcmp(subsys_array[i].name, argv[1])) {
+ status = subsys_array[i].fn(argc--, argv++, prefix);
+ goto end;
+ }
+ }
+
+ /* No subsystem matched. */
+ fprintf(stderr, "Unknown subsystem:%s\n", argv[0]);
+ status = 1;
+
+end:
+ return status;
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index e11d8d2..b346b66 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -25,5 +25,6 @@ extern int cmd_timechart(int argc, const char **argv, const char *prefix);
extern int cmd_top(int argc, const char **argv, const char *prefix);
extern int cmd_trace(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix);
+extern int cmd_bench(int argc, const char **argv, const char *prefix);

#endif
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 19fc7fe..9e7b48c 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -295,6 +295,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "version", cmd_version, 0 },
{ "trace", cmd_trace, 0 },
{ "sched", cmd_sched, 0 },
+ { "bench", cmd_bench, 0 },
};
unsigned int i;
static const char ext[] = STRIP_EXTENSION;

2009-11-02 14:26:10

by Mike Galbraith

[permalink] [raw]
Subject: Re: [PATCH][RFC] Adding benchmark subsystem to perf

Hi,

On Mon, 2009-11-02 at 22:51 +0900, Hitoshi Mitake wrote:

> Example of usage:
> % perf bench sched -t -l 1000
> 2.863

I think this should allow other benchmarks to be added is a logical
manner. Perhaps perf bench -t hackbench -t -l 1000, ie bench should
process args to allow the user to select a benchmark from a suite, and
pass args to it as well, but with a default set built in so people can
just type perf bench -t foo, to run any benchmark with it's defaults.

-Mike

2009-11-03 04:26:14

by Hitoshi Mitake

[permalink] [raw]
Subject: Re: [PATCH][RFC] Adding benchmark subsystem to perf

From: Mike Galbraith <[email protected]>
Subject: Re: [PATCH][RFC] Adding benchmark subsystem to perf
Date: Mon, 02 Nov 2009 15:26:09 +0100

> Hi,
>
> On Mon, 2009-11-02 at 22:51 +0900, Hitoshi Mitake wrote:
>
> > Example of usage:
> > % perf bench sched -t -l 1000
> > 2.863
>
> I think this should allow other benchmarks to be added is a logical
> manner. Perhaps perf bench -t hackbench -t -l 1000, ie bench should
> process args to allow the user to select a benchmark from a suite, and
> pass args to it as well, but with a default set built in so people can
> just type perf bench -t foo, to run any benchmark with it's defaults.

Thanks for your comment, Mike.

I forgot to do that. So I prepared new patch series
to implement your suggestion.

The new patch series containts new benchmark program,
based on Ingo's pipe-test-1m.c
http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c

And the series became too big to pack into one mail.
So I'll send it in new thread. Please wait a minute.