blob: 974b0460ed7ab3dd46b027f3401ffc7717309eab [file] [log] [blame]
Elliott Hughesb28e4902014-03-11 11:19:06 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "benchmark.h"
18
Mark Salyzyn7e50fb22015-02-09 08:18:10 -080019#include <pthread.h>
Elliott Hughesb28e4902014-03-11 11:19:06 -070020#include <semaphore.h>
Mark Salyzyn7e50fb22015-02-09 08:18:10 -080021#include <stdatomic.h>
22#include <stdio.h>
Elliott Hughesb28e4902014-03-11 11:19:06 -070023
24static void BM_semaphore_sem_getvalue(int iters) {
25 StopBenchmarkTiming();
26 sem_t semaphore;
27 sem_init(&semaphore, 1, 1);
28 StartBenchmarkTiming();
29
30 for (int i = 0; i < iters; ++i) {
31 int dummy;
32 sem_getvalue(&semaphore, &dummy);
33 }
34
35 StopBenchmarkTiming();
36}
37BENCHMARK(BM_semaphore_sem_getvalue);
38
39static void BM_semaphore_sem_wait_sem_post(int iters) {
40 StopBenchmarkTiming();
41 sem_t semaphore;
42 sem_init(&semaphore, 1, 1);
43 StartBenchmarkTiming();
44
45 for (int i = 0; i < iters; ++i) {
46 sem_wait(&semaphore);
47 sem_post(&semaphore);
48 }
49
50 StopBenchmarkTiming();
51}
52BENCHMARK(BM_semaphore_sem_wait_sem_post);
Mark Salyzyn7e50fb22015-02-09 08:18:10 -080053
54/*
55 * This test reports the overhead of the underlying futex wake syscall on
56 * the producer. It does not report the overhead from issuing the wake to the
57 * point where the posted consumer thread wakes up. It suffers from
58 * clock_gettime syscall overhead. Lock the CPU speed for consistent results
59 * as we may not reach >50% cpu utilization.
60 *
61 * We will run a background thread that catches the sem_post wakeup and
62 * loops immediately returning back to sleep in sem_wait for the next one. This
63 * thread is run with policy SCHED_OTHER (normal policy), a middle policy.
64 *
65 * The primary thread will run at SCHED_IDLE (lowest priority policy) when
66 * monitoring the background thread to detect when it hits sem_wait sleep. It
67 * will do so with no clock running. Once we are ready, we will switch to
68 * SCHED_FIFO (highest priority policy) to time the act of running sem_post
69 * with the benchmark clock running. This ensures nothing else in the system
70 * can preempt our timed activity, including the background thread. We are
71 * also protected with the scheduling policy of letting a process hit a
72 * resource limit rather than get hit with a context switch.
73 *
74 * The background thread will start executing either on another CPU, or
75 * after we back down from SCHED_FIFO, but certainly not in the context of
76 * the timing of the sem_post.
77 */
78static atomic_int BM_semaphore_sem_post_running;
79
80static void *BM_semaphore_sem_post_start_thread(void *obj) {
81 sem_t *semaphore = reinterpret_cast<sem_t *>(obj);
82
83 while ((BM_semaphore_sem_post_running > 0) && !sem_wait(semaphore)) {
84 ;
85 }
86 BM_semaphore_sem_post_running = -1;
87 return NULL;
88}
89
90static void BM_semaphore_sem_post(int iters) {
91 StopBenchmarkTiming();
92
93 sem_t semaphore;
94 sem_init(&semaphore, 0, 0);
95
96 pthread_attr_t attr;
97 pthread_attr_init(&attr);
98 BM_semaphore_sem_post_running = 1;
99 struct sched_param param = { 0, };
100 pthread_attr_setschedparam(&attr, &param);
101 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
102 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Mark Salyzyn5d064462015-02-10 08:34:35 -0800103#ifdef PTHREAD_SET_INHERIT_SCHED
104 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
105#endif
Mark Salyzyn7e50fb22015-02-09 08:18:10 -0800106 pthread_t pthread;
107 pthread_create(&pthread, &attr, BM_semaphore_sem_post_start_thread, &semaphore);
108 pthread_attr_destroy(&attr);
109
110 sched_setscheduler((pid_t)0, SCHED_IDLE, &param);
111 for (int i = 0; i < iters; ++i) {
112 int trys = 3, dummy = 0;
113 do {
114 if (BM_semaphore_sem_post_running < 0) {
115 sched_setscheduler((pid_t)0, SCHED_OTHER, &param);
116 fprintf(stderr, "BM_semaphore_sem_post: start_thread died unexpectedly\n");
117 return;
118 }
119 sched_yield();
120 sem_getvalue(&semaphore, &dummy);
121 if (dummy < 0) { // POSIX.1-2001 possibility 1
122 break;
123 }
124 if (dummy == 0) { // POSIX.1-2001 possibility 2
125 --trys;
126 }
127 } while (trys);
128 param.sched_priority = 1;
129 sched_setscheduler((pid_t)0, SCHED_FIFO, &param);
130 StartBenchmarkTiming();
131 sem_post(&semaphore);
132 StopBenchmarkTiming(); // Remember to subtract clock syscall overhead
133 param.sched_priority = 0;
134 sched_setscheduler((pid_t)0, SCHED_IDLE, &param);
135 }
136 sched_setscheduler((pid_t)0, SCHED_OTHER, &param);
137
138 if (BM_semaphore_sem_post_running > 0) {
139 BM_semaphore_sem_post_running = 0;
140 }
141 do {
142 sem_post(&semaphore);
143 sched_yield();
144 } while (!BM_semaphore_sem_post_running);
145}
146BENCHMARK(BM_semaphore_sem_post);
Mark Salyzyn5d064462015-02-10 08:34:35 -0800147
148/*
149 * This test reports the overhead of sem_post to sem_wake. A circle of
150 * num_semaphore - 1 threads are run on a set of semaphores to measure the
151 * activity. One can calculate the sem_wake overhead alone by:
152 *
153 * BM_semaphore_sem_post_sem_wait - BM_semaphore_sem_post - BM_time_clock_gettime
154 *
155 * Differences will result if there are more threads than active processors,
156 * there will be delay induced when scheduling the processes. This cost is
157 * measured by trying different values of num_semaphore. The governor selected
158 * will have a major impact on the results for a large number of threads.
159 *
160 * To reduce the chances for threads racing ahead and not triggering the
161 * futex, for example the background threads finish their job before the
162 * sem_wait is hit in the main thread, the background threads will run at
163 * batch priority and the main thread at fifo priority. This should generally
164 * guarantee the main thread completes its task of priming itself with the
165 * sem_wait before the other threads can start. In practice without the
166 * sched mechanics here, this works on Android configured kernels, this is
167 * insurance for wacky(tm) sched configurations.
168 */
169static void *BM_semaphore_sem_post_sem_wait_start_thread(void *obj) {
170 sem_t *semaphore = reinterpret_cast<sem_t *>(obj);
171
172 while ((BM_semaphore_sem_post_running > 0) && !sem_wait(semaphore)) {
173 sem_post(semaphore + 1);
174 }
175 --BM_semaphore_sem_post_running;
176 return NULL;
177}
178
179static void BM_semaphore_sem_post_sem_wait_num(int iters, int num_semaphore) {
180 StopBenchmarkTiming();
181
182 sem_t semaphore[num_semaphore];
183
184 for (int i = 0; i < num_semaphore; ++i) {
185 sem_init(semaphore + i, 0, 0);
186 }
187
188 pthread_attr_t attr;
189 pthread_attr_init(&attr);
190 BM_semaphore_sem_post_running = 1;
191 struct sched_param param = { 0, };
192 pthread_attr_setschedparam(&attr, &param);
193 pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
194 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
195#ifdef PTHREAD_SET_INHERIT_SCHED
196 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
197#endif
198 for (int i = 0; i < (num_semaphore - 1); ++i) {
199 pthread_t pthread;
200 pthread_create(&pthread, &attr, BM_semaphore_sem_post_sem_wait_start_thread, semaphore + i);
201 }
202 pthread_attr_destroy(&attr);
203 sched_yield();
204
205 param.sched_priority = 1;
206 sched_setscheduler((pid_t)0, SCHED_FIFO, &param);
207
208 StartBenchmarkTiming();
209
210 for (int i = 0; i < iters; i += num_semaphore) {
211 sem_post(semaphore);
212 sem_wait(semaphore + num_semaphore - 1);
213 }
214
215 StopBenchmarkTiming();
216
217 param.sched_priority = 0;
218 sched_setscheduler((pid_t)0, SCHED_OTHER, &param);
219
220 if (BM_semaphore_sem_post_running > 0) {
221 BM_semaphore_sem_post_running = 0;
222 }
223 for (int i = 0;
224 (i < (10 * num_semaphore)) && (BM_semaphore_sem_post_running > (1 - num_semaphore));
225 ++i) {
226 for (int j = 0; j < (num_semaphore - 1); ++j) {
227 sem_post(semaphore + j);
228 }
229 sched_yield();
230 }
231}
232
233static void BM_semaphore_sem_post_sem_wait_low(int iters) {
234 BM_semaphore_sem_post_sem_wait_num(iters, 2);
235}
236BENCHMARK(BM_semaphore_sem_post_sem_wait_low);
237
238static void BM_semaphore_sem_post_sem_wait_high(int iters) {
239 BM_semaphore_sem_post_sem_wait_num(iters, 100);
240}
241BENCHMARK(BM_semaphore_sem_post_sem_wait_high);