1 //===-- runtime/time-intrinsic.cpp ----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // Implements time-related intrinsic subroutines. 10 11 #include "time-intrinsic.h" 12 13 #include <ctime> 14 15 // CPU_TIME (Fortran 2018 16.9.57) 16 // We can use std::clock() from the <ctime> header as a fallback implementation 17 // that should be available everywhere. This may not provide the best resolution 18 // and is particularly troublesome on (some?) POSIX systems where CLOCKS_PER_SEC 19 // is defined as 10^6 regardless of the actual precision of std::clock(). 20 // Therefore, we will usually prefer platform-specific alternatives when they 21 // are available. 22 // 23 // We can use SFINAE to choose a platform-specific alternative. To do so, we 24 // introduce a helper function template, whose overload set will contain only 25 // implementations relying on interfaces which are actually available. Each 26 // overload will have a dummy parameter whose type indicates whether or not it 27 // should be preferred. Any other parameters required for SFINAE should have 28 // default values provided. 29 namespace { 30 // Types for the dummy parameter indicating the priority of a given overload. 31 // We will invoke our helper with an integer literal argument, so the overload 32 // with the highest priority should have the type int. 33 using fallback_implementation = double; 34 using preferred_implementation = int; 35 36 // This is the fallback implementation, which should work everywhere. 37 template <typename Unused = void> double getCpuTime(fallback_implementation) { 38 std::clock_t timestamp{std::clock()}; 39 if (timestamp != std::clock_t{-1}) { 40 return static_cast<double>(timestamp) / CLOCKS_PER_SEC; 41 } 42 43 // Return some negative value to represent failure. 44 return -1.0; 45 } 46 47 // POSIX implementation using clock_gettime. This is only enabled if 48 // clock_gettime is available. 49 template <typename T = int, typename U = struct timespec> 50 double getCpuTime(preferred_implementation, 51 // We need some dummy parameters to pass to decltype(clock_gettime). 52 T ClockId = 0, U *Timespec = nullptr, 53 decltype(clock_gettime(ClockId, Timespec)) *Enabled = nullptr) { 54 #if defined CLOCK_THREAD_CPUTIME_ID 55 #define CLOCKID CLOCK_THREAD_CPUTIME_ID 56 #elif defined CLOCK_PROCESS_CPUTIME_ID 57 #define CLOCKID CLOCK_PROCESS_CPUTIME_ID 58 #elif defined CLOCK_MONOTONIC 59 #define CLOCKID CLOCK_MONOTONIC 60 #else 61 #define CLOCKID CLOCK_REALTIME 62 #endif 63 struct timespec tspec; 64 if (clock_gettime(CLOCKID, &tspec) == 0) { 65 return tspec.tv_nsec * 1.0e-9 + tspec.tv_sec; 66 } 67 68 // Return some negative value to represent failure. 69 return -1.0; 70 } 71 } // anonymous namespace 72 73 namespace Fortran::runtime { 74 extern "C" { 75 76 double RTNAME(CpuTime)() { return getCpuTime(0); } 77 } // extern "C" 78 } // namespace Fortran::runtime 79