1/*
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 <pthread.h>
18#include <semaphore.h>
19#include <stdatomic.h>
20#include <stdio.h>
21#include <stdlib.h>
22
23#include <benchmark/benchmark.h>
24
25static void BM_semaphore_sem_getvalue(benchmark::State& state) {
26  sem_t semaphore;
27  sem_init(&semaphore, 1, 1);
28
29  while (state.KeepRunning()) {
30    int dummy;
31    sem_getvalue(&semaphore, &dummy);
32  }
33}
34BENCHMARK(BM_semaphore_sem_getvalue);
35
36static void BM_semaphore_sem_wait_sem_post(benchmark::State& state) {
37  sem_t semaphore;
38  sem_init(&semaphore, 1, 1);
39
40  while (state.KeepRunning()) {
41    sem_wait(&semaphore);
42    sem_post(&semaphore);
43  }
44}
45BENCHMARK(BM_semaphore_sem_wait_sem_post);
46
47// This test reports the overhead of the underlying futex wake syscall on
48// the producer. It does not report the overhead from issuing the wake to the
49// point where the posted consumer thread wakes up. It suffers from
50// clock_gettime syscall overhead. Lock the CPU speed for consistent results
51// as we may not reach >50% cpu utilization.
52//
53// We will run a background thread that catches the sem_post wakeup and
54// loops immediately returning back to sleep in sem_wait for the next one. This
55// thread is run with policy SCHED_OTHER (normal policy), a middle policy.
56//
57// The primary thread will run at SCHED_IDLE (lowest priority policy) when
58// monitoring the background thread to detect when it hits sem_wait sleep. It
59// will do so with no clock running. Once we are ready, we will switch to
60// SCHED_FIFO (highest priority policy) to time the act of running sem_post
61// with the benchmark clock running. This ensures nothing else in the system
62// can preempt our timed activity, including the background thread. We are
63// also protected with the scheduling policy of letting a process hit a
64// resource limit rather than get hit with a context switch.
65//
66// The background thread will start executing either on another CPU, or
67// after we back down from SCHED_FIFO, but certainly not in the context of
68// the timing of the sem_post.
69
70static atomic_int BM_semaphore_sem_post_running;
71
72static void* BM_semaphore_sem_post_start_thread(void* arg) {
73  sem_t* semaphore = reinterpret_cast<sem_t*>(arg);
74  while ((BM_semaphore_sem_post_running > 0) && !sem_wait(semaphore)) {
75  }
76  BM_semaphore_sem_post_running = -1;
77  return NULL;
78}
79
80class SemaphoreFixture : public benchmark::Fixture {
81 public:
82  void SetUp(const benchmark::State&) {
83    sem_init(&semaphore, 0, 0);
84
85    pthread_attr_t attr;
86    pthread_attr_init(&attr);
87
88    memset(&param, 0, sizeof(param));
89    pthread_attr_setschedparam(&attr, &param);
90    pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
91    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
92    pthread_t pthread;
93    pthread_create(&pthread, &attr, BM_semaphore_sem_post_start_thread, &semaphore);
94    pthread_attr_destroy(&attr);
95
96    sched_setscheduler(0, SCHED_IDLE, &param);
97
98    BM_semaphore_sem_post_running = 1;
99  }
100
101  ~SemaphoreFixture() {
102    sched_setscheduler(0, SCHED_OTHER, &param);
103
104    if (BM_semaphore_sem_post_running > 0) {
105      BM_semaphore_sem_post_running = 0;
106    }
107    do {
108      sem_post(&semaphore);
109      sched_yield();
110    } while (BM_semaphore_sem_post_running != -1);
111  }
112
113  sem_t semaphore;
114  sched_param param;
115};
116
117BENCHMARK_F(SemaphoreFixture, semaphore_sem_post)(benchmark::State& state) {
118  while (state.KeepRunning()) {
119    state.PauseTiming();
120
121    int trys = 3, dummy = 0;
122    do {
123      if (BM_semaphore_sem_post_running < 0) {
124        sched_setscheduler(0, SCHED_OTHER, &param);
125        fprintf(stderr, "BM_semaphore_sem_post: start_thread died unexpectedly\n");
126        abort();
127      }
128      sched_yield();
129      sem_getvalue(&semaphore, &dummy);
130      if (dummy < 0) {  // POSIX.1-2001 possibility 1
131        break;
132      }
133      if (dummy == 0) { // POSIX.1-2001 possibility 2
134        --trys;
135      }
136    } while (trys);
137
138    param.sched_priority = 1;
139    sched_setscheduler(0, SCHED_FIFO, &param);
140
141    state.ResumeTiming();
142    sem_post(&semaphore);
143
144    param.sched_priority = 0;
145    sched_setscheduler(0, SCHED_IDLE, &param);
146  }
147}
148