/* * trigbench.c - demonstrate timings of the trig functions * * You want to compile with the best inline optimizations possible * so the trig functions will use hardware instructions rather * then going to a math lib software solution. For example, * on a Pentium class Intel processor, gcc options would be: * * gcc -O3 -fno-rtti -funroll-loops -mno-ieee-fp -ffast-math \ * -march=pentium -o trigbench trigbench.c -lm * * Can take one argument, the number of seconds to target for in * each test. e.g. * ./trigbench 8 * requests 8 seconds in each test. With no arguments, the default * is 5 to 7 seconds in each test. It takes at least that much time * to calibrate itself, so a complete run takes much longer actually. * * JD 2451687.191458 - Mon May 22 16:35:42 PDT 2000 - created * Hiram Clawson */ #include #include #include #include #include static struct tms t0; /* tms structure with user and sys clock ticks */ static time_t tloc0; /* seconds since epoch */ static struct tms t1; /* tms structure with user and sys clock ticks */ static time_t tloc1; /* seconds since epoch */ static int target_seconds; /* external result will help prevent compiler optimizations */ static double result; void set_t0() { (void) times( & t0 ); (void) time( & tloc0 ); return; } void set_t1() { (void) times( & t1 ); (void) time( & tloc1 ); return; } #if defined(NOT) void showtime(struct tms *t, time_t * tloc) { char ctime_buf[128]; int sl; (void) ctime_r(tloc, ctime_buf); sl=strlen(ctime_buf); ctime_buf[sl-1] = (char) 0; printf( "%d %s tloc: %d, user: %d, sys: %d\n", sl, ctime_buf, *tloc, t->tms_utime, t->tms_stime ); return; } #endif /* NOT */ void elapsed_time(char * test_name, struct tms * t, time_t * tloc, unsigned int reps) { double ticks_per_sec = (double) CLK_TCK; time_t et_tloc = *tloc - tloc0; clock_t et_utime = t->tms_utime - t0.tms_utime; clock_t et_stime = t->tms_stime - t0.tms_stime; clock_t total_et = et_utime + et_stime; /* protect against divide by zero */ if ( et_tloc < 1 ) et_tloc = 1; if ( total_et < 1 ) total_et = 1; printf("%s %6d %6.2f %6.2f %6.2f %10d %10.0f %10.0f\n", test_name, et_tloc, et_utime/ticks_per_sec, et_stime/ticks_per_sec, total_et/ticks_per_sec, reps, ((double)reps)/(total_et/ticks_per_sec), (double)reps/et_tloc ); return; } void sin_test(unsigned long reps) { unsigned long i; double test_arg; double range; double increment; /* Want to run test over the range [-1.6:+1.6] radians */ range = 3.2; /* radians */ test_arg = -(range/2.0); /* radians start */ increment = range / (double) reps; /* radians increment */ for ( i = 0; i < reps; ++i ) { result = sin(test_arg); test_arg += increment; } } void cos_test(unsigned long reps) { unsigned long i; double test_arg; double range; double increment; /* Want to run test over the range [-1.6:+1.6] radians */ range = 3.2; /* radians */ test_arg = -(range/2.0); /* radians start */ increment = range / (double) reps; /* radians increment */ for ( i = 0; i < reps; ++i ) { result = cos(test_arg); test_arg += increment; } } void exp_test(unsigned long reps) { unsigned long i; double test_arg; double range; double increment; /* Want to run test over the range [-509:+509] radians */ range = 1018.0; /* radians */ test_arg = -(range/2.0); /* radians start */ increment = range / (double) reps; /* radians increment */ for ( i = 0; i < reps; ++i ) { result = exp(test_arg); test_arg += increment; } } void log_test(unsigned long reps) { unsigned long i; double test_arg; for ( i = 0; i < reps; ++i ) { test_arg = i; result = log(test_arg); } } void atan_test(unsigned long reps) { unsigned long i; double test_arg; for ( i = 0; i < reps; ++i ) { test_arg = i; result = atan(test_arg); } } perform_test( void test_fp(unsigned long), char * ident ) { unsigned int reps; int calibrated; double et_seconds; double ticks_per_sec = (double) CLK_TCK; /* calibrate the test, aim for target_seconds +-1 */ reps = 8; calibrated = 0; while (! calibrated ) { set_t0(); test_fp(reps); set_t1(); et_seconds = (t1.tms_utime - t0.tms_utime) / ticks_per_sec; if (((int)et_seconds >= target_seconds-1) && ((int)et_seconds <= target_seconds+1) ) { calibrated = 1; } else { if ( (tloc1-tloc0) > 1 ) { /* new target = (%50 of old target) + (%50 of new target) */ reps = (0.50*reps) + (0.50* ((unsigned int) (((double)target_seconds) * ((double)reps/et_seconds))) ); } else { reps <<= 2; } } } set_t0(); test_fp(reps); set_t1(); elapsed_time( ident ,& t1, & tloc1, reps ); /* et = t0 - t1 */ return; } void Usage() { printf ("\nusage: trigbench [n]\n" ); printf ("\tn - target seconds for each test. Default is 6, minimum of 2\n" ); exit( 0 ); } main(int argc, char *argv[]) { double ticks_per_sec = (double) CLK_TCK; target_seconds = 6; if ( 2 <= argc ) { target_seconds = atoi(argv[1]); if ( target_seconds < 2 ) Usage(); } printf( "trig function performance test (CLK_TCK = %.1f ticks/sec)\n", ticks_per_sec ); printf( "requested target of %d to %d seconds per test\n", target_seconds-1, target_seconds+1 ); printf("\nTest seconds seconds from ticks reps/second reps/second\n" ); printf("name real time user+system =total reps (from ticks) (real sec)\n"); perform_test( sin_test, "sine " ); perform_test( cos_test, "cosine " ); perform_test( exp_test, "exp " ); perform_test( log_test, "log " ); perform_test(atan_test, "atan " ); printf("\nThe reps/second calculated with the real time can be\n" ); printf(" much less than the reps/second calculated from ticks\n" ); printf(" due to system load. With lower system load, the times\n" ); printf(" can be much closer to the same.\n" ); exit(0); }