Skip to content

[Flang][runtime] Distinguish CPU time and elapsed time for cpu_time and system_clock #96652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 2, 2024

Conversation

mjklemm
Copy link
Contributor

@mjklemm mjklemm commented Jun 25, 2024

The current implementation for system_clock() returns the CPU time instead of elapsed wallclock time.

@mjklemm mjklemm self-assigned this Jun 25, 2024
@llvmbot llvmbot added flang:runtime flang Flang issues not falling into any other category labels Jun 25, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 25, 2024

@llvm/pr-subscribers-flang-runtime

Author: Michael Klemm (mjklemm)

Changes

The current implementation for system_clock() returns the CPU time instead of elapsed wallclock time.


Full diff: https://github.com/llvm/llvm-project/pull/96652.diff

1 Files Affected:

  • (modified) flang/runtime/time-intrinsic.cpp (+24-15)
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index a141fe63764a7..2b2c8a3c34e4d 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -64,20 +64,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>
@@ -86,13 +95,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;
@@ -149,15 +158,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) {
+      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)};
@@ -170,7 +179,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,

@jeanPerier
Copy link
Contributor

I am bit worried about the C++ runtime dependency that this could bring if std::chrono::high_resolution_clock is not header only in some C++ standard library. Did check that? Could you add a system_clock test in test/Runtime/no-cpp-dep.c?

Copy link
Member

@ergawy ergawy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one question about the lit test. Thanks @mjklemm.

@kiranchandramohan
Copy link
Contributor

I am bit worried about the C++ runtime dependency that this could bring if std::chrono::high_resolution_clock is not header only in some C++ standard library. Did check that? Could you add a system_clock test in test/Runtime/no-cpp-dep.c?

Yeah, using chrono related stuff has always ended up with some dependency. Most recently it came in as part of Sleep and it was removed in #84911.

mjklemm and others added 4 commits June 27, 2024 14:37
With commit, some functions seem to be very close to each other, so some cleanup might be desired.
After the changes made, we do not rely on std::clock_t anymore, so
we can report a much higher limit for the maximum.
@mjklemm
Copy link
Contributor Author

mjklemm commented Jun 28, 2024

Alrighty then. Let me see if I can remove the std::chrono stuff.

I am bit worried about the C++ runtime dependency that this could bring if std::chrono::high_resolution_clock is not header only in some C++ standard library. Did check that? Could you add a system_clock test in test/Runtime/no-cpp-dep.c?

Yeah, using chrono related stuff has always ended up with some dependency. Most recently it came in as part of Sleep and it was removed in #84911.

We now have two implementations

  • one based on std::timespec_get as the fallback
  • one based on clock_gettime

I have also done some mild refactoring of the code to slightly reduce duplication due to my changes.

What do y'all think?

Copy link
Member

@ergawy ergawy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, verified that this works properly by comparing the timings to omp_get_wtime().

@mjklemm mjklemm requested a review from luporl July 1, 2024 14:42
Copy link
Contributor

@luporl luporl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with this part of the code, but the changes make sense to me and look good.

@mjklemm
Copy link
Contributor Author

mjklemm commented Jul 1, 2024

@rovka @vdonaldson Would you like to have a quick look at this?

@mjklemm mjklemm merged commit 7359edb into llvm:main Jul 2, 2024
6 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jul 2, 2024

LLVM Buildbot has detected a new failure on builder openmp-offload-sles-build-only running on rocm-worker-hw-04-sles while building flang at step 5 "compile-openmp".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/140/builds/1277

Here is the relevant piece of the build log for the reference:

Step 5 (compile-openmp) failure: build (failure)
...
26.462 [903/32/5905] Linking CXX executable bin/llvm-debuginfod
26.464 [902/32/5906] Building CXX object tools/llvm-size/CMakeFiles/llvm-size.dir/llvm-size.cpp.o
26.477 [901/32/5907] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer-driver.cpp.o
26.484 [900/32/5908] Building Opts.inc...
26.507 [899/32/5909] Building CXX object tools/llvm-tli-checker/CMakeFiles/llvm-tli-checker.dir/llvm-tli-checker.cpp.o
26.516 [898/32/5910] Building CXX object tools/llvm-strings/CMakeFiles/llvm-strings.dir/llvm-strings.cpp.o
26.520 [897/32/5911] Linking CXX executable bin/f18-parse-demo
26.534 [896/32/5912] Building CXX object tools/llvm-undname/CMakeFiles/llvm-undname.dir/llvm-undname.cpp.o
26.541 [895/32/5913] Linking CXX executable bin/llvm-modextract
26.547 [894/32/5914] Building CXX object tools/flang/runtime/CMakeFiles/obj.FortranRuntime.dir/time-intrinsic.cpp.o
FAILED: tools/flang/runtime/CMakeFiles/obj.FortranRuntime.dir/time-intrinsic.cpp.o 
ccache /usr/bin/c++ -DFLANG_INCLUDE_TESTS=1 -DFLANG_LITTLE_ENDIAN=1 -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Itools/flang/runtime -I/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime -I/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/include -Itools/flang/include -Iinclude -I/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/llvm/include -isystem /home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/llvm/../mlir/include -isystem tools/mlir/include -isystem tools/clang/include -isystem /home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/llvm/../clang/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wno-comment -Wno-misleading-indentation -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-deprecated-copy -Wno-ctad-maybe-unsupported -fno-strict-aliasing -fno-semantic-interposition -fno-lto -O3 -DNDEBUG   -U_GLIBCXX_ASSERTIONS -U_LIBCPP_ENABLE_ASSERTIONS -UNDEBUG -std=c++1z  -fno-exceptions -funwind-tables -fno-rtti -MD -MT tools/flang/runtime/CMakeFiles/obj.FortranRuntime.dir/time-intrinsic.cpp.o -MF tools/flang/runtime/CMakeFiles/obj.FortranRuntime.dir/time-intrinsic.cpp.o.d -o tools/flang/runtime/CMakeFiles/obj.FortranRuntime.dir/time-intrinsic.cpp.o -c /home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:129:53: error: ‘timespec’ in namespace ‘std’ does not name a type
 count_t ConvertTimeSpecToCount(int kind, const std::timespec &tspec) {
                                                     ^~~~~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp: In function ‘{anonymous}::count_t {anonymous}::ConvertTimeSpecToCount(int, const int&)’:
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:131:60: error: request for member ‘tv_sec’ in ‘tspec’, which is of non-class type ‘const int’
   unsigned_count_t sec{static_cast<unsigned_count_t>(tspec.tv_sec)};
                                                            ^~~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:132:61: error: request for member ‘tv_nsec’ in ‘tspec’, which is of non-class type ‘const int’
   unsigned_count_t nsec{static_cast<unsigned_count_t>(tspec.tv_nsec)};
                                                             ^~~~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp: In function ‘{anonymous}::count_t {anonymous}::GetSystemClockCount(int, {anonymous}::fallback_implementation)’:
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:145:8: error: ‘timespec’ is not a member of ‘std’
   std::timespec tspec;
        ^~~~~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:145:8: note: suggested alternative: ‘time_put’
   std::timespec tspec;
        ^~~~~~~~
        time_put
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:147:12: error: ‘timespec_get’ is not a member of ‘std’
   if (std::timespec_get(&tspec, TIME_UTC) < 0) {
            ^~~~~~~~~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:147:12: note: suggested alternative: ‘time_put’
   if (std::timespec_get(&tspec, TIME_UTC) < 0) {
            ^~~~~~~~~~~~
            time_put
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:147:26: error: ‘tspec’ was not declared in this scope
   if (std::timespec_get(&tspec, TIME_UTC) < 0) {
                          ^~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:154:39: error: ‘tspec’ was not declared in this scope
   return ConvertTimeSpecToCount(kind, tspec);
                                       ^~~~~
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp: In function ‘{anonymous}::count_t {anonymous}::GetSystemClockCount(int, {anonymous}::preferred_implementation, T, U*, decltype (clock_gettime(ClockId, Timespec))*)’:
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:182:44: error: invalid initialization of reference of type ‘const int&’ from expression of type ‘timespec’
   return ConvertTimeSpecToCount(kind, tspec);
                                            ^
/home/botworker/bbot/builds/openmp-offload-sles-build/llvm.src/flang/runtime/time-intrinsic.cpp:129:9: note: in passing argument 2 of ‘{anonymous}::count_t {anonymous}::ConvertTimeSpecToCount(int, const int&)’
 count_t ConvertTimeSpecToCount(int kind, const std::timespec &tspec) {

mjklemm added a commit to mjklemm/llvm-project that referenced this pull request Jul 2, 2024
mjklemm added a commit to mjklemm/llvm-project that referenced this pull request Jul 2, 2024
lravenclaw pushed a commit to lravenclaw/llvm-project that referenced this pull request Jul 3, 2024
…nd system_clock (llvm#96652)

The current implementation for `system_clock()` returns the CPU time
instead of elapsed wallclock time. This PR fixes the issue and makes
`system_clock()` correctly return elapsed time.
lravenclaw pushed a commit to lravenclaw/llvm-project that referenced this pull request Jul 3, 2024
kbluck pushed a commit to kbluck/llvm-project that referenced this pull request Jul 6, 2024
…nd system_clock (llvm#96652)

The current implementation for `system_clock()` returns the CPU time
instead of elapsed wallclock time. This PR fixes the issue and makes
`system_clock()` correctly return elapsed time.
kbluck pushed a commit to kbluck/llvm-project that referenced this pull request Jul 6, 2024
@DanielCChen
Copy link
Contributor

DanielCChen commented Jul 15, 2024

Alrighty then. Let me see if I can remove the std::chrono stuff.

I am bit worried about the C++ runtime dependency that this could bring if std::chrono::high_resolution_clock is not header only in some C++ standard library. Did check that? Could you add a system_clock test in test/Runtime/no-cpp-dep.c?

Yeah, using chrono related stuff has always ended up with some dependency. Most recently it came in as part of Sleep and it was removed in #84911.

We now have two implementations

  • one based on std::timespec_get as the fallback
  • one based on clock_gettime

I have also done some mild refactoring of the code to slightly reduce duplication due to my changes.

What do y'all think?

Is there a particular reason to go in favor of std::timespec_get over POSIX function clock_gettime?
I posted PR #98915 to fix the build breakage on AIX because of it. @mjklemm

@mjklemm
Copy link
Contributor Author

mjklemm commented Jul 15, 2024

Is there a particular reason to go in favor of std::timespec_get over POSIX function clock_gettime?
I posted PR #98915 to fix the build breakage on AIX because of it. @mjklemm

clock_gettime is still there. Before this PR, the code used the wrong entry point and did not return time, but returned CPU time, which was not correct for what the Fortran intrinsic was supposed to return. I'll comment in your PR also.

@DanielCChen
Copy link
Contributor

Thanks for the explanation. The reason of Flang build failure on AIX is because macro TIME_UTC does not exist on AIX 7.2. The build failed at the compile time with "undefined symbol". The code that references TIME_UTC is not "guarded".

@oerdnj oerdnj mentioned this pull request Aug 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:runtime flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants