forked from OSchip/llvm-project
[flang] Add default implementation for SYSTEM_CLOCK
Add an implementation for the runtime functions related to SYSTEM_CLOCK. As with CPU_TIME, this is based on std::clock(), which should be available everywhere, but it is highly recommended to add platform-specific implementations for systems where std::clock() behaves poorly (e.g. POSIX). The documentation for std::clock() doesn't specify a maximum value and in fact wrap around behaviour is non-conforming. Therefore, this implementation of SYSTEM_CLOCK is not guaranteed to wrap around either, and after std::clock reaches its maximum value we will likely just return failure rather than wrap around. If this happens often on your system, please add a new platform-specific implementation. We define COUNT_MAX as either the maximum value that can be stored in a std::clock_t or in a 64-bit integer (whichever is smaller), and COUNT_RATE as CLOCKS_PER_SEC. For POSIX systems, the value of CLOCKS_PER_SEC is hardcoded to 10^6 and irrelevant for the values returned by std::clock. Differential Revision: https://reviews.llvm.org/D105969
This commit is contained in:
parent
284006079e
commit
0104cc85b1
|
@ -13,6 +13,8 @@
|
|||
#include <ctime>
|
||||
|
||||
// CPU_TIME (Fortran 2018 16.9.57)
|
||||
// SYSTEM_CLOCK (Fortran 2018 16.9.168)
|
||||
//
|
||||
// We can use std::clock() from the <ctime> 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
|
||||
|
@ -68,11 +70,64 @@ double GetCpuTime(preferred_implementation,
|
|||
// Return some negative value to represent failure.
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
using count_t =
|
||||
Fortran::runtime::CppTypeFor<Fortran::common::TypeCategory::Integer, 8>;
|
||||
|
||||
// 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.
|
||||
template <typename Unused = void>
|
||||
count_t GetSystemClockCount(fallback_implementation) {
|
||||
std::clock_t timestamp{std::clock()};
|
||||
if (timestamp == static_cast<std::clock_t>(-1)) {
|
||||
// Return -HUGE() to represent failure.
|
||||
return -std::numeric_limits<count_t>::max();
|
||||
}
|
||||
|
||||
// If our return type is large enough to hold any value returned by
|
||||
// std::clock, our work is done. Otherwise, we have to wrap around.
|
||||
static constexpr auto max{std::numeric_limits<count_t>::max()};
|
||||
if constexpr (std::numeric_limits<std::clock_t>::max() <= max) {
|
||||
return static_cast<count_t>(timestamp);
|
||||
} else {
|
||||
// Since std::clock_t could be a floating point type, we can't just use the
|
||||
// % operator, so we have to wrap around manually.
|
||||
return static_cast<count_t>(timestamp - max * std::floor(timestamp / max));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Unused = void>
|
||||
count_t GetSystemClockCountRate(fallback_implementation) {
|
||||
return CLOCKS_PER_SEC;
|
||||
}
|
||||
|
||||
template <typename Unused = void>
|
||||
count_t GetSystemClockCountMax(fallback_implementation) {
|
||||
static constexpr auto max_clock_t = std::numeric_limits<std::clock_t>::max();
|
||||
static constexpr auto max_count_t = std::numeric_limits<count_t>::max();
|
||||
if constexpr (max_clock_t < max_count_t) {
|
||||
return static_cast<count_t>(max_clock_t);
|
||||
} else {
|
||||
return max_count_t;
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace Fortran::runtime {
|
||||
extern "C" {
|
||||
|
||||
double RTNAME(CpuTime)() { return GetCpuTime(0); }
|
||||
|
||||
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCount)() {
|
||||
return GetSystemClockCount(0);
|
||||
}
|
||||
|
||||
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountRate)() {
|
||||
return GetSystemClockCountRate(0);
|
||||
}
|
||||
|
||||
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountMax)() {
|
||||
return GetSystemClockCountMax(0);
|
||||
}
|
||||
} // extern "C"
|
||||
} // namespace Fortran::runtime
|
||||
|
|
|
@ -26,3 +26,33 @@ TEST(TimeIntrinsics, CpuTime) {
|
|||
ASSERT_GE(end, start);
|
||||
}
|
||||
}
|
||||
|
||||
using count_t = CppTypeFor<TypeCategory::Integer, 8>;
|
||||
|
||||
TEST(TimeIntrinsics, SystemClock) {
|
||||
// We can't really test that we get the "right" result for SYSTEM_CLOCK, but
|
||||
// we can have a smoke test to see that we get something reasonable on the
|
||||
// platforms where we expect to support it.
|
||||
|
||||
// The value of the count rate and max will vary by platform, but they should
|
||||
// always be strictly positive if we have a working implementation of
|
||||
// SYSTEM_CLOCK.
|
||||
EXPECT_GT(RTNAME(SystemClockCountRate)(), 0);
|
||||
|
||||
count_t max{RTNAME(SystemClockCountMax)()};
|
||||
EXPECT_GT(max, 0);
|
||||
|
||||
count_t start{RTNAME(SystemClockCount)()};
|
||||
EXPECT_GE(start, 0);
|
||||
EXPECT_LE(start, max);
|
||||
|
||||
// Loop until we get a different value from SystemClockCount. If we don't get
|
||||
// one before we time out, then we should probably look into an implementation
|
||||
// for SystemClokcCount with a better timer resolution on this platform.
|
||||
for (count_t end = start; end == start; end = RTNAME(SystemClockCount)()) {
|
||||
EXPECT_GE(end, 0);
|
||||
EXPECT_LE(end, max);
|
||||
|
||||
EXPECT_GE(end, start);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue