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