diff options
Diffstat (limited to 'nptl/sockperf.c')
-rw-r--r-- | nptl/sockperf.c | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/nptl/sockperf.c b/nptl/sockperf.c new file mode 100644 index 0000000000..d29a6ee26a --- /dev/null +++ b/nptl/sockperf.c @@ -0,0 +1,594 @@ +#define _GNU_SOURCE +#include <argp.h> +#include <complex.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gd.h> +#include <inttypes.h> +#include <pthread.h> +#include <signal.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/un.h> + + +#define size_x 320 +#define size_y 240 + + +#define PATH "/tmp/s.sockperf" + + +struct thread_param +{ + unsigned int from; + unsigned int to; + unsigned int nserv; +}; + +struct coord +{ + unsigned int x; + unsigned int y; + complex double z; +}; + + +/* We use 64bit values for the times. */ +typedef unsigned long long int hp_timing_t; + + +static unsigned int nclients = 2; +static unsigned int nservers = 2; + +static bool timing; +static int points; + + +static complex double top_left = -0.7 + 0.2i; +static complex double bottom_right = -0.5 - 0.0i; + + +static int colors[256]; +static gdImagePtr image; +static pthread_mutex_t image_lock; + +static int sock; + + +static void * +client (void *arg) +{ + struct thread_param *param = arg; + unsigned int cnt; + unsigned int nserv = param->nserv; + int clisock[nserv]; + struct pollfd servpoll[nserv]; + struct sockaddr_un servaddr; + socklen_t servlen; + struct coord c; + + bool new_coord (void) + { + if (cnt >= param->to) + return false; + + unsigned int row = cnt / size_x; + unsigned int col = cnt % size_x; + + c.x = col; + c.y = row; + c.z = (top_left + + ((col + * (creal (bottom_right) - creal (top_left))) / size_x) + + (_Complex_I * (row * (cimag (bottom_right) - cimag (top_left))) + / size_y)); + + ++cnt; + + return true; + } + + + for (cnt = 0; cnt < nserv; ++cnt) + { + servpoll[cnt].fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (clisock < 0) + { + puts ("cannot create socket in client"); + return NULL; + } + + memset (&servaddr, '\0', sizeof (servaddr)); + servaddr.sun_family = AF_UNIX; + strncpy (servaddr.sun_path, PATH, sizeof (servaddr.sun_path)); + servlen = offsetof (struct sockaddr_un, sun_path) + strlen (PATH) + 1; + + + int err; + while (1) + { + err = TEMP_FAILURE_RETRY (connect (servpoll[cnt].fd, &servaddr, + servlen)); + if (err != -1 || errno != ECONNREFUSED) + break; + + pthread_yield (); + } + + if (err == -1) + { + printf ("cannot connect: %m (%d)\n", errno); + exit (1); + } + + servpoll[cnt].events = POLLOUT; + servpoll[cnt].revents = 0; + } + + cnt = param->from; + + new_coord (); + bool z_valid = true; + + while (1) + { + int i; + int n = poll (servpoll, nserv, -1); + if (n == -1) + { + puts ("poll returned error"); + break; + } + + bool cont = false; + for (i = 0; i < nserv && n > 0; ++i) + if (servpoll[i].revents != 0) + { + if (servpoll[i].revents == POLLIN) + { + unsigned int vals[3]; + if (TEMP_FAILURE_RETRY (read (servpoll[i].fd, &vals, + sizeof (vals))) + != sizeof (vals)) + { + puts ("read error in client"); + return NULL; + } + + pthread_mutex_lock (&image_lock); + + gdImageSetPixel (image, vals[0], vals[1], vals[2]); + ++points; + + pthread_mutex_unlock (&image_lock); + + servpoll[i].events = POLLOUT; + } + else + { + if (servpoll[i].revents != POLLOUT) + printf ("revents: %hd != POLLOUT ???\n", + servpoll[i].revents); + + if (z_valid) + { + if (TEMP_FAILURE_RETRY (write (servpoll[i].fd, &c, + sizeof (c))) != sizeof (c)) + { + puts ("write error in client"); + return NULL; + } + cont = true; + servpoll[i].events = POLLIN; + + z_valid = new_coord (); + if (! z_valid) + /* No more to do. Clear the event fields. */ + for (i = 0; i < nserv; ++i) + if (servpoll[i].events == POLLOUT) + servpoll[i].events = servpoll[i].revents = 0; + } + else + servpoll[i].events = servpoll[i].revents = 0; + } + + --n; + } + else if (servpoll[i].events != 0) + cont = true; + + if (! cont && ! z_valid) + break; + } + + c.x = 0xffffffff; + c.y = 0xffffffff; + for (cnt = 0; cnt < nserv; ++cnt) + { + TEMP_FAILURE_RETRY (write (servpoll[cnt].fd, &c, sizeof (c))); + close (servpoll[cnt].fd); + } + + return NULL; +} + + +static void * +server (void *arg) +{ + struct sockaddr_un cliaddr; + socklen_t clilen; + int clisock = TEMP_FAILURE_RETRY (accept (sock, &cliaddr, &clilen)); + + if (clisock == -1) + { + puts ("accept failed"); + return NULL; + } + + while (1) + { + struct coord c; + + if (TEMP_FAILURE_RETRY (read (clisock, &c, sizeof (c))) != sizeof (c)) + { + printf ("server read failed: %m (%d)\n", errno); + break; + } + + if (c.x == 0xffffffff && c.y == 0xffffffff) + break; + + unsigned int rnds = 0; + complex double z = c.z; + while (cabs (z) < 4.0) + { + z = z * z - 1; + if (++rnds == 255) + break; + } + + unsigned int vals[3] = { c.x, c.y, rnds }; + if (TEMP_FAILURE_RETRY (write (clisock, vals, sizeof (vals))) + != sizeof (vals)) + { + puts ("server write error"); + return NULL; + } + } + + close (clisock); + + return NULL; +} + + +static const char *outfilename = "test.png"; + + +static const struct argp_option options[] = + { + { "clients", 'c', "NUMBER", 0, "Number of client threads" }, + { "servers", 's', "NUMBER", 0, "Number of server threads per client" }, + { "timing", 'T', NULL, 0, + "Measure time from startup to the last thread finishing" }, + { NULL, 0, NULL, 0, NULL } + }; + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt +}; + + +int +main (int argc, char *argv[]) +{ + int cnt; + FILE *outfile; + struct sockaddr_un servaddr; + socklen_t servlen; + int remaining; + + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + + pthread_t servth[nservers * nclients]; + pthread_t clntth[nclients]; + struct thread_param clntparam[nclients]; + + + image = gdImageCreate (size_x, size_y); + if (image == NULL) + { + puts ("gdImageCreate failed"); + return 1; + } + + for (cnt = 0; cnt < 255; ++cnt) + colors[cnt] = gdImageColorAllocate (image, 256 - cnt, 256 - cnt, + 256 - cnt); + /* Black. */ + colors[cnt] = gdImageColorAllocate (image, 0, 0, 0); + + + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + error (EXIT_FAILURE, errno, "cannot create socket"); + + memset (&servaddr, '\0', sizeof (servaddr)); + servaddr.sun_family = AF_UNIX; + strncpy (servaddr.sun_path, PATH, sizeof (servaddr.sun_path)); + servlen = offsetof (struct sockaddr_un, sun_path) + strlen (PATH) + 1; + + if (bind (sock, &servaddr, servlen) == -1) + error (EXIT_FAILURE, errno, "bind failed"); + + listen (sock, SOMAXCONN); + + pthread_mutex_init (&image_lock, NULL); + + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + + clockid_t cl; + struct timespec start_time; + if (timing) + { + if (clock_getcpuclockid (0, &cl) != 0 + || clock_gettime (cl, &start_time) != 0) + timing = false; + } + + /* Start the servers. */ + for (cnt = 0; cnt < nservers * nclients; ++cnt) + { + if (pthread_create (&servth[cnt], NULL, server, NULL) != 0) + { + puts ("pthread_create for server failed"); + exit (1); + } + } + + for (cnt = 0; cnt < nclients; ++cnt) + { + clntparam[cnt].from = cnt * (size_x * size_y) / nclients; + clntparam[cnt].to = MIN ((cnt + 1) * (size_x * size_y) / nclients, + size_x * size_y); + clntparam[cnt].nserv = nservers; + + if (pthread_create (&clntth[cnt], NULL, client, &clntparam[cnt]) != 0) + { + puts ("pthread_create for client failed"); + exit (1); + } + } + + + /* Wait for the clients. */ + for (cnt = 0; cnt < nclients; ++cnt) + if (pthread_join (clntth[cnt], NULL) != 0) + { + puts ("client pthread_join failed"); + exit (1); + } + + /* Wait for the servers. */ + for (cnt = 0; cnt < nclients * nservers; ++cnt) + if (pthread_join (servth[cnt], NULL) != 0) + { + puts ("server pthread_join failed"); + exit (1); + } + + + if (timing) + { + struct timespec end_time; + + if (clock_gettime (cl, &end_time) == 0) + { + end_time.tv_sec -= start_time.tv_sec; + end_time.tv_nsec -= start_time.tv_nsec; + if (end_time.tv_nsec < 0) + { + end_time.tv_nsec += 1000000000; + --end_time.tv_sec; + } + + printf ("\nRuntime: %lu.%09lu seconds\n%d points computed\n", + (unsigned long int) end_time.tv_sec, + (unsigned long int) end_time.tv_nsec, + points); + } + } + + + outfile = fopen (outfilename, "w"); + if (outfile == NULL) + error (EXIT_FAILURE, errno, "cannot open output file '%s'", outfilename); + + gdImagePng (image, outfile); + + fclose (outfile); + + unlink (PATH); + + return 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'c': + nclients = strtoul (arg, NULL, 0); + break; + + case 's': + nservers = strtoul (arg, NULL, 0); + break; + + case 'T': + timing = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + + +static hp_timing_t +get_clockfreq (void) +{ + /* We read the information from the /proc filesystem. It contains at + least one line like + cpu MHz : 497.840237 + or also + cpu MHz : 497.841 + We search for this line and convert the number in an integer. */ + static hp_timing_t result; + int fd; + + /* If this function was called before, we know the result. */ + if (result != 0) + return result; + + fd = open ("/proc/cpuinfo", O_RDONLY); + if (__builtin_expect (fd != -1, 1)) + { + /* XXX AFAIK the /proc filesystem can generate "files" only up + to a size of 4096 bytes. */ + char buf[4096]; + ssize_t n; + + n = read (fd, buf, sizeof buf); + if (__builtin_expect (n, 1) > 0) + { + char *mhz = memmem (buf, n, "cpu MHz", 7); + + if (__builtin_expect (mhz != NULL, 1)) + { + char *endp = buf + n; + int seen_decpoint = 0; + int ndigits = 0; + + /* Search for the beginning of the string. */ + while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n') + ++mhz; + + while (mhz < endp && *mhz != '\n') + { + if (*mhz >= '0' && *mhz <= '9') + { + result *= 10; + result += *mhz - '0'; + if (seen_decpoint) + ++ndigits; + } + else if (*mhz == '.') + seen_decpoint = 1; + + ++mhz; + } + + /* Compensate for missing digits at the end. */ + while (ndigits++ < 6) + result *= 10; + } + } + + close (fd); + } + + return result; +} + + +int +clock_getcpuclockid (pid_t pid, clockid_t *clock_id) +{ + /* We don't allow any process ID but our own. */ + if (pid != 0 && pid != getpid ()) + return EPERM; + +#ifdef CLOCK_PROCESS_CPUTIME_ID + /* Store the number. */ + *clock_id = CLOCK_PROCESS_CPUTIME_ID; + + return 0; +#else + /* We don't have a timer for that. */ + return ENOENT; +#endif +} + + +#define HP_TIMING_NOW(Var) __asm__ __volatile__ ("rdtsc" : "=A" (Var)) + +/* Get current value of CLOCK and store it in TP. */ +int +clock_gettime (clockid_t clock_id, struct timespec *tp) +{ + int retval = -1; + + switch (clock_id) + { + case CLOCK_PROCESS_CPUTIME_ID: + { + + static hp_timing_t freq; + hp_timing_t tsc; + + /* Get the current counter. */ + HP_TIMING_NOW (tsc); + + if (freq == 0) + { + freq = get_clockfreq (); + if (freq == 0) + return EINVAL; + } + + /* Compute the seconds. */ + tp->tv_sec = tsc / freq; + + /* And the nanoseconds. This computation should be stable until + we get machines with about 16GHz frequency. */ + tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq; + + retval = 0; + } + break; + + default: + errno = EINVAL; + break; + } + + return retval; +} |