From 166192eb78cdeb41ee25a37e920259fb13f82f6a Mon Sep 17 00:00:00 2001 From: Diana Picus Date: Wed, 9 Jun 2021 09:47:10 +0000 Subject: [PATCH] [flang] Add POSIX implementation for CPU_TIME Add an implementation for CPU_TIME using the POSIX function clock_gettime. I think on most POSIX systems this will be included for free via , which corresponds to "time.h" (YMMV, we can fix the code if the need arises). Detecting that clock_gettime is available is tricky. For instance, commit 827407a86aa07 used the following incantation in f18-parse-demo.cpp: #if _POSIX_C_SOURCE >= 199309L && _POSIX_TIMERS > 0 && _POSIX_CPUTIME && \ defined CLOCK_PROCESS_CPUTIME_ID This doesn't work on my AArch64 Ubuntu system, which provides clock_gettime but doesn't define _POSIX_TIMERS. Since finding the right combination of macros requires infinite time, patience and access to sundry POSIX systems, we should probably try a different approach. This patch attempts to use SFINAE instead of the preprocessor to choose an implementation for CPU_TIME. We define a helper function template which helps us check if clock_gettime is available (and has the interface we expect). I hope the comments explain it well enough. This approach has the advantage that it keeps the detection of clock_gettime close to the code that uses it. An alternative would be to use CMake to check for the symbol (I personally haven't used this before so I don't know if there are any quirks). Differential Revision: https://reviews.llvm.org/D104020 --- flang/runtime/time-intrinsic.cpp | 60 +++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp index 00d51a5b..bd84ef9 100644 --- a/flang/runtime/time-intrinsic.cpp +++ b/flang/runtime/time-intrinsic.cpp @@ -12,14 +12,29 @@ #include -namespace Fortran::runtime { -extern "C" { - // CPU_TIME (Fortran 2018 16.9.57) -double RTNAME(CpuTime)() { - // This is part of the c++ standard, so it should at least exist everywhere. - // It probably does not have the best resolution, so we prefer other - // platform-specific alternatives if they exist. +// We can use std::clock() from the header as a fallback implementation +// that should be available everywhere. This may not provide the best resolution +// and is particularly troublesome on (some?) POSIX systems where CLOCKS_PER_SEC +// is defined as 10^6 regardless of the actual precision of std::clock(). +// Therefore, we will usually prefer platform-specific alternatives when they +// are available. +// +// We can use SFINAE to choose a platform-specific alternative. To do so, we +// introduce a helper function template, whose overload set will contain only +// implementations relying on interfaces which are actually available. Each +// overload will have a dummy parameter whose type indicates whether or not it +// should be preferred. Any other parameters required for SFINAE should have +// default values provided. +namespace { +// Types for the dummy parameter indicating the priority of a given overload. +// We will invoke our helper with an integer literal argument, so the overload +// with the highest priority should have the type int. +using fallback_implementation = double; +using preferred_implementation = int; + +// This is the fallback implementation, which should work everywhere. +template double getCpuTime(fallback_implementation) { std::clock_t timestamp{std::clock()}; if (timestamp != std::clock_t{-1}) { return static_cast(timestamp) / CLOCKS_PER_SEC; @@ -28,5 +43,36 @@ double RTNAME(CpuTime)() { // Return some negative value to represent failure. return -1.0; } + +// POSIX implementation using clock_gettime. This is only enabled if +// clock_gettime is available. +template +double getCpuTime(preferred_implementation, + // We need some dummy parameters to pass to decltype(clock_gettime). + T ClockId = 0, U *Timespec = nullptr, + decltype(clock_gettime(ClockId, Timespec)) *Enabled = nullptr) { +#if defined CLOCK_THREAD_CPUTIME_ID +#define CLOCKID CLOCK_THREAD_CPUTIME_ID +#elif defined CLOCK_PROCESS_CPUTIME_ID +#define CLOCKID CLOCK_PROCESS_CPUTIME_ID +#elif defined CLOCK_MONOTONIC +#define CLOCKID CLOCK_MONOTONIC +#else +#define CLOCKID CLOCK_REALTIME +#endif + struct timespec tspec; + if (clock_gettime(CLOCKID, &tspec) == 0) { + return tspec.tv_nsec * 1.0e-9 + tspec.tv_sec; + } + + // Return some negative value to represent failure. + return -1.0; +} +} // anonymous namespace + +namespace Fortran::runtime { +extern "C" { + +double RTNAME(CpuTime)() { return getCpuTime(0); } } // extern "C" } // namespace Fortran::runtime -- 2.7.4