platform_bionic/benchmarks/semaphore_benchmark.cpp
Christopher Ferris 1783941f23 Fix infinite loop if semaphore test is not run.
When using the --benchmark_filter option, all of the test objects
get created, but not all are run. Previously, if this test didn't run
it would get into an infinite loop waiting for the test to complete.
This change only waits for the test to complete if it was actually
executed.

Change-Id: I5151a0b4b3d5349b978e716ec4a02ebd8b4eae00
2016-06-06 14:13:17 -07:00

152 lines
4.7 KiB
C++

/*
* 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>
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);
}
}
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);
}
}
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 NULL;
}
class SemaphoreFixture : public benchmark::Fixture {
public:
void SetUp(const benchmark::State&) {
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() {
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;
};
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);
}
}