1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* tests/threads/init_ctx.c */ 3 /* 4 * Copyright (C) 2009 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 /* 28 * krb5 context creation performance testing 29 * initially contributed by Ken Raeburn 30 */ 31 32 #include "k5-platform.h" 33 #include <unistd.h> 34 #include <pthread.h> 35 #include <krb5.h> 36 37 #include <sys/time.h> 38 #include <sys/resource.h> 39 40 #define N_THREADS 4 41 #define ITER_COUNT 40000 42 static int init_krb5_first = 0; 43 44 struct resource_info { 45 struct timeval start_time, end_time; 46 }; 47 struct thread_info { 48 pthread_t tid; 49 struct resource_info r; 50 }; 51 52 static char *prog; 53 static unsigned int n_threads = N_THREADS; 54 static int iter_count = ITER_COUNT; 55 static int do_pause; 56 57 static void usage (void) __attribute__((noreturn)); 58 59 static void 60 usage () 61 { 62 fprintf (stderr, "usage: %s [ options ]\n", prog); 63 fprintf (stderr, "options:\n"); 64 fprintf (stderr, "\t-t N\tspecify number of threads (default %d)\n", 65 N_THREADS); 66 fprintf (stderr, "\t-i N\tset iteration count (default %d)\n", 67 ITER_COUNT); 68 fprintf (stderr, "\t-K\tinitialize a krb5_context for the duration\n"); 69 fprintf (stderr, "\t-P\tpause briefly after starting, to allow attaching dtrace/strace/etc\n"); 70 exit (1); 71 } 72 73 static int 74 numarg (char *arg) 75 { 76 char *end; 77 long val; 78 79 val = strtol (arg, &end, 10); 80 if (*arg == 0 || *end != 0) { 81 fprintf (stderr, "invalid numeric argument '%s'\n", arg); 82 usage (); 83 } 84 if (val >= 1 && val <= INT_MAX) 85 return val; 86 fprintf (stderr, "out of range numeric value %ld (1..%d)\n", 87 val, INT_MAX); 88 usage (); 89 } 90 91 static char optstring[] = "t:i:KP"; 92 93 static void 94 process_options (int argc, char *argv[]) 95 { 96 int c; 97 98 prog = strrchr (argv[0], '/'); 99 if (prog) 100 prog++; 101 else 102 prog = argv[0]; 103 while ((c = getopt (argc, argv, optstring)) != -1) { 104 switch (c) { 105 case '?': 106 case ':': 107 usage (); 108 break; 109 110 case 't': 111 n_threads = numarg (optarg); 112 if (n_threads >= SIZE_MAX / sizeof (struct thread_info)) { 113 n_threads = SIZE_MAX / sizeof (struct thread_info); 114 fprintf (stderr, "limiting n_threads to %u\n", n_threads); 115 } 116 break; 117 118 case 'i': 119 iter_count = numarg (optarg); 120 break; 121 122 case 'K': 123 init_krb5_first = 1; 124 break; 125 126 case 'P': 127 do_pause = 1; 128 break; 129 } 130 } 131 if (argc != optind) 132 usage (); 133 } 134 135 static long double 136 tvsub (struct timeval t1, struct timeval t2) 137 { 138 /* POSIX says .tv_usec is signed. */ 139 return (t1.tv_sec - t2.tv_sec 140 + (long double) 1.0e-6 * (t1.tv_usec - t2.tv_usec)); 141 } 142 143 static struct timeval 144 now (void) 145 { 146 struct timeval tv; 147 if (gettimeofday (&tv, NULL) < 0) { 148 perror ("gettimeofday"); 149 exit (1); 150 } 151 return tv; 152 } 153 154 static void run_iterations (struct resource_info *r) 155 { 156 int i; 157 krb5_error_code err; 158 krb5_context ctx; 159 160 r->start_time = now (); 161 for (i = 0; i < iter_count; i++) { 162 err = krb5_init_context(&ctx); 163 if (err) { 164 com_err(prog, err, "initializing krb5 context"); 165 exit(1); 166 } 167 krb5_free_context(ctx); 168 } 169 r->end_time = now (); 170 } 171 172 static void * 173 thread_proc (void *p) 174 { 175 run_iterations (p); 176 return 0; 177 } 178 179 static struct thread_info *tinfo; 180 181 static krb5_context kctx; 182 static struct rusage start, finish; 183 static struct timeval start_time, finish_time; 184 185 int 186 main (int argc, char *argv[]) 187 { 188 long double user, sys, wallclock, total; 189 unsigned int i; 190 191 process_options (argc, argv); 192 193 /* 194 * Some places in the krb5 library cache data globally. 195 * This option allows you to test the effect of that. 196 */ 197 if (init_krb5_first && krb5_init_context (&kctx) != 0) { 198 fprintf (stderr, "krb5_init_context error\n"); 199 exit (1); 200 } 201 tinfo = calloc (n_threads, sizeof (*tinfo)); 202 if (tinfo == NULL) { 203 perror ("calloc"); 204 exit (1); 205 } 206 printf ("Threads: %d iterations: %d\n", n_threads, iter_count); 207 if (do_pause) { 208 printf ("pid %lu napping...\n", (unsigned long) getpid ()); 209 sleep (10); 210 } 211 printf ("starting...\n"); 212 /* And *now* we start measuring the performance. */ 213 if (getrusage (RUSAGE_SELF, &start) < 0) { 214 perror ("getrusage"); 215 exit (1); 216 } 217 start_time = now (); 218 #define foreach_thread(IDXVAR) for (IDXVAR = 0; IDXVAR < n_threads; IDXVAR++) 219 foreach_thread (i) { 220 int err; 221 222 err = pthread_create (&tinfo[i].tid, NULL, thread_proc, &tinfo[i].r); 223 if (err) { 224 fprintf (stderr, "pthread_create: %s\n", strerror (err)); 225 exit (1); 226 } 227 } 228 foreach_thread (i) { 229 int err; 230 void *val; 231 232 err = pthread_join (tinfo[i].tid, &val); 233 if (err) { 234 fprintf (stderr, "pthread_join: %s\n", strerror (err)); 235 exit (1); 236 } 237 } 238 finish_time = now (); 239 if (getrusage (RUSAGE_SELF, &finish) < 0) { 240 perror ("getrusage"); 241 exit (1); 242 } 243 if (init_krb5_first) 244 krb5_free_context (kctx); 245 foreach_thread (i) { 246 printf ("Thread %2d: elapsed time %Lfs\n", i, 247 tvsub (tinfo[i].r.end_time, tinfo[i].r.start_time)); 248 } 249 wallclock = tvsub (finish_time, start_time); 250 /* 251 * Report on elapsed time and CPU usage. Depending what 252 * performance issue you're chasing down, different values may be 253 * of particular interest, so report all the info we've got. 254 */ 255 printf ("Overall run time with %d threads = %Lfs, %Lfms per iteration.\n", 256 n_threads, wallclock, 1000 * wallclock / iter_count); 257 user = tvsub (finish.ru_utime, start.ru_utime); 258 sys = tvsub (finish.ru_stime, start.ru_stime); 259 total = user + sys; 260 printf ("CPU usage: user=%Lfs sys=%Lfs total=%Lfs.\n", user, sys, total); 261 printf ("Utilization: user=%5.1Lf%% sys=%5.1Lf%% total=%5.1Lf%%\n", 262 100 * user / wallclock, 263 100 * sys / wallclock, 264 100 * total / wallclock); 265 printf ("Util/thread: user=%5.1Lf%% sys=%5.1Lf%% total=%5.1Lf%%\n", 266 100 * user / wallclock / n_threads, 267 100 * sys / wallclock / n_threads, 268 100 * total / wallclock / n_threads); 269 printf ("Total CPU use per iteration per thread: %Lfms\n", 270 1000 * total / n_threads / iter_count); 271 free(tinfo); 272 return 0; 273 } 274