ronnie
2022-10-23 cb8ede114f8c3e5ead5b294f66344b8a42004745
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include <pthread.h>
#include <semaphore.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
 
#include <benchmark/benchmark.h>
#include "util.h"
 
static void BM_semaphore_sem_getvalue(benchmark::State& state) {
  sem_t semaphore;
  sem_init(&semaphore, 1, 1);
 
  while (state.KeepRunning()) {
    int dummy;
    sem_getvalue(&semaphore, &dummy);
  }
}
BIONIC_BENCHMARK(BM_semaphore_sem_getvalue);
 
static void BM_semaphore_sem_wait_sem_post(benchmark::State& state) {
  sem_t semaphore;
  sem_init(&semaphore, 1, 1);
 
  while (state.KeepRunning()) {
    sem_wait(&semaphore);
    sem_post(&semaphore);
  }
}
BIONIC_BENCHMARK(BM_semaphore_sem_wait_sem_post);
 
// This test reports the overhead of the underlying futex wake syscall on
// the producer. It does not report the overhead from issuing the wake to the
// point where the posted consumer thread wakes up. It suffers from
// clock_gettime syscall overhead. Lock the CPU speed for consistent results
// as we may not reach >50% cpu utilization.
//
// We will run a background thread that catches the sem_post wakeup and
// loops immediately returning back to sleep in sem_wait for the next one. This
// thread is run with policy SCHED_OTHER (normal policy), a middle policy.
//
// The primary thread will run at SCHED_IDLE (lowest priority policy) when
// monitoring the background thread to detect when it hits sem_wait sleep. It
// will do so with no clock running. Once we are ready, we will switch to
// SCHED_FIFO (highest priority policy) to time the act of running sem_post
// with the benchmark clock running. This ensures nothing else in the system
// can preempt our timed activity, including the background thread. We are
// also protected with the scheduling policy of letting a process hit a
// resource limit rather than get hit with a context switch.
//
// The background thread will start executing either on another CPU, or
// after we back down from SCHED_FIFO, but certainly not in the context of
// the timing of the sem_post.
 
static atomic_int BM_semaphore_sem_post_running;
 
static void* BM_semaphore_sem_post_start_thread(void* arg) {
  sem_t* semaphore = reinterpret_cast<sem_t*>(arg);
  while ((BM_semaphore_sem_post_running > 0) && !sem_wait(semaphore)) {
  }
  BM_semaphore_sem_post_running = -1;
  return nullptr;
}
 
class SemaphoreFixture : public benchmark::Fixture {
 public:
  void SetUp(const benchmark::State&) override {
    sem_init(&semaphore, 0, 0);
 
    pthread_attr_t attr;
    pthread_attr_init(&attr);
 
    memset(&param, 0, sizeof(param));
    pthread_attr_setschedparam(&attr, &param);
    pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_t pthread;
    pthread_create(&pthread, &attr, BM_semaphore_sem_post_start_thread, &semaphore);
    pthread_attr_destroy(&attr);
 
    sched_setscheduler(0, SCHED_IDLE, &param);
 
    BM_semaphore_sem_post_running = 1;
    setup = true;
  }
 
  ~SemaphoreFixture() override {
    if (setup) {
      // Only do this if the test was actually run.
      sched_setscheduler(0, SCHED_OTHER, &param);
 
      if (BM_semaphore_sem_post_running > 0) {
        BM_semaphore_sem_post_running = 0;
      }
      do {
        sem_post(&semaphore);
        sched_yield();
      } while (BM_semaphore_sem_post_running != -1);
    }
  }
 
  sem_t semaphore;
  sched_param param;
  bool setup = false;
};
 
// This is commented out because dynamic benchmark registering doesn't currently support fixtures.
// Uncomment it and recompile to run this benchmark on every run.
/* BENCHMARK_F(SemaphoreFixture, semaphore_sem_post)(benchmark::State& state) {
  while (state.KeepRunning()) {
    state.PauseTiming();
 
    int trys = 3, dummy = 0;
    do {
      if (BM_semaphore_sem_post_running < 0) {
        sched_setscheduler(0, SCHED_OTHER, &param);
        fprintf(stderr, "BM_semaphore_sem_post: start_thread died unexpectedly\n");
        abort();
      }
      sched_yield();
      sem_getvalue(&semaphore, &dummy);
      if (dummy < 0) {  // POSIX.1-2001 possibility 1
        break;
      }
      if (dummy == 0) { // POSIX.1-2001 possibility 2
        --trys;
      }
    } while (trys);
 
    param.sched_priority = 1;
    sched_setscheduler(0, SCHED_FIFO, &param);
 
    state.ResumeTiming();
    sem_post(&semaphore);
 
    param.sched_priority = 0;
    sched_setscheduler(0, SCHED_IDLE, &param);
  }
}*/