Skip to content
59 changes: 31 additions & 28 deletions flang/runtime/time-intrinsic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <chrono>
#ifdef _WIN32
#include "flang/Common/windows-include.h"
#else
Expand Down Expand Up @@ -64,20 +65,29 @@ template <typename Unused = void> double GetCpuTime(fallback_implementation) {
// clock_gettime is implemented in the pthread library for MinGW.
// Using it here would mean that all programs that link libFortranRuntime are
// required to also link to pthread. Instead, don't use the function.
#undef CLOCKID
#elif defined CLOCK_PROCESS_CPUTIME_ID
#define CLOCKID CLOCK_PROCESS_CPUTIME_ID
#undef CLOCKID_CPU_TIME
#undef CLOCKID_ELAPSED_TIME
#else
// Determine what clock to use for CPU time.
#if defined CLOCK_PROCESS_CPUTIME_ID
#define CLOCKID_CPU_TIME CLOCK_PROCESS_CPUTIME_ID
#elif defined CLOCK_THREAD_CPUTIME_ID
#define CLOCKID CLOCK_THREAD_CPUTIME_ID
#elif defined CLOCK_MONOTONIC
#define CLOCKID CLOCK_MONOTONIC
#define CLOCKID_CPU_TIME CLOCK_THREAD_CPUTIME_ID
#else
#undef CLOCKID_CPU_TIME
#endif

// Determine what clock to use for elapsed time.
#if defined CLOCK_MONOTONIC
#define CLOCKID_ELAPSED_TIME CLOCK_MONOTONIC
#elif defined CLOCK_REALTIME
#define CLOCKID CLOCK_REALTIME
#define CLOCKID_ELAPSED_TIME CLOCK_REALTIME
#else
#undef CLOCKID
#undef CLOCKID_ELAPSED_TIME
#endif
#endif

#ifdef CLOCKID
#ifdef CLOCKID_CPU_TIME
// POSIX implementation using clock_gettime. This is only enabled where
// clock_gettime is available.
template <typename T = int, typename U = struct timespec>
Expand All @@ -86,13 +96,13 @@ double GetCpuTime(preferred_implementation,
T ClockId = 0, U *Timespec = nullptr,
decltype(clock_gettime(ClockId, Timespec)) *Enabled = nullptr) {
struct timespec tspec;
if (clock_gettime(CLOCKID, &tspec) == 0) {
if (clock_gettime(CLOCKID_CPU_TIME, &tspec) == 0) {
return tspec.tv_nsec * 1.0e-9 + tspec.tv_sec;
}
// Return some negative value to represent failure.
return -1.0;
}
#endif
#endif // CLOCKID_CPU_TIME

using count_t = std::int64_t;
using unsigned_count_t = std::uint64_t;
Expand All @@ -105,31 +115,24 @@ static constexpr inline unsigned_count_t GetHUGE(int kind) {
return (unsigned_count_t{1} << ((8 * kind) - 1)) - 1;
}

// This is the fallback implementation, which should work everywhere. Note that
// in general we can't recover after std::clock has reached its maximum value.
// This is the fallback implementation, which should work everywhere.
template <typename Unused = void>
count_t GetSystemClockCount(int kind, fallback_implementation) {
std::clock_t timestamp{std::clock()};
if (timestamp == static_cast<std::clock_t>(-1)) {
unsigned_count_t timestamp;
timestamp =
std::chrono::high_resolution_clock::now().time_since_epoch().count();
if (timestamp == static_cast<unsigned_count_t>(-1)) {
// Return -HUGE(COUNT) to represent failure.
return -static_cast<count_t>(GetHUGE(kind));
}
// Convert the timestamp to std::uint64_t with wrap-around. The timestamp is
// most likely a floating-point value (since C'11), so compute the modulus
// carefully when one is required.
constexpr auto maxUnsignedCount{std::numeric_limits<unsigned_count_t>::max()};
if constexpr (std::numeric_limits<std::clock_t>::max() > maxUnsignedCount) {
timestamp -= maxUnsignedCount * std::floor(timestamp / maxUnsignedCount);
}
unsigned_count_t unsignedCount{static_cast<unsigned_count_t>(timestamp)};
// Return the modulus of the unsigned integral count with HUGE(COUNT)+1.
// The result is a signed integer but never negative.
return static_cast<count_t>(unsignedCount % (GetHUGE(kind) + 1));
return static_cast<count_t>(timestamp % (GetHUGE(kind) + 1));
}

template <typename Unused = void>
count_t GetSystemClockCountRate(int kind, fallback_implementation) {
return CLOCKS_PER_SEC;
return std::chrono::high_resolution_clock::period::den;
}

template <typename Unused = void>
Expand All @@ -149,15 +152,15 @@ constexpr unsigned_count_t DS_PER_SEC{10u};
constexpr unsigned_count_t MS_PER_SEC{1'000u};
constexpr unsigned_count_t NS_PER_SEC{1'000'000'000u};

#ifdef CLOCKID
#ifdef CLOCKID_ELAPSED_TIME
template <typename T = int, typename U = struct timespec>
count_t GetSystemClockCount(int kind, 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) {
struct timespec tspec;
const unsigned_count_t huge{GetHUGE(kind)};
if (clock_gettime(CLOCKID, &tspec) != 0) {
if (clock_gettime(CLOCKID_ELAPSED_TIME, &tspec) != 0) {
return -huge; // failure
}
unsigned_count_t sec{static_cast<unsigned_count_t>(tspec.tv_sec)};
Expand All @@ -170,7 +173,7 @@ count_t GetSystemClockCount(int kind, preferred_implementation,
return (sec * DS_PER_SEC + (nsec / (NS_PER_SEC / DS_PER_SEC))) % (huge + 1);
}
}
#endif
#endif // CLOCKID_ELAPSED_TIME

template <typename T = int, typename U = struct timespec>
count_t GetSystemClockCountRate(int kind, preferred_implementation,
Expand Down
2 changes: 2 additions & 0 deletions flang/test/Runtime/no-cpp-dep.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ int32_t RTNAME(ArgumentCount)();
int32_t RTNAME(GetCommandArgument)(int32_t, const struct Descriptor *,
const struct Descriptor *, const struct Descriptor *);
int32_t RTNAME(GetEnvVariable)();
int64_t RTNAME(SystemClockCount)(int kind);

int main() {
double x = RTNAME(CpuTime)();
RTNAME(ProgramStart)(0, 0, 0, 0);
int32_t c = RTNAME(ArgumentCount)();
int32_t v = RTNAME(GetCommandArgument)(0, 0, 0, 0);
int32_t e = RTNAME(GetEnvVariable)("FOO", 0, 0);
int64_t t = RTNAME(SystemClockCount)(8);
return x + c + v + e;
}
27 changes: 27 additions & 0 deletions flang/test/Runtime/system_clock.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
! RUN: %flang -o %t %s
! RUN: %t

program system_clock_test
use iso_fortran_env, only: int64, real64
implicit none

integer, parameter :: delta = 1
real(kind=real64), parameter :: epsilon = 0.001

integer(kind=int64) :: t_start, t_end
integer(kind=int64) :: rate
real(kind=real64) :: diff

call system_clock(count_rate=rate)

call system_clock(t_start)
call sleep(delta)
call system_clock(t_end)

diff = real(t_end - t_start, kind=real64) / real(rate, kind=real64)

if (abs(diff - real(delta, kind=real64)) <= epsilon) then
stop 0, quiet=.true.
end if
stop 1, quiet=.true.
end program system_clock_test