context_switch.c 8.3 KB

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