YoushouldhavereceivedcopiesoftheGNUGeneralPublicLicenseandthe GNULesserGeneralPublicLicensealongwiththeGNUMPLibrary.Ifnot,
see https://www.gnu.org/licenses/. */
OnPowerPCthetimebaseregisterscouldbeused,butwouldhavetodo somethingtofindoutthespeed.On6xxchipsit'snormally1/4bus speed,on4xxchipsit'seitherthatoranexternalclock.Measuring
against gettimeofday might be ok. */
#if HAVE_FCNTL_H #include <fcntl.h> /* for open() */ #endif
#if HAVE_STDINT_H #include <stdint.h> /* for uint64_t */ #endif
#if HAVE_UNISTD_H #include <unistd.h> /* for sysconf() */ #endif
#include <sys/types.h>
#if TIME_WITH_SYS_TIME # include <sys/time.h> /* for struct timeval */ # include <time.h> #else # if HAVE_SYS_TIME_H # include <sys/time.h> # else # include <time.h> # endif #endif
#if HAVE_SYS_MMAN_H #include <sys/mman.h> /* for mmap() */ #endif
#if HAVE_SYS_RESOURCE_H #include <sys/resource.h> /* for struct rusage */ #endif
#if HAVE_SYS_SYSSGI_H #include <sys/syssgi.h> /* for syssgi() */ #endif
#if HAVE_SYS_SYSTEMCFG_H #include <sys/systemcfg.h> /* for RTC_POWER on AIX */ #endif
#if HAVE_SYS_TIMES_H #include <sys/times.h> /* for times() and struct tms */ #endif
#include"gmp-impl.h"
#include"speed.h"
/* strerror is only used for some stuff on newish systems, no need to have a
proper replacement */ #if ! HAVE_STRERROR #define strerror(n) "<strerror not available>" #endif
Itcanbeassumedthatifafunctionexiststhenitsdatatypewill,but ifthefunctiondoesn'tthenthedatatypemightormightnotexist,so thetypecan'tbeusedunconditionally.The"struct_rusage"etcmacros
provide dummies when the respective function doesn't exist. */
/* for RTC_POWER format, ie. seconds and nanoseconds */ #define TIMEBASESTRUCT_SECS(t) ((t)->tb_high + (t)->tb_low * 1e-9)
/* Return a string representing a time in seconds, nicely formatted.
Eg. "10.25ms". */ char *
unittime_string (double t)
{ staticchar buf[128];
constchar *unit; int prec;
/* choose units and scale */ if (t < 1e-6)
t *= 1e9, unit = "ns"; elseif (t < 1e-3)
t *= 1e6, unit = "us"; elseif (t < 1.0)
t *= 1e3, unit = "ms"; else
unit = "s";
int
cycles_works_p (void)
{ staticint result = -1;
if (result != -1) goto done;
/* FIXME: On linux, the cycle counter is not saved and restored over *contextswitches,makingitalmostuselessforprecisecputime *measurements.Whenavailable,it'sbettertouseclock_gettime, *whichseemstohavereasonableaccuracy(testedonx86_32, *linux-2.6.26,glibc-2.7).However,therearealsosomelinux *systemswhereclock_gettimeisbrokeninonewayortheother, *likeCLOCK_PROCESS_CPUTIME_IDnotimplemented(easycase)or *kind-ofimplementedbutbroken(needscodetodetectthat),and *onthosesystemsawall-clockcyclecounteristheleastbad *fallback. * *Soweneedsomecodetodisablethecyclecounteronsomebutnot
* all linux systems. */ #ifdef SIGILL
{
RETSIGTYPE (*old_handler) (int); unsigned cycles[2];
old_handler = signal (SIGILL, cycles_works_handler); if (old_handler == SIG_ERR)
{ if (speed_option_verbose)
printf ("cycles_works_p(): SIGILL not supported, assuming speed_cyclecounter() works\n"); goto yes;
} if (setjmp (cycles_works_buf))
{ if (speed_option_verbose)
printf ("cycles_works_p(): SIGILL during speed_cyclecounter(), so doesn't work\n");
result = 0; goto done;
}
speed_cyclecounter (cycles);
signal (SIGILL, old_handler); if (speed_option_verbose)
printf ("cycles_works_p(): speed_cyclecounter() works\n");
} #else
if (speed_option_verbose)
printf ("cycles_works_p(): SIGILL not defined, assuming speed_cyclecounter() works\n"); goto yes; #endif
yes:
result = 1;
done: return result;
}
/* The number of clock ticks per second, but looking at sysconf rather than
just CLK_TCK, where possible. */ long
clk_tck (void)
{ staticlong result = -1L; if (result != -1L) return result;
#if HAVE_SYSCONF
result = sysconf (_SC_CLK_TCK); if (result != -1L)
{ if (speed_option_verbose)
printf ("sysconf(_SC_CLK_TCK) is %ld per second\n", result); return result;
}
fprintf (stderr, "sysconf(_SC_CLK_TCK) not working, using CLK_TCK instead\n"); #endif
#ifdef CLK_TCK
result = CLK_TCK; if (speed_option_verbose)
printf ("CLK_TCK is %ld per second\n", result); return result; #else
fprintf (stderr, "CLK_TCK not defined, cannot continue\n");
abort (); #endif
}
/* If two times can be observed less than half a clock tick apart, then assume"get"ismicrosecondaccurate.
/* Test whether getrusage goes backwards, return non-zero if it does (suggestingit'sflawed).
Onamacintoshm68040-unknown-netbsd1.4.1getrusagelookslikeit's microsecondaccurate,buthasbeenseenremainingunchangedaftermany microsecondshaveelapsed.Italsoregularlygoesbackwardsby1000to 5000usecs,thishasbeenseenafterbetween500and4000attemptstaking perhaps0.03seconds.Weconsiderthistoobrokenforgoodmeasuring. Weusedtohaveconfigurepretendgetrusagedidn'texistonthissystem, butaruntimetestshouldbemorereliable,sinceweimaginetheproblem
is not confined to just this exact system tuple. */
int
getrusage_backwards_p (void)
{ staticint result = -1; struct rusage start, prev, next; long d; int i;
result = 0;
i = 0; for (;;)
{
memcpy (&prev, &next, sizeof (prev));
getrusage (0, &next);
if (next.ru_utime.tv_sec < prev.ru_utime.tv_sec
|| (next.ru_utime.tv_sec == prev.ru_utime.tv_sec
&& next.ru_utime.tv_usec < prev.ru_utime.tv_usec))
{ if (speed_option_verbose)
printf ("getrusage went backwards (attempt %d: %ld.%06ld -> %ld.%06ld)\n",
i,
(long) prev.ru_utime.tv_sec, (long) prev.ru_utime.tv_usec,
(long) next.ru_utime.tv_sec, (long) next.ru_utime.tv_usec);
result = 1; break;
}
/* minimum 1000 attempts, then stop after either 0.1 seconds or 50000
attempts, whichever comes first */
d = 1000000 * (next.ru_utime.tv_sec - start.ru_utime.tv_sec)
+ (next.ru_utime.tv_usec - start.ru_utime.tv_usec);
i++; if (i > 50000 || (i > 1000 && d > 100000)) break;
}
return result;
}
/* CLOCK_PROCESS_CPUTIME_ID looks like it's going to be in a future version ofglibc(sometimepost2.2).
CLOCK_VIRTUALisprocesstime,availableinBSDsystems(thoughsometimes
defined, but returning -1 for an error). */
int
cgt_works_p (void)
{ staticint result = -1;
struct_timespec unit;
if (! have_cgt) return0;
if (! have_cgt_id)
{ if (speed_option_verbose)
printf ("clock_gettime don't know what ID to use\n");
result = 0; return result;
}
if (result != -1) return result;
/* trial run to see if it works */ if (clock_gettime (CGT_ID, &unit) != 0)
{ if (speed_option_verbose)
printf ("clock_gettime id=%d error: %s\n", CGT_ID, strerror (errno));
result = 0; return result;
}
/* get the resolution */ if (clock_getres (CGT_ID, &unit) != 0)
{ if (speed_option_verbose)
printf ("clock_getres id=%d error: %s\n", CGT_ID, strerror (errno));
result = 0; return result;
}
cgt_unittime = unit.tv_sec + unit.tv_nsec * 1e-9; if (speed_option_verbose)
printf ("clock_gettime is %s accurate\n", unittime_string (cgt_unittime));
if (cgt_unittime < 10e-9)
{ /* Do we believe this? */ struct timespec start, end; staticvolatileint counter; double duration; if (clock_gettime (CGT_ID, &start))
{ if (speed_option_verbose)
printf ("clock_gettime id=%d error: %s\n", CGT_ID, strerror (errno));
result = 0; return result;
} /* Loop of at least 1000 memory accesses, ought to take at
least 100 ns*/ for (counter = 0; counter < CGT_DELAY_COUNT; counter++)
; if (clock_gettime (CGT_ID, &end))
{ if (speed_option_verbose)
printf ("clock_gettime id=%d error: %s\n", CGT_ID, strerror (errno));
result = 0; return result;
}
duration = (end.tv_sec + end.tv_nsec * 1e-9
- start.tv_sec - start.tv_nsec * 1e-9); if (speed_option_verbose)
printf ("delay loop of %d rounds took %s (according to clock_gettime)\n",
CGT_DELAY_COUNT, unittime_string (duration)); if (duration < 100e-9)
{ if (speed_option_verbose)
printf ("clock_gettime id=%d not believable\n", CGT_ID);
result = 0; return result;
}
}
result = 1; return result;
}
/* suppress a warning about a[] unused */
a[0] = 0;
if (! have_mftb) return0;
#ifdef SIGILL
old_handler = signal (SIGILL, mftb_works_handler); if (old_handler == SIG_ERR)
{ if (speed_option_verbose)
printf ("mftb_works_p(): SIGILL not supported, assuming mftb works\n"); return1;
} if (setjmp (mftb_works_buf))
{ if (speed_option_verbose)
printf ("mftb_works_p(): SIGILL during mftb, so doesn't work\n"); return0;
}
MFTB (a);
signal (SIGILL, old_handler); if (speed_option_verbose)
printf ("mftb_works_p(): mftb works\n"); #else
if (speed_option_verbose)
printf ("mftb_works_p(): SIGILL not defined, assuming mftb works\n"); #endif
#if ! HAVE_GETTIMEOFDAY if (speed_option_verbose)
printf ("mftb_works_p(): no gettimeofday available to measure mftb\n"); return0; #endif
/* The time base is normally 1/4 of the bus speed on 6xx and 7xx chips, on
other chips it can be driven from an external clock. */
cycletime = freq_measure ("mftb", freq_measure_mftb_one); if (cycletime == -1.0)
{ if (speed_option_verbose)
printf ("mftb_works_p(): cannot measure mftb period\n"); return0;
}
mftb_unittime = cycletime; return1;
}
volatileunsigned *sgi_addr;
int
sgi_works_p (void)
{ #if HAVE_SYSSGI && HAVE_MMAP staticint result = -1;
phys = syssgi (SGI_QUERY_CYCLECNTR, &period_picoseconds); if (phys == (__psunsigned_t) -1)
{ /* ENODEV is the error when a counter is not available */ if (speed_option_verbose)
printf ("syssgi SGI_QUERY_CYCLECNTR error: %s\n", strerror (errno));
result = 0; return result;
}
sgi_unittime = period_picoseconds * 1e-12;
/* IRIX 5 doesn't have SGI_CYCLECNTR_SIZE, assume 32 bits in that case. Challenge/ONYXhardwarehasa64bitbytecounter,butthereseemsno
obvious way to identify that without SGI_CYCLECNTR_SIZE. */ #ifdef SGI_CYCLECNTR_SIZE
size = syssgi (SGI_CYCLECNTR_SIZE); if (size == -1)
{ if (speed_option_verbose)
{
printf ("syssgi SGI_CYCLECNTR_SIZE error: %s\n", strerror (errno));
printf (" will assume size==4\n");
}
size = 32;
} #else
size = 32; #endif
if (size < 32)
{
printf ("syssgi SGI_CYCLECNTR_SIZE gives %d, expected 32 or 64\n", size);
result = 0; return result;
}
/* only used if a supplementary method is chosen below */
cycles_limit = (have_cycles == 1 ? M_2POW32 : M_2POW64) / 2.0
* speed_cycletime;
if (have_grus && getrusage_microseconds_p() && ! getrusage_backwards_p())
{ /* this is a good combination */
use_grus = 1;
supplement_unittime = grus_unittime = 1.0e-6;
strcpy (speed_time_string, "CPU cycle counter, supplemented by microsecond getrusage()");
} elseif (have_cycles == 1)
{ /* When speed_cyclecounter has a limited range, look for something
to supplement it. */ if (have_gtod && gettimeofday_microseconds_p())
{
use_gtod = 1;
supplement_unittime = gtod_unittime = 1.0e-6;
strcpy (speed_time_string, "CPU cycle counter, supplemented by microsecond gettimeofday()");
} elseif (have_grus)
{
use_grus = 1;
supplement_unittime = grus_unittime = 1.0 / (double) clk_tck ();
sprintf (speed_time_string, "CPU cycle counter, supplemented by %s clock tick getrusage()", unittime_string (supplement_unittime));
} elseif (have_times)
{
use_times = 1;
supplement_unittime = times_unittime = 1.0 / (double) clk_tck ();
sprintf (speed_time_string, "CPU cycle counter, supplemented by %s clock tick times()", unittime_string (supplement_unittime));
} elseif (have_gtod)
{
use_gtod = 1;
supplement_unittime = gtod_unittime = 1.0 / (double) clk_tck ();
sprintf (speed_time_string, "CPU cycle counter, supplemented by %s clock tick gettimeofday()", unittime_string (supplement_unittime));
} else
{
fprintf (stderr, "WARNING: cycle counter is 32 bits and there's no other functions.\n");
fprintf (stderr, " Wraparounds may produce bad results on long measurements.\n");
}
}
if (use_grus || use_times || use_gtod)
{ /* must know cycle period to compare cycles to other measuring
(via cycles_limit) */
speed_cycletime_need_seconds ();
/* This is for use after time_base_to_time, ie. for seconds and nanoseconds. */ double
timebasestruct_diff_secs (const timebasestruct_t *end, const timebasestruct_t *start)
{
DIFF_SECS_ROUTINE (tb_high, tb_low, 1e-9);
}
double
speed_endtime (void)
{ #define END_USE(name,value) \ do { \ if (speed_option_verbose >= 3) \
printf ("speed_endtime(): used %s\n", name); \
result = value; \ goto done; \
} while (0)
#define END_ENOUGH(name,value) \ do { \ if (speed_option_verbose >= 3) \
printf ("speed_endtime(): %s gives enough precision\n", name); \
result = value; \ goto done; \
} while (0)
#define END_EXCEED(name,value) \ do { \ if (speed_option_verbose >= 3) \
printf ("speed_endtime(): cycle counter limit exceeded, used %s\n", \
name); \
result = value; \ goto done; \
} while (0)
if (use_grus)
{
t_grus = rusage_diff_secs (&end_grus, &start_grus);
/* Use getrusage() if the cycle counter limit would be exceeded, or if
it provides enough accuracy already. */ if (use_cycles)
{ if (t_grus >= speed_precision*grus_unittime)
END_ENOUGH ("getrusage()", t_grus); if (t_grus >= cycles_limit)
END_EXCEED ("getrusage()", t_grus);
}
}
if (use_times)
{
t_times = (end_times.tms_utime - start_times.tms_utime) * times_unittime;
/* Use times() if the cycle counter limit would be exceeded, or if
it provides enough accuracy already. */ if (use_cycles)
{ if (t_times >= speed_precision*times_unittime)
END_ENOUGH ("times()", t_times); if (t_times >= cycles_limit)
END_EXCEED ("times()", t_times);
}
}
if (use_gtod)
{
t_gtod = timeval_diff_secs (&end_gtod, &start_gtod);
/* Use gettimeofday() if it measured a value bigger than the cycle
counter can handle. */ if (use_cycles)
{ if (t_gtod >= cycles_limit)
END_EXCEED ("gettimeofday()", t_gtod);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.