Skip to content

Commit dd2f8d2

Browse files
committed
workaround LLVMFLang 64-bit timer bug
1 parent 1f77782 commit dd2f8d2

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

CMakePresets.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
"name": "multi", "inherits": "default",
1515
"displayName": "Ninja Multi-Config",
1616
"generator": "Ninja Multi-Config"
17+
},
18+
{
19+
"name": "clang_gfortran", "inherits": "default",
20+
"displayName": "Clang + GFortran",
21+
"description": "Use Clang for C/C++ and GFortran for Fortran",
22+
"cacheVariables": {
23+
"CMAKE_C_COMPILER": "clang",
24+
"CMAKE_CXX_COMPILER": "clang++",
25+
"CMAKE_Fortran_COMPILER": "gfortran"
26+
}
1727
}
1828
],
1929
"buildPresets": [

test/sleep/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
set_property(DIRECTORY PROPERTY LABELS sleep)
22

3+
add_executable(fortran_sleep ticks.f90)
4+
add_test(NAME Fortran_sleep COMMAND fortran_sleep)
5+
6+
if(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang")
7+
set(bad_ticks true)
8+
else()
9+
set(bad_ticks false)
10+
endif()
11+
12+
set_property(TEST Fortran_sleep PROPERTY DISABLED ${bad_ticks})
13+
314
add_library(f_sleep OBJECT ${PROJECT_SOURCE_DIR}/src/sleep/sleep_std.f90)
415

516
# not OBJECT so include propagates
@@ -25,8 +36,10 @@ if(WIN32)
2536
add_library(f_win_sleep OBJECT ${PROJECT_SOURCE_DIR}/src/sleep/reference/windows.f90)
2637
set_property(TARGET f_win_sleep PROPERTY Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/windows)
2738
target_include_directories(f_win_sleep PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include/windows)
39+
2840
add_executable(fortran_win_sleep test_sleep.f90)
2941
target_link_libraries(fortran_win_sleep PRIVATE f_win_sleep)
42+
3043
add_test(NAME Fortran_win_sleep COMMAND fortran_win_sleep)
3144

3245
endif()
@@ -36,15 +49,20 @@ if(NOT MSVC)
3649
add_library(f_micro_sleep OBJECT ${PROJECT_SOURCE_DIR}/src/sleep/reference/posix_usleep.f90)
3750
set_property(TARGET f_micro_sleep PROPERTY Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/posix_usleep)
3851
target_include_directories(f_micro_sleep PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include/posix_usleep)
52+
3953
add_executable(fortran_micro_sleep test_sleep.f90)
4054
target_link_libraries(fortran_micro_sleep PRIVATE f_micro_sleep)
55+
4156
add_test(NAME Fortran_micro_sleep COMMAND fortran_micro_sleep)
4257

58+
4359
add_library(f_nano_sleep OBJECT ${PROJECT_SOURCE_DIR}/src/sleep/reference/posix_nanosleep.f90)
4460
set_property(TARGET f_nano_sleep PROPERTY Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/posix_nanosleep)
4561
target_include_directories(f_nano_sleep PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include/posix_nanosleep)
62+
4663
add_executable(fortran_nano_sleep test_sleep.f90)
4764
target_link_libraries(fortran_nano_sleep PRIVATE f_nano_sleep)
65+
4866
add_test(NAME Fortran_nano_sleep COMMAND fortran_nano_sleep)
4967
set_property(TEST Fortran_nano_sleep PROPERTY DISABLED true)
5068
# this test is shaky. Fails on MinGW and macOS Apple Silicon. Best to use an auxiliary C function to interface with C struct.

test/sleep/test_sleep.f90

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,58 @@
11
program sleep_demo
2+
!! we relax this example to use 32-bit timers as Flang 20 currently has a bug with 64-bit timers where the tick rate is reported as 1/1000th the actual rate.
23

3-
use, intrinsic :: iso_fortran_env, only : int64, stderr => error_unit
4+
use, intrinsic :: iso_fortran_env, only : stderr => error_unit, compiler_version
45

56
use sleep_std, only : sleep_ms
67

78
implicit none
89

9-
integer :: ierr, millisec
10-
character(8) :: argv
11-
integer(int64) :: tic, toc, trate
10+
11+
integer :: ierr, millisec, L
12+
character(:), allocatable :: argv
13+
integer :: tic, toc, trate, ticks
1214
real :: t_ms
1315

1416
call system_clock(count_rate=trate)
1517

18+
if (trate <= 0) error stop "system_clock count_rate indicates no clock available"
19+
print '(a, i0)', 'CPU tick rate (ticks/sec): ', trate
20+
1621
millisec = 500
1722
if(command_argument_count() > 0) then
23+
24+
call get_command_argument(1, length=L)
25+
allocate(character(L) :: argv)
1826
call get_command_argument(1, argv, status=ierr)
1927
if (ierr /= 0) error stop "please specify milliseconds to sleep"
20-
read(argv, '(i6)') millisec
28+
read(argv, '(i5)') millisec
2129
end if
2230

2331
if (millisec <= 0) error stop "please specify positive milliseconds to sleep"
32+
if (millisec > 10000) error stop "please specify milliseconds to sleep less than 10000"
33+
34+
print '(a, i0)', 'sleeping for (ms): ', millisec
2435

2536
call system_clock(count=tic)
2637
call sleep_ms(millisec)
2738
call system_clock(count=toc)
2839

29-
t_ms = real(toc-tic) * 1000. / real(trate)
40+
ticks = toc-tic
41+
t_ms = 1000 * real(ticks) / real(trate)
42+
43+
print '(a, i0)', 'ticks slept: ', ticks
3044

3145
if (t_ms < 0.5 * millisec) then
3246
!> slept less than half expected time
33-
write(stderr, '(a, f9.6)') 'ERROR: measured sleep time too short (millisec): ', t_ms
47+
write(stderr, *) 'ERROR: measured sleep time too short (millisec): ', t_ms
3448
error stop
3549
end if
3650
if (t_ms > 2 * millisec) then
3751
!> slept more than twice expected time
38-
write(stderr, '(a, f9.6)') 'ERROR: measure sleep time too long (millisec): ', t_ms
52+
write(stderr, *) 'ERROR: measure sleep time too long (millisec): ', t_ms
3953
error stop
4054
end if
4155

42-
print '(A, F6.1)', 'OK: test_sleep: slept for (ms): ', t_ms
56+
print '(A, F7.1)', 'OK: ' // compiler_version() // ' slept for (ms): ', t_ms
4357

4458
end program

test/sleep/ticks.f90

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
program ticks
2+
3+
use, intrinsic :: iso_fortran_env, only : int32, int64, error_unit
4+
5+
implicit none
6+
7+
integer(int32) :: trate32, tic32, toc32, ticks32
8+
integer(int64) :: trate, tic, toc, ticks64
9+
logical :: ok = .true.
10+
11+
call system_clock(count_rate=trate)
12+
call system_clock(count_rate=trate32)
13+
14+
if (trate <= 0) error stop "system_clock count_rate indicates no clock available"
15+
print '(a, i0)', 'CPU 64-bit tick rate (ticks/sec): ', trate
16+
print '(a, i0)', 'CPU 32-bit tick rate (ticks/sec): ', trate32
17+
18+
call system_clock(count=tic)
19+
call system_clock(count=tic32)
20+
21+
call sleep(1)
22+
! this is non-standard, but we are interested in tick counts
23+
24+
call system_clock(count=toc)
25+
call system_clock(count=toc32)
26+
27+
ticks64 = toc - tic
28+
ticks32 = toc32 - tic32
29+
print '(a,i0,a,i0)', 'CPU ticks: 64-bit ', ticks64, ' 32-bit ', ticks32
30+
31+
if (real(ticks64) < real(trate) * 0.95 .or. real(ticks64) > real(trate) * 1.05) then
32+
ok = .false.
33+
write(error_unit, *) 'ERROR: 64-bit ticks: expected about ', trate, ' but got ', ticks64
34+
end if
35+
36+
if (real(ticks32) < real(trate32) * 0.95 .or. real(ticks32) > real(trate32) * 1.05) then
37+
ok = .false.
38+
write(error_unit, *) 'ERROR: 32-bit ticks: expected about ', trate32, ' but got ', ticks32
39+
end if
40+
41+
if (.not. ok) error stop "tick rate test failed"
42+
43+
print *, 'OK: ticks: tick rates look good'
44+
45+
end program

0 commit comments

Comments
 (0)