FMS  2025.04
Flexible Modeling System
fms_affinity.F90
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 
19 !> @defgroup fms_affinity_mod fms_affinity_mod
20 !> @ingroup affinity
21 !> @brief Fortran API interfaces to set the thread affinity.
22 !! API interfaces to allow setting and getting thread affinity. The routines @ref get_cpuset
23 !! , @ref set_cpu_affinity , and @ref fms_affinity_get are defined via C routines in affinity.c.
24 !!
25 !! @author Rusty Benson
26 
27 !> @addtogroup fms_affinity_mod
28 !> @{
29 module fms_affinity_mod
30  !--- standard system modules
31  use, intrinsic :: iso_c_binding, only: c_int, c_bool
32  use omp_lib
33 
34  !--- FMS modules
35  use mpp_mod, only: input_nml_file, mpp_pe, stdlog
36  use fms_mod, only: fms_init, check_nml_error, write_version_number, &
37  error_mesg, fatal, note
38 
39  !--- default scoping
40  implicit none
41  private
42 
43  interface
44 
45  !> Interface to get affinity from the current component.
46  !!
47  !> Defined in @ref affinity.c.
48  integer(KIND=c_int) function fms_affinity_get() bind(c, name="get_cpu_affinity")
49  import c_int
50  end function fms_affinity_get
51 
52  !> Private interface to retrieve this groups CPU set and it's size.
53  !!
54  !> Defined in @ref affinity.c.
55  integer(KIND=c_int) function get_cpuset(fsz, output, pe, debug) bind(c, name="get_cpuset")
56  import c_int, c_bool
57  integer(KIND=c_int), value, intent(in) :: fsz, pe
58  integer(KIND=c_int), dimension(*), intent(inout) :: output
59  logical(KIND=c_bool), value :: debug
60  end function get_cpuset
61 
62  !> Private interface to set CPU afinity to a given core.
63  !!
64  !> Defined in @ref affinity.c.
65  integer(KIND=c_int) function set_cpu_affinity(cpu) bind(c, name="set_cpu_affinity")
66  import c_int
67  integer(KIND=c_int), value, intent(in) :: cpu
68  end function set_cpu_affinity
69  end interface
70 
71  !--- namelist parameters
72  logical:: affinity = .true.
73  logical:: strict = .true.
74  logical:: debug_affinity = .false.
75  logical(c_bool):: debug_cpuset = .false.
76  namelist /fms_affinity_nml/ affinity, strict, debug_affinity, debug_cpuset
77 
79 
80  !---- version number
81  ! Include variable "version" to be written to log file.
82 #include <file_version.h>
83 
84  logical :: module_is_initialized = .false.
85 
86 contains
87 
88  !> Initialization routine for affinity handling
89  subroutine fms_affinity_init()
90  !--- local variables
91  integer:: io_stat
92  integer:: ierr
93  integer:: iunit
94 
95  !--- return if module is initialized
96  if (module_is_initialized) return
97 
98  !--- ensure fms/mpp has been initialized
99  call fms_init()
100 
101  !--- read in namelist
102  read(input_nml_file, fms_affinity_nml, iostat=io_stat)
103  ierr = check_nml_error(io_stat,'fms_affinity_nml')
104 
105  !--- output information to logfile
106  call write_version_number("fms_affinity_mod", version)
107  iunit = stdlog()
108  write(iunit,nml=fms_affinity_nml)
109 
110  module_is_initialized = .true.
111 
112  end subroutine fms_affinity_init
113 
114 
115  !> Routine to set affinity for a component
116  subroutine fms_affinity_set (component, use_hyper_thread, nthreads)
117  !--- interface variables
118  character(len=*), intent(in):: component !< Component name
119  logical, intent(in):: use_hyper_thread !< .TRUE. if using hyperthreads
120  integer, intent(in):: nthreads !< Number of threads
121 
122  !--- local declarations for Fortran/C affinity interoperability
123  integer(c_int):: cpuset_sz
124  integer(c_int), dimension(:), allocatable:: cpu_set
125  integer(c_int):: retcode
126 
127  !--- local variables
128  character(len=32):: h_name
129  integer:: msg_type
130  integer:: th_num
131  integer:: indx
132 
133  if (.not. module_is_initialized) call fms_affinity_init()
134  if (.not. affinity) return
135 
136  if (strict) then
137  msg_type = fatal
138  else
139  msg_type = note
140  endif
141 
142  h_name = 'generic '
143 
144  !--- allocate storage for cpuset
145  if (use_hyper_thread) then
146  cpuset_sz = nthreads
147  else
148  cpuset_sz = nthreads * 2
149  endif
150  allocate (cpu_set(0:cpuset_sz-1))
151 
152  !--- get cpuset for this MPI-rank
153  retcode = get_cpuset(cpuset_sz, cpu_set, mpp_pe(), debug_cpuset)
154  if (retcode == -1) then
155  call error_mesg('fms_affinity_set',trim(component)//' cpu_set size > allocated storage',fatal)
156  elseif ( (retcode == cpuset_sz/2) .and. (retcode == nthreads) ) then
157  call error_mesg('fms_affinity_set',trim(component)//' affinity assumes hyper-threading hardware disabled',note)
158  elseif (retcode < cpuset_sz) then
159  call error_mesg('fms_affinity_set',trim(component)//' cpu_set size smaller than expected',msg_type)
160  endif
161 
162  !--- set the affinity for the MPI-rank
163  retcode = set_cpu_affinity(cpu_set(0))
164  if (retcode == -1) then
165  call error_mesg('fms_affinity_set',trim(component)//': issue setting cpu affinity', fatal)
166  endif
167 
168  !--- set affinity for threads associated with this MPI-rank
169 !$OMP PARALLEL NUM_THREADS (nthreads) &
170 !$OMP& DEFAULT (none) &
171 !$OMP& SHARED (use_hyper_thread, cpuset_sz, component, cpu_set, debug_affinity) &
172 !$OMP& PRIVATE (th_num, indx, retcode, h_name)
173 !$ th_num = omp_get_thread_num()
174  !--- handle hyper threading case by alternating threads between logical and virtual cores
175 !$ if (use_hyper_thread) then
176 !$ if (mod(th_num,2) == 0 ) then
177 !$ indx = th_num/2
178 !$ else
179 !$ indx = (cpuset_sz - 1) - ((cpuset_sz - 1) - th_num)/2
180 !$ endif
181 !$ else
182 !$ indx = th_num
183 !$ endif
184 !$ retcode = set_cpu_affinity(cpu_set(indx))
185 !$ if (retcode == -1) then
186 !$ call error_mesg('fms_affinity_set',trim(component)//': issue setting cpu affinity', FATAL)
187 !$ endif
188  !--- output affinity placement
189 !$ if (debug_affinity) then
190 !$ call hostnm(h_name)
191 !$ print *, 'DEBUG:',mpp_pe(),trim(component),' ',trim(h_name),fms_affinity_get(),th_num
192 !$ endif
193 !$OMP END PARALLEL
194 
195  end subroutine fms_affinity_set
196 end module fms_affinity_mod
subroutine, public fms_affinity_init()
Initialization routine for affinity handling.
subroutine, public fms_affinity_set(component, use_hyper_thread, nthreads)
Routine to set affinity for a component.
Private interface to retrieve this groups CPU set and it's size.
Private interface to set CPU afinity to a given core.
integer function, public check_nml_error(IOSTAT, NML_NAME)
Checks the iostat argument that is returned after reading a namelist and determines if the error code...
Definition: fms.F90:523
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
Definition: fms.F90:701
subroutine, public fms_init(localcomm, alt_input_nml_path)
Initializes the FMS module and also calls the initialization routines for all modules in the MPP pack...
Definition: fms.F90:286
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
Definition: fms.F90:441
integer function stdlog()
This function returns the current standard fortran unit numbers for log messages. Log messages,...
Definition: mpp_util.inc:58
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:406
Interface to get affinity from the current component.