sockperf.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. #define _GNU_SOURCE
  2. #include <argp.h>
  3. #include <complex.h>
  4. #include <errno.h>
  5. #include <error.h>
  6. #include <fcntl.h>
  7. #include <gd.h>
  8. #include <inttypes.h>
  9. #include <pthread.h>
  10. #include <signal.h>
  11. #include <stdbool.h>
  12. #include <stddef.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <time.h>
  17. #include <unistd.h>
  18. #include <sys/param.h>
  19. #include <sys/poll.h>
  20. #include <sys/socket.h>
  21. #include <sys/un.h>
  22. #define size_x 320
  23. #define size_y 240
  24. #define PATH "/tmp/s.sockperf"
  25. struct thread_param
  26. {
  27. unsigned int from;
  28. unsigned int to;
  29. unsigned int nserv;
  30. };
  31. struct coord
  32. {
  33. unsigned int x;
  34. unsigned int y;
  35. complex double z;
  36. };
  37. /* We use 64bit values for the times. */
  38. typedef unsigned long long int hp_timing_t;
  39. static unsigned int nclients = 2;
  40. static unsigned int nservers = 2;
  41. static bool timing;
  42. static int points;
  43. static complex double top_left = -0.7 + 0.2i;
  44. static complex double bottom_right = -0.5 - 0.0i;
  45. static int colors[256];
  46. static gdImagePtr image;
  47. static pthread_mutex_t image_lock;
  48. static int sock;
  49. static void *
  50. client (void *arg)
  51. {
  52. struct thread_param *param = arg;
  53. unsigned int cnt;
  54. unsigned int nserv = param->nserv;
  55. int clisock[nserv];
  56. struct pollfd servpoll[nserv];
  57. struct sockaddr_un servaddr;
  58. socklen_t servlen;
  59. struct coord c;
  60. bool new_coord (void)
  61. {
  62. if (cnt >= param->to)
  63. return false;
  64. unsigned int row = cnt / size_x;
  65. unsigned int col = cnt % size_x;
  66. c.x = col;
  67. c.y = row;
  68. c.z = (top_left
  69. + ((col
  70. * (creal (bottom_right) - creal (top_left))) / size_x)
  71. + (_Complex_I * (row * (cimag (bottom_right) - cimag (top_left)))
  72. / size_y));
  73. ++cnt;
  74. return true;
  75. }
  76. for (cnt = 0; cnt < nserv; ++cnt)
  77. {
  78. servpoll[cnt].fd = socket (AF_UNIX, SOCK_STREAM, 0);
  79. if (clisock < 0)
  80. {
  81. puts ("cannot create socket in client");
  82. return NULL;
  83. }
  84. memset (&servaddr, '\0', sizeof (servaddr));
  85. servaddr.sun_family = AF_UNIX;
  86. strncpy (servaddr.sun_path, PATH, sizeof (servaddr.sun_path));
  87. servlen = offsetof (struct sockaddr_un, sun_path) + strlen (PATH) + 1;
  88. int err;
  89. while (1)
  90. {
  91. err = TEMP_FAILURE_RETRY (connect (servpoll[cnt].fd, &servaddr,
  92. servlen));
  93. if (err != -1 || errno != ECONNREFUSED)
  94. break;
  95. pthread_yield ();
  96. }
  97. if (err == -1)
  98. {
  99. printf ("cannot connect: %m (%d)\n", errno);
  100. exit (1);
  101. }
  102. servpoll[cnt].events = POLLOUT;
  103. servpoll[cnt].revents = 0;
  104. }
  105. cnt = param->from;
  106. new_coord ();
  107. bool z_valid = true;
  108. while (1)
  109. {
  110. int i;
  111. int n = poll (servpoll, nserv, -1);
  112. if (n == -1)
  113. {
  114. puts ("poll returned error");
  115. break;
  116. }
  117. bool cont = false;
  118. for (i = 0; i < nserv && n > 0; ++i)
  119. if (servpoll[i].revents != 0)
  120. {
  121. if (servpoll[i].revents == POLLIN)
  122. {
  123. unsigned int vals[3];
  124. if (TEMP_FAILURE_RETRY (read (servpoll[i].fd, &vals,
  125. sizeof (vals)))
  126. != sizeof (vals))
  127. {
  128. puts ("read error in client");
  129. return NULL;
  130. }
  131. pthread_mutex_lock (&image_lock);
  132. gdImageSetPixel (image, vals[0], vals[1], vals[2]);
  133. ++points;
  134. pthread_mutex_unlock (&image_lock);
  135. servpoll[i].events = POLLOUT;
  136. }
  137. else
  138. {
  139. if (servpoll[i].revents != POLLOUT)
  140. printf ("revents: %hd != POLLOUT ???\n",
  141. servpoll[i].revents);
  142. if (z_valid)
  143. {
  144. if (TEMP_FAILURE_RETRY (write (servpoll[i].fd, &c,
  145. sizeof (c))) != sizeof (c))
  146. {
  147. puts ("write error in client");
  148. return NULL;
  149. }
  150. cont = true;
  151. servpoll[i].events = POLLIN;
  152. z_valid = new_coord ();
  153. if (! z_valid)
  154. /* No more to do. Clear the event fields. */
  155. for (i = 0; i < nserv; ++i)
  156. if (servpoll[i].events == POLLOUT)
  157. servpoll[i].events = servpoll[i].revents = 0;
  158. }
  159. else
  160. servpoll[i].events = servpoll[i].revents = 0;
  161. }
  162. --n;
  163. }
  164. else if (servpoll[i].events != 0)
  165. cont = true;
  166. if (! cont && ! z_valid)
  167. break;
  168. }
  169. c.x = 0xffffffff;
  170. c.y = 0xffffffff;
  171. for (cnt = 0; cnt < nserv; ++cnt)
  172. {
  173. TEMP_FAILURE_RETRY (write (servpoll[cnt].fd, &c, sizeof (c)));
  174. close (servpoll[cnt].fd);
  175. }
  176. return NULL;
  177. }
  178. static void *
  179. server (void *arg)
  180. {
  181. struct sockaddr_un cliaddr;
  182. socklen_t clilen;
  183. int clisock = TEMP_FAILURE_RETRY (accept (sock, &cliaddr, &clilen));
  184. if (clisock == -1)
  185. {
  186. puts ("accept failed");
  187. return NULL;
  188. }
  189. while (1)
  190. {
  191. struct coord c;
  192. if (TEMP_FAILURE_RETRY (read (clisock, &c, sizeof (c))) != sizeof (c))
  193. {
  194. printf ("server read failed: %m (%d)\n", errno);
  195. break;
  196. }
  197. if (c.x == 0xffffffff && c.y == 0xffffffff)
  198. break;
  199. unsigned int rnds = 0;
  200. complex double z = c.z;
  201. while (cabs (z) < 4.0)
  202. {
  203. z = z * z - 1;
  204. if (++rnds == 255)
  205. break;
  206. }
  207. unsigned int vals[3] = { c.x, c.y, rnds };
  208. if (TEMP_FAILURE_RETRY (write (clisock, vals, sizeof (vals)))
  209. != sizeof (vals))
  210. {
  211. puts ("server write error");
  212. return NULL;
  213. }
  214. }
  215. close (clisock);
  216. return NULL;
  217. }
  218. static const char *outfilename = "test.png";
  219. static const struct argp_option options[] =
  220. {
  221. { "clients", 'c', "NUMBER", 0, "Number of client threads" },
  222. { "servers", 's', "NUMBER", 0, "Number of server threads per client" },
  223. { "timing", 'T', NULL, 0,
  224. "Measure time from startup to the last thread finishing" },
  225. { NULL, 0, NULL, 0, NULL }
  226. };
  227. /* Prototype for option handler. */
  228. static error_t parse_opt (int key, char *arg, struct argp_state *state);
  229. /* Data structure to communicate with argp functions. */
  230. static struct argp argp =
  231. {
  232. options, parse_opt
  233. };
  234. int
  235. main (int argc, char *argv[])
  236. {
  237. int cnt;
  238. FILE *outfile;
  239. struct sockaddr_un servaddr;
  240. socklen_t servlen;
  241. int remaining;
  242. /* Parse and process arguments. */
  243. argp_parse (&argp, argc, argv, 0, &remaining, NULL);
  244. pthread_t servth[nservers * nclients];
  245. pthread_t clntth[nclients];
  246. struct thread_param clntparam[nclients];
  247. image = gdImageCreate (size_x, size_y);
  248. if (image == NULL)
  249. {
  250. puts ("gdImageCreate failed");
  251. return 1;
  252. }
  253. for (cnt = 0; cnt < 255; ++cnt)
  254. colors[cnt] = gdImageColorAllocate (image, 256 - cnt, 256 - cnt,
  255. 256 - cnt);
  256. /* Black. */
  257. colors[cnt] = gdImageColorAllocate (image, 0, 0, 0);
  258. sock = socket (AF_UNIX, SOCK_STREAM, 0);
  259. if (sock < 0)
  260. error (EXIT_FAILURE, errno, "cannot create socket");
  261. memset (&servaddr, '\0', sizeof (servaddr));
  262. servaddr.sun_family = AF_UNIX;
  263. strncpy (servaddr.sun_path, PATH, sizeof (servaddr.sun_path));
  264. servlen = offsetof (struct sockaddr_un, sun_path) + strlen (PATH) + 1;
  265. if (bind (sock, &servaddr, servlen) == -1)
  266. error (EXIT_FAILURE, errno, "bind failed");
  267. listen (sock, SOMAXCONN);
  268. pthread_mutex_init (&image_lock, NULL);
  269. struct sigaction sa;
  270. sa.sa_handler = SIG_IGN;
  271. sigemptyset (&sa.sa_mask);
  272. sa.sa_flags = 0;
  273. clockid_t cl;
  274. struct timespec start_time;
  275. if (timing)
  276. {
  277. if (clock_getcpuclockid (0, &cl) != 0
  278. || clock_gettime (cl, &start_time) != 0)
  279. timing = false;
  280. }
  281. /* Start the servers. */
  282. for (cnt = 0; cnt < nservers * nclients; ++cnt)
  283. {
  284. if (pthread_create (&servth[cnt], NULL, server, NULL) != 0)
  285. {
  286. puts ("pthread_create for server failed");
  287. exit (1);
  288. }
  289. }
  290. for (cnt = 0; cnt < nclients; ++cnt)
  291. {
  292. clntparam[cnt].from = cnt * (size_x * size_y) / nclients;
  293. clntparam[cnt].to = MIN ((cnt + 1) * (size_x * size_y) / nclients,
  294. size_x * size_y);
  295. clntparam[cnt].nserv = nservers;
  296. if (pthread_create (&clntth[cnt], NULL, client, &clntparam[cnt]) != 0)
  297. {
  298. puts ("pthread_create for client failed");
  299. exit (1);
  300. }
  301. }
  302. /* Wait for the clients. */
  303. for (cnt = 0; cnt < nclients; ++cnt)
  304. if (pthread_join (clntth[cnt], NULL) != 0)
  305. {
  306. puts ("client pthread_join failed");
  307. exit (1);
  308. }
  309. /* Wait for the servers. */
  310. for (cnt = 0; cnt < nclients * nservers; ++cnt)
  311. if (pthread_join (servth[cnt], NULL) != 0)
  312. {
  313. puts ("server pthread_join failed");
  314. exit (1);
  315. }
  316. if (timing)
  317. {
  318. struct timespec end_time;
  319. if (clock_gettime (cl, &end_time) == 0)
  320. {
  321. end_time.tv_sec -= start_time.tv_sec;
  322. end_time.tv_nsec -= start_time.tv_nsec;
  323. if (end_time.tv_nsec < 0)
  324. {
  325. end_time.tv_nsec += 1000000000;
  326. --end_time.tv_sec;
  327. }
  328. printf ("\nRuntime: %lu.%09lu seconds\n%d points computed\n",
  329. (unsigned long int) end_time.tv_sec,
  330. (unsigned long int) end_time.tv_nsec,
  331. points);
  332. }
  333. }
  334. outfile = fopen (outfilename, "w");
  335. if (outfile == NULL)
  336. error (EXIT_FAILURE, errno, "cannot open output file '%s'", outfilename);
  337. gdImagePng (image, outfile);
  338. fclose (outfile);
  339. unlink (PATH);
  340. return 0;
  341. }
  342. /* Handle program arguments. */
  343. static error_t
  344. parse_opt (int key, char *arg, struct argp_state *state)
  345. {
  346. switch (key)
  347. {
  348. case 'c':
  349. nclients = strtoul (arg, NULL, 0);
  350. break;
  351. case 's':
  352. nservers = strtoul (arg, NULL, 0);
  353. break;
  354. case 'T':
  355. timing = true;
  356. break;
  357. default:
  358. return ARGP_ERR_UNKNOWN;
  359. }
  360. return 0;
  361. }
  362. static hp_timing_t
  363. get_clockfreq (void)
  364. {
  365. /* We read the information from the /proc filesystem. It contains at
  366. least one line like
  367. cpu MHz : 497.840237
  368. or also
  369. cpu MHz : 497.841
  370. We search for this line and convert the number in an integer. */
  371. static hp_timing_t result;
  372. int fd;
  373. /* If this function was called before, we know the result. */
  374. if (result != 0)
  375. return result;
  376. fd = open ("/proc/cpuinfo", O_RDONLY);
  377. if (__builtin_expect (fd != -1, 1))
  378. {
  379. /* XXX AFAIK the /proc filesystem can generate "files" only up
  380. to a size of 4096 bytes. */
  381. char buf[4096];
  382. ssize_t n;
  383. n = read (fd, buf, sizeof buf);
  384. if (__builtin_expect (n, 1) > 0)
  385. {
  386. char *mhz = memmem (buf, n, "cpu MHz", 7);
  387. if (__builtin_expect (mhz != NULL, 1))
  388. {
  389. char *endp = buf + n;
  390. int seen_decpoint = 0;
  391. int ndigits = 0;
  392. /* Search for the beginning of the string. */
  393. while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n')
  394. ++mhz;
  395. while (mhz < endp && *mhz != '\n')
  396. {
  397. if (*mhz >= '0' && *mhz <= '9')
  398. {
  399. result *= 10;
  400. result += *mhz - '0';
  401. if (seen_decpoint)
  402. ++ndigits;
  403. }
  404. else if (*mhz == '.')
  405. seen_decpoint = 1;
  406. ++mhz;
  407. }
  408. /* Compensate for missing digits at the end. */
  409. while (ndigits++ < 6)
  410. result *= 10;
  411. }
  412. }
  413. close (fd);
  414. }
  415. return result;
  416. }
  417. int
  418. clock_getcpuclockid (pid_t pid, clockid_t *clock_id)
  419. {
  420. /* We don't allow any process ID but our own. */
  421. if (pid != 0 && pid != getpid ())
  422. return EPERM;
  423. #ifdef CLOCK_PROCESS_CPUTIME_ID
  424. /* Store the number. */
  425. *clock_id = CLOCK_PROCESS_CPUTIME_ID;
  426. return 0;
  427. #else
  428. /* We don't have a timer for that. */
  429. return ENOENT;
  430. #endif
  431. }
  432. #define HP_TIMING_NOW(Var) __asm__ __volatile__ ("rdtsc" : "=A" (Var))
  433. /* Get current value of CLOCK and store it in TP. */
  434. int
  435. clock_gettime (clockid_t clock_id, struct timespec *tp)
  436. {
  437. int retval = -1;
  438. switch (clock_id)
  439. {
  440. case CLOCK_PROCESS_CPUTIME_ID:
  441. {
  442. static hp_timing_t freq;
  443. hp_timing_t tsc;
  444. /* Get the current counter. */
  445. HP_TIMING_NOW (tsc);
  446. if (freq == 0)
  447. {
  448. freq = get_clockfreq ();
  449. if (freq == 0)
  450. return EINVAL;
  451. }
  452. /* Compute the seconds. */
  453. tp->tv_sec = tsc / freq;
  454. /* And the nanoseconds. This computation should be stable until
  455. we get machines with about 16GHz frequency. */
  456. tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq;
  457. retval = 0;
  458. }
  459. break;
  460. default:
  461. errno = EINVAL;
  462. break;
  463. }
  464. return retval;
  465. }