context_switch.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*
  2. * Context switch microbenchmark.
  3. *
  4. * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. */
  11. #define _GNU_SOURCE
  12. #include <errno.h>
  13. #include <sched.h>
  14. #include <string.h>
  15. #include <stdio.h>
  16. #include <unistd.h>
  17. #include <stdlib.h>
  18. #include <getopt.h>
  19. #include <signal.h>
  20. #include <assert.h>
  21. #include <pthread.h>
  22. #include <limits.h>
  23. #include <sys/time.h>
  24. #include <sys/syscall.h>
  25. #include <sys/types.h>
  26. #include <sys/shm.h>
  27. #include <linux/futex.h>
  28. #ifdef __powerpc__
  29. #include <altivec.h>
  30. #endif
  31. #include "utils.h"
  32. static unsigned int timeout = 30;
  33. static int touch_vdso;
  34. struct timeval tv;
  35. static int touch_fp = 1;
  36. double fp;
  37. static int touch_vector = 1;
  38. vector int a, b, c;
  39. #ifdef __powerpc__
  40. static int touch_altivec = 1;
  41. /*
  42. * Note: LTO (Link Time Optimisation) doesn't play well with this function
  43. * attribute. Be very careful enabling LTO for this test.
  44. */
  45. static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
  46. {
  47. c = a + b;
  48. }
  49. #endif
  50. static void touch(void)
  51. {
  52. if (touch_vdso)
  53. gettimeofday(&tv, NULL);
  54. if (touch_fp)
  55. fp += 0.1;
  56. #ifdef __powerpc__
  57. if (touch_altivec)
  58. altivec_touch_fn();
  59. #endif
  60. if (touch_vector)
  61. c = a + b;
  62. asm volatile("# %0 %1 %2": : "r"(&tv), "r"(&fp), "r"(&c));
  63. }
  64. static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu)
  65. {
  66. int rc;
  67. pthread_t tid;
  68. cpu_set_t cpuset;
  69. pthread_attr_t attr;
  70. CPU_ZERO(&cpuset);
  71. CPU_SET(cpu, &cpuset);
  72. rc = pthread_attr_init(&attr);
  73. if (rc) {
  74. errno = rc;
  75. perror("pthread_attr_init");
  76. exit(1);
  77. }
  78. rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
  79. if (rc) {
  80. errno = rc;
  81. perror("pthread_attr_setaffinity_np");
  82. exit(1);
  83. }
  84. rc = pthread_create(&tid, &attr, fn, arg);
  85. if (rc) {
  86. errno = rc;
  87. perror("pthread_create");
  88. exit(1);
  89. }
  90. }
  91. static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu)
  92. {
  93. int pid;
  94. cpu_set_t cpuset;
  95. pid = fork();
  96. if (pid == -1) {
  97. perror("fork");
  98. exit(1);
  99. }
  100. if (pid)
  101. return;
  102. CPU_ZERO(&cpuset);
  103. CPU_SET(cpu, &cpuset);
  104. if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {
  105. perror("sched_setaffinity");
  106. exit(1);
  107. }
  108. fn(arg);
  109. exit(0);
  110. }
  111. static unsigned long iterations;
  112. static unsigned long iterations_prev;
  113. static void sigalrm_handler(int junk)
  114. {
  115. unsigned long i = iterations;
  116. printf("%ld\n", i - iterations_prev);
  117. iterations_prev = i;
  118. if (--timeout == 0)
  119. kill(0, SIGUSR1);
  120. alarm(1);
  121. }
  122. static void sigusr1_handler(int junk)
  123. {
  124. exit(0);
  125. }
  126. struct actions {
  127. void (*setup)(int, int);
  128. void *(*thread1)(void *);
  129. void *(*thread2)(void *);
  130. };
  131. #define READ 0
  132. #define WRITE 1
  133. static int pipe_fd1[2];
  134. static int pipe_fd2[2];
  135. static void pipe_setup(int cpu1, int cpu2)
  136. {
  137. if (pipe(pipe_fd1) || pipe(pipe_fd2))
  138. exit(1);
  139. }
  140. static void *pipe_thread1(void *arg)
  141. {
  142. signal(SIGALRM, sigalrm_handler);
  143. alarm(1);
  144. while (1) {
  145. assert(read(pipe_fd1[READ], &c, 1) == 1);
  146. touch();
  147. assert(write(pipe_fd2[WRITE], &c, 1) == 1);
  148. touch();
  149. iterations += 2;
  150. }
  151. return NULL;
  152. }
  153. static void *pipe_thread2(void *arg)
  154. {
  155. while (1) {
  156. assert(write(pipe_fd1[WRITE], &c, 1) == 1);
  157. touch();
  158. assert(read(pipe_fd2[READ], &c, 1) == 1);
  159. touch();
  160. }
  161. return NULL;
  162. }
  163. static struct actions pipe_actions = {
  164. .setup = pipe_setup,
  165. .thread1 = pipe_thread1,
  166. .thread2 = pipe_thread2,
  167. };
  168. static void yield_setup(int cpu1, int cpu2)
  169. {
  170. if (cpu1 != cpu2) {
  171. fprintf(stderr, "Both threads must be on the same CPU for yield test\n");
  172. exit(1);
  173. }
  174. }
  175. static void *yield_thread1(void *arg)
  176. {
  177. signal(SIGALRM, sigalrm_handler);
  178. alarm(1);
  179. while (1) {
  180. sched_yield();
  181. touch();
  182. iterations += 2;
  183. }
  184. return NULL;
  185. }
  186. static void *yield_thread2(void *arg)
  187. {
  188. while (1) {
  189. sched_yield();
  190. touch();
  191. }
  192. return NULL;
  193. }
  194. static struct actions yield_actions = {
  195. .setup = yield_setup,
  196. .thread1 = yield_thread1,
  197. .thread2 = yield_thread2,
  198. };
  199. static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
  200. void *addr2, int val3)
  201. {
  202. return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
  203. }
  204. static unsigned long cmpxchg(unsigned long *p, unsigned long expected,
  205. unsigned long desired)
  206. {
  207. unsigned long exp = expected;
  208. __atomic_compare_exchange_n(p, &exp, desired, 0,
  209. __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
  210. return exp;
  211. }
  212. static unsigned long xchg(unsigned long *p, unsigned long val)
  213. {
  214. return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST);
  215. }
  216. static int processes;
  217. static int mutex_lock(unsigned long *m)
  218. {
  219. int c;
  220. int flags = FUTEX_WAIT;
  221. if (!processes)
  222. flags |= FUTEX_PRIVATE_FLAG;
  223. c = cmpxchg(m, 0, 1);
  224. if (!c)
  225. return 0;
  226. if (c == 1)
  227. c = xchg(m, 2);
  228. while (c) {
  229. sys_futex(m, flags, 2, NULL, NULL, 0);
  230. c = xchg(m, 2);
  231. }
  232. return 0;
  233. }
  234. static int mutex_unlock(unsigned long *m)
  235. {
  236. int flags = FUTEX_WAKE;
  237. if (!processes)
  238. flags |= FUTEX_PRIVATE_FLAG;
  239. if (*m == 2)
  240. *m = 0;
  241. else if (xchg(m, 0) == 1)
  242. return 0;
  243. sys_futex(m, flags, 1, NULL, NULL, 0);
  244. return 0;
  245. }
  246. static unsigned long *m1, *m2;
  247. static void futex_setup(int cpu1, int cpu2)
  248. {
  249. if (!processes) {
  250. static unsigned long _m1, _m2;
  251. m1 = &_m1;
  252. m2 = &_m2;
  253. } else {
  254. int shmid;
  255. void *shmaddr;
  256. shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W);
  257. if (shmid < 0) {
  258. perror("shmget");
  259. exit(1);
  260. }
  261. shmaddr = shmat(shmid, NULL, 0);
  262. if (shmaddr == (char *)-1) {
  263. perror("shmat");
  264. shmctl(shmid, IPC_RMID, NULL);
  265. exit(1);
  266. }
  267. shmctl(shmid, IPC_RMID, NULL);
  268. m1 = shmaddr;
  269. m2 = shmaddr + sizeof(*m1);
  270. }
  271. *m1 = 0;
  272. *m2 = 0;
  273. mutex_lock(m1);
  274. mutex_lock(m2);
  275. }
  276. static void *futex_thread1(void *arg)
  277. {
  278. signal(SIGALRM, sigalrm_handler);
  279. alarm(1);
  280. while (1) {
  281. mutex_lock(m2);
  282. mutex_unlock(m1);
  283. iterations += 2;
  284. }
  285. return NULL;
  286. }
  287. static void *futex_thread2(void *arg)
  288. {
  289. while (1) {
  290. mutex_unlock(m2);
  291. mutex_lock(m1);
  292. }
  293. return NULL;
  294. }
  295. static struct actions futex_actions = {
  296. .setup = futex_setup,
  297. .thread1 = futex_thread1,
  298. .thread2 = futex_thread2,
  299. };
  300. static struct option options[] = {
  301. { "test", required_argument, 0, 't' },
  302. { "process", no_argument, &processes, 1 },
  303. { "timeout", required_argument, 0, 's' },
  304. { "vdso", no_argument, &touch_vdso, 1 },
  305. { "no-fp", no_argument, &touch_fp, 0 },
  306. #ifdef __powerpc__
  307. { "no-altivec", no_argument, &touch_altivec, 0 },
  308. #endif
  309. { "no-vector", no_argument, &touch_vector, 0 },
  310. { 0, },
  311. };
  312. static void usage(void)
  313. {
  314. fprintf(stderr, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
  315. fprintf(stderr, "\t\t--test=X\tpipe, futex or yield (default)\n");
  316. fprintf(stderr, "\t\t--process\tUse processes (default threads)\n");
  317. fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
  318. fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n");
  319. fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n");
  320. #ifdef __powerpc__
  321. fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n");
  322. #endif
  323. fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n");
  324. }
  325. int main(int argc, char *argv[])
  326. {
  327. signed char c;
  328. struct actions *actions = &yield_actions;
  329. int cpu1;
  330. int cpu2;
  331. static void (*start_fn)(void *(*fn)(void *), void *arg, unsigned long cpu);
  332. while (1) {
  333. int option_index = 0;
  334. c = getopt_long(argc, argv, "", options, &option_index);
  335. if (c == -1)
  336. break;
  337. switch (c) {
  338. case 0:
  339. if (options[option_index].flag != 0)
  340. break;
  341. usage();
  342. exit(1);
  343. break;
  344. case 't':
  345. if (!strcmp(optarg, "pipe")) {
  346. actions = &pipe_actions;
  347. } else if (!strcmp(optarg, "yield")) {
  348. actions = &yield_actions;
  349. } else if (!strcmp(optarg, "futex")) {
  350. actions = &futex_actions;
  351. } else {
  352. usage();
  353. exit(1);
  354. }
  355. break;
  356. case 's':
  357. timeout = atoi(optarg);
  358. break;
  359. default:
  360. usage();
  361. exit(1);
  362. }
  363. }
  364. if (processes)
  365. start_fn = start_process_on;
  366. else
  367. start_fn = start_thread_on;
  368. if (((argc - optind) != 2)) {
  369. cpu1 = cpu2 = pick_online_cpu();
  370. } else {
  371. cpu1 = atoi(argv[optind++]);
  372. cpu2 = atoi(argv[optind++]);
  373. }
  374. printf("Using %s with ", processes ? "processes" : "threads");
  375. if (actions == &pipe_actions)
  376. printf("pipe");
  377. else if (actions == &yield_actions)
  378. printf("yield");
  379. else
  380. printf("futex");
  381. printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
  382. cpu1, cpu2, touch_fp ? "yes" : "no", touch_altivec ? "yes" : "no",
  383. touch_vector ? "yes" : "no", touch_vdso ? "yes" : "no");
  384. /* Create a new process group so we can signal everyone for exit */
  385. setpgid(getpid(), getpid());
  386. signal(SIGUSR1, sigusr1_handler);
  387. actions->setup(cpu1, cpu2);
  388. start_fn(actions->thread1, NULL, cpu1);
  389. start_fn(actions->thread2, NULL, cpu2);
  390. while (1)
  391. sleep(3600);
  392. return 0;
  393. }