1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29/* This program is used to benchmark various pthread operations
30 * Note that we want to be able to build it with GLibc, both on
31 *  a Linux host and an Android device. For example, on ARM, one
32 * can build it manually with:
33 *
34 *     arm-linux-none-gnueabi-gcc -static -o bench_pthread_gnueabi \
35 *           bench_pthread.c -O2 -lpthread -lrt
36 */
37#define _GNU_SOURCE 1
38#include <time.h>
39#include <stdio.h>
40#include <stdint.h>
41#include <limits.h>
42#include <pthread.h>
43#include <semaphore.h>
44#include <stdlib.h>
45#include <unistd.h>
46
47#define S(x)  S_(x)
48#define S_(x) #x
49
50#define C(x,y)  C_(x,y)
51#define C_(x,y) x ## y
52
53#ifndef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
54#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER  PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
55#endif
56
57#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER
58#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER  PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
59#endif
60
61static int64_t now_ns(void)
62{
63    struct timespec ts;
64    /* NOTE: get thread-specific CPU-time clock to ensure
65     *       we don't measure stuff like kernel thread preemptions
66     *       that might happen during the benchmark
67     */
68    clock_gettime(CLOCK_THREAD_CPUTIME_ID,&ts);
69    return ts.tv_sec*1000000000LL + ts.tv_nsec;
70}
71
72#define SUBCOUNT   10000
73#define MAX_STATS  1000000
74
75/* Maximum time we'll wait for a single bench run */
76#define MAX_WAIT_MS  1000
77
78static int64_t  stats[MAX_STATS];
79
80static int
81compare_stats(const void* a, const void* b)
82{
83    uint64_t sa = *(const uint64_t*)a;
84    uint64_t sb = *(const uint64_t*)b;
85    if (sa < sb)
86        return -1;
87    if (sa > sb)
88        return +1;
89    else
90        return 0;
91}
92
93static void
94filter_stats(int count, const char* statement)
95{
96    int64_t  min, max, avg, median;
97
98    /* sort the array in increasing order */
99    qsort(stats, count, sizeof(stats[0]), compare_stats);
100
101    /* trim 10% to remove outliers */
102    int min_index = count*0.05;
103    int max_index = count - min_index;
104    if (max_index >= count)
105        max_index = count-1;
106
107    count = (max_index - min_index)+1;
108
109    /* the median is the center item */
110    median = stats[(min_index+max_index)/2];
111
112    /* the minimum is the first, the max the last */
113    min = stats[min_index];
114    max = stats[max_index];
115
116    /* compute the average */
117    int nn;
118    int64_t  total = 0;
119    for (nn = min_index; nn <= max_index; nn++) {
120        total += stats[nn];
121    }
122
123    printf("BENCH: %5.1f %5.1f %5.1f, %s\n",
124           min*1./SUBCOUNT,
125           max*1./SUBCOUNT,
126           median*1./SUBCOUNT,
127           statement);
128    if (0) {
129        for (nn = min_index; nn <= max_index; nn++) {
130            printf(" %lld", (long long)stats[nn]);
131        }
132        printf("\n");
133    }
134}
135
136#define BENCH_COUNT(stmnt,total) do { \
137        int64_t  count = total; \
138        int      num_stats = 0; \
139        int64_t  bench_start = now_ns(); \
140        while (num_stats < MAX_STATS && count >= SUBCOUNT) { \
141            int      tries = SUBCOUNT; \
142            int64_t  sub_start = now_ns(); \
143            count -= tries; \
144            for ( ; tries > 0; tries-- ) {\
145                stmnt;\
146            }\
147            int64_t  sub_end = now_ns(); \
148            stats[num_stats++] = sub_end - sub_start; \
149            if (sub_end - bench_start >= MAX_WAIT_MS*1e6) \
150                break; \
151        } \
152        filter_stats(num_stats, #stmnt); \
153    } while (0)
154
155#define DEFAULT_COUNT 10000000
156
157#define BENCH(stmnt) BENCH_COUNT(stmnt,DEFAULT_COUNT)
158
159/* Will be called by pthread_once() for benchmarking */
160static void _dummy_init(void)
161{
162    /* nothing */
163}
164
165/* Used when creating the key */
166static void key_destroy(void* param)
167{
168    /* nothing */
169}
170
171int main(void)
172{
173    pthread_once_t  once = PTHREAD_ONCE_INIT;
174    pthread_once(&once, _dummy_init);
175
176    pthread_key_t   key;
177    pthread_key_create(&key, key_destroy);
178    pthread_setspecific(key, (void*)(int)100);
179
180    BENCH(getpid());
181    BENCH(pthread_self());
182    BENCH(pthread_getspecific(key));
183    BENCH(pthread_once(&once, _dummy_init));
184
185    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
186    BENCH(pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex));
187
188    pthread_mutex_t errorcheck_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;
189    BENCH(pthread_mutex_lock(&errorcheck_mutex); pthread_mutex_unlock(&errorcheck_mutex));
190
191    pthread_mutex_t recursive_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
192    BENCH(pthread_mutex_lock(&recursive_mutex); pthread_mutex_unlock(&recursive_mutex));
193
194	/* TODO: Benchmark pshared mutexes */
195
196    sem_t semaphore;
197    int dummy;
198    sem_init(&semaphore, 1, 1);
199    BENCH(sem_getvalue(&semaphore,&dummy));
200    BENCH(sem_wait(&semaphore); sem_post(&semaphore));
201    return 0;
202}
203