From 1265ede3844fa08b3e938318edbf482651e4a299 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Mon, 15 May 2023 11:44:17 -0500 Subject: [PATCH] Add precise_time.c to misc to help speed up Windows --- lib/Makefile.am | 4 +- lib/precise_time.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 lib/precise_time.c diff --git a/lib/Makefile.am b/lib/Makefile.am index 391cb2279..ccfac19b0 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,7 @@ -EXTRA_DIST = getopt.c getopt.h getopt_long.c usleep.c \ +EXTRA_DIST = getopt.c getopt.h getopt_long.c usleep.c \ termios.c win32termios.h gettimeofday.c getaddrinfo.c noinst_LTLIBRARIES = libmisc.la -libmisc_la_SOURCES = cJSON.c cJSON.h asyncpipe.c asyncpipe.h +libmisc_la_SOURCES = cJSON.c cJSON.h asyncpipe.c asyncpipe.h precise_time.c libmisc_la_LIBADD = $(LTLIBOBJS) $(NET_LIBS) diff --git a/lib/precise_time.c b/lib/precise_time.c new file mode 100644 index 000000000..c4e0ddb89 --- /dev/null +++ b/lib/precise_time.c @@ -0,0 +1,164 @@ +// --------------------------------------------------------------------- +// precise_time.cxx +// +// Copyright (C) 2023 +// Dave Freese, W1HKJ +// +// This file is part of flrig +// +// flrig is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// flrig is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with fldigi. If not, see . +// --------------------------------------------------------------------- + +#include +#include +#include + +#if 0 +// return current tick time in seconds +double monotonic_seconds() +{ + static struct timeval t1; + gettimeofday(&t1, NULL); + return t1.tv_sec + t1.tv_usec / 1e6; +} + +#else + +//====================================================================== +// +// A cross platform monotonic timer. +// Copyright 2013 Alex Reece. +// + +#include + +#define NANOS_PER_SECF 1000000000.0 +#define USECS_PER_SEC 1000000 + +static int showme = 0; + +#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) + // If we have it, use clock_gettime and CLOCK_MONOTONIC. + + #include + + double monotonic_seconds() { + if (showme) { + showme = 0; + } + struct timespec time; + // Note: Make sure to link with -lrt to define clock_gettime. + clock_gettime(CLOCK_MONOTONIC, &time); + return ((double) time.tv_sec) + ((double) time.tv_nsec / (NANOS_PER_SECF)); + } + +#elif defined(__APPLE__) + // If we don't have CLOCK_MONOTONIC, we might be on a Mac. There we instead + // use mach_absolute_time(). + + #include + + static mach_timebase_info_data_t info; + static void __attribute__((constructor)) init_info() { + mach_timebase_info(&info); + } + + double monotonic_seconds() { + uint64_t time = mach_absolute_time(); + double dtime = (double) time; + dtime *= (double) info.numer; + dtime /= (double) info.denom; + return dtime / NANOS_PER_SECF; + } + +#elif defined(__WIN32__) + // On Windows, use QueryPerformanceCounter and QueryPerformanceFrequency. + + #include + + static double PCFreq = 0.0; + + // According to http://stackoverflow.com/q/1113409/447288, this will + // make this function a constructor. + // TODO(awreece) Actually attempt to compile on windows. + // w1hkj - builds OK on mingw32 + + static void __cdecl init_pcfreq(); + __declspec(allocate(".CRT$XCU")) void (__cdecl*init_pcfreq_)() = init_pcfreq; + static void __cdecl init_pcfreq() { + // Accoring to http://stackoverflow.com/a/1739265/447288, this will + // properly initialize the QueryPerformanceCounter. + LARGE_INTEGER li; + int has_qpc = QueryPerformanceFrequency(&li); + assert(has_qpc); + + PCFreq = ((double) li.QuadPart) / 1000.0; + } + + double monotonic_seconds() { + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return ((double) li.QuadPart) / PCFreq; + } + +#else + // Fall back to rdtsc. The reason we don't use clock() is this scary message + // from the man page: + // "On several other implementations, the value returned by clock() also + // includes the times of any children whose status has been collected via + // wait(2) (or another wait-type call)." + // + // Also, clock() only has microsecond accuracy. + // + // This whitepaper offered excellent advice on how to use rdtscp for + // profiling: http://download.intel.com/embedded/software/IA/324264.pdf + // + // Unfortunately, we can't follow its advice exactly with our semantics, + // so we're just going to use rdtscp with cpuid. + // + // Note that rdtscp will only be available on new processors. + + #include + + static inline uint64_t rdtsc() { + uint32_t hi, lo; + asm volatile("rdtscp\n" + "movl %%edx, %0\n" + "movl %%eax, %1\n" + "cpuid" + : "=r" (hi), "=r" (lo) : : "%rax", "%rbx", "%rcx", "%rdx"); + return (((uint64_t)hi) << 32) | (uint64_t)lo; + } + + static uint64_t rdtsc_per_sec = 0; + static void __attribute__((constructor)) init_rdtsc_per_sec() { + uint64_t before, after; + + before = rdtsc(); + usleep(USECS_PER_SEC); + after = rdtsc(); + + rdtsc_per_sec = after - before; + } + + double monotonic_seconds() { + if (showme) { + showme = false; + } + return (double) rdtsc() / (double) rdtsc_per_sec; + } + +#endif + +#endif