/*
* Copyright (C) 2011 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* This program is used to benchmark various pthread operations
* Note that we want to be able to build it with GLibc, both on
* a Linux host and an Android device. For example, on ARM, one
* can build it manually with:
*
* arm-linux-none-gnueabi-gcc -static -o bench_pthread_gnueabi \
* bench_pthread.c -O2 -lpthread -lrt
*/
#define _GNU_SOURCE 1
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#define S(x) S_(x)
#define S_(x) #x
#define C(x,y) C_(x,y)
#define C_(x,y) x ## y
#ifndef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
#endif
#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
#endif
static int64_t now_ns(void)
{
struct timespec ts;
/* NOTE: get thread-specific CPU-time clock to ensure
* we don't measure stuff like kernel thread preemptions
* that might happen during the benchmark
*/
clock_gettime(CLOCK_THREAD_CPUTIME_ID,&ts);
return ts.tv_sec*1000000000LL + ts.tv_nsec;
}
#define SUBCOUNT 10000
#define MAX_STATS 1000000
/* Maximum time we'll wait for a single bench run */
#define MAX_WAIT_MS 1000
static int64_t stats[MAX_STATS];
static int
compare_stats(const void* a, const void* b)
{
uint64_t sa = *(const uint64_t*)a;
uint64_t sb = *(const uint64_t*)b;
if (sa < sb)
return -1;
if (sa > sb)
return +1;
else
return 0;
}
static void
filter_stats(int count, const char* statement)
{
int64_t min, max, avg, median;
/* sort the array in increasing order */
qsort(stats, count, sizeof(stats[0]), compare_stats);
/* trim 10% to remove outliers */
int min_index = count*0.05;
int max_index = count - min_index;
if (max_index >= count)
max_index = count-1;
count = (max_index - min_index)+1;
/* the median is the center item */
median = stats[(min_index+max_index)/2];
/* the minimum is the first, the max the last */
min = stats[min_index];
max = stats[max_index];
/* compute the average */
int nn;
int64_t total = 0;
for (nn = min_index; nn <= max_index; nn++) {
total += stats[nn];
}
printf("BENCH: %5.1f %5.1f %5.1f, %s\n",
min*1./SUBCOUNT,
max*1./SUBCOUNT,
median*1./SUBCOUNT,
statement);
if (0) {
for (nn = min_index; nn <= max_index; nn++) {
printf(" %lld", (long long)stats[nn]);
}
printf("\n");
}
}
#define BENCH_COUNT(stmnt,total) do { \
int64_t count = total; \
int num_stats = 0; \
int64_t bench_start = now_ns(); \
while (num_stats < MAX_STATS && count >= SUBCOUNT) { \
int tries = SUBCOUNT; \
int64_t sub_start = now_ns(); \
count -= tries; \
for ( ; tries > 0; tries-- ) {\
stmnt;\
}\
int64_t sub_end = now_ns(); \
stats[num_stats++] = sub_end - sub_start; \
if (sub_end - bench_start >= MAX_WAIT_MS*1e6) \
break; \
} \
filter_stats(num_stats, #stmnt); \
} while (0)
#define DEFAULT_COUNT 10000000
#define BENCH(stmnt) BENCH_COUNT(stmnt,DEFAULT_COUNT)
/* Will be called by pthread_once() for benchmarking */
static void _dummy_init(void)
{
/* nothing */
}
/* Used when creating the key */
static void key_destroy(void* param)
{
/* nothing */
}
int main(void)
{
pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once, _dummy_init);
pthread_key_t key;
pthread_key_create(&key, key_destroy);
pthread_setspecific(key, (void*)(int)100);
BENCH(getpid());
BENCH(pthread_self());
BENCH(pthread_getspecific(key));
BENCH(pthread_once(&once, _dummy_init));
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
BENCH(pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex));
pthread_mutex_t errorcheck_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;
BENCH(pthread_mutex_lock(&errorcheck_mutex); pthread_mutex_unlock(&errorcheck_mutex));
pthread_mutex_t recursive_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
BENCH(pthread_mutex_lock(&recursive_mutex); pthread_mutex_unlock(&recursive_mutex));
/* TODO: Benchmark pshared mutexes */
sem_t semaphore;
int dummy;
sem_init(&semaphore, 1, 1);
BENCH(sem_getvalue(&semaphore,&dummy));
BENCH(sem_wait(&semaphore); sem_post(&semaphore));
return 0;
}