Home | History | Annotate | Download | only in common
      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 
     61 static 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 
     78 static int64_t  stats[MAX_STATS];
     79 
     80 static int
     81 compare_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 
     93 static void
     94 filter_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 */
    160 static void _dummy_init(void)
    161 {
    162     /* nothing */
    163 }
    164 
    165 /* Used when creating the key */
    166 static void key_destroy(void* param)
    167 {
    168     /* nothing */
    169 }
    170 
    171 int 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