go_timer.F90 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. !#######################################################################
  2. !
  3. ! NAME
  4. ! GO_Timer - General Objects : Timing routines
  5. !
  6. ! USAGE
  7. !
  8. ! use GO_Timer
  9. !
  10. ! ! timer id's:
  11. ! integer :: itim1, itim2, itim2a, itim2b
  12. !
  13. ! ! start timing:
  14. ! call GO_Timer_Init( status )
  15. !
  16. ! ! define timer names, return timer id's:
  17. ! call GO_Timer_Def( itim1 , 'part1' , status )
  18. ! call GO_Timer_Def( itim2 , 'part2' , status )
  19. ! call GO_Timer_Def( itim2a, 'part2a', status )
  20. ! call GO_Timer_Def( itim2b, 'part2b', status )
  21. !
  22. ! ! first task:
  23. ! call GO_Timer_Start(itim1,status)
  24. ! ! ...
  25. ! call GO_Timer_End(itim1,status)
  26. !
  27. ! ! second task:
  28. ! call GO_Timer_Start(itim2,status)
  29. ! ! ...
  30. ! ! child tasks:
  31. ! call GO_Timer_Start(itim2a,status)
  32. ! ! ...
  33. ! call GO_Timer_End(itim2a,status)
  34. ! call GO_Timer_Start(itim2b,status)
  35. ! ! ...
  36. ! call GO_Timer_End(itim2b,status)
  37. ! call GO_Timer_End(itim2,status)
  38. !
  39. ! ! stop timing, print profile to standard output;
  40. ! ! if an output file name is provided, the timing data is written
  41. ! ! to this file with the profile in the header:
  42. ! call GO_Timer_Done( status [,'profile.dat'] )
  43. !
  44. !
  45. ! HISTORY
  46. !
  47. ! 2008 apr, Arjo Segers, TNO
  48. !
  49. !#######################################################################
  50. !
  51. #define TRACEBACK write (gol,'("in ",a," (line",i5,")")') __FILE__, __LINE__; call goErr
  52. !
  53. #define IF_NOTOK_RETURN(action) if (status/=0) then; TRACEBACK; action; return; end if
  54. #define IF_ERROR_RETURN(action) if (status >0) then; TRACEBACK; action; return; end if
  55. !
  56. ! code compiled together with other GO modules ...
  57. #define with_go
  58. !
  59. !#######################################################################
  60. module GO_Timer
  61. #ifdef with_go
  62. use GO_Print, only : gol, goPr, goErr
  63. #endif
  64. implicit none
  65. ! --- in/out ------------------------
  66. private
  67. public :: GO_Timer_Init, GO_Timer_Done
  68. public :: GO_Timer_Def, GO_Timer_Start, GO_Timer_End
  69. public :: GO_Timer_Get
  70. ! --- const --------------------------
  71. character(len=*), parameter :: mname = 'GO_Timer'
  72. ! maximum number of times:
  73. integer, parameter :: maxtimer = 60
  74. ! real kind returned by cpu_time etc
  75. integer, parameter :: rknd = 8
  76. ! integer kind returned by system_clock etc
  77. integer, parameter :: iknd = 4
  78. ! --- types --------------------------
  79. type T_Timer
  80. ! label:
  81. character(len=64) :: name
  82. ! total time:
  83. !real(rknd) :: total_cpu
  84. real(rknd) :: total_sys
  85. end type T_Timer
  86. type T_Stopwatch
  87. !! timing using 'cpu_time' routine:
  88. !real(rknd) :: start_cpu
  89. !real(rknd) :: end_cpu
  90. !real(rknd) :: total_cpu
  91. ! timing using 'system_clock' routine:
  92. integer(iknd) :: start_sys
  93. integer(iknd) :: end_sys
  94. real(rknd) :: total_sys
  95. end type T_Stopwatch
  96. ! --- var ----------------------------
  97. ! list of timers:
  98. type(T_Timer) :: Timers(0:maxtimer)
  99. ! currently in use:
  100. integer :: ntimer
  101. ! root timer:
  102. integer :: itim_root
  103. ! parent-child relations:
  104. !logical :: child(0:maxtimer,maxtimer)
  105. ! StopWatch for each parent/child pair:
  106. type(T_Stopwatch) :: StopWatch(0:maxtimer,maxtimer)
  107. ! stack of current timers:
  108. integer :: stack(0:maxtimer)
  109. integer :: top
  110. ! parameters of system_clock :
  111. integer(iknd) :: sysclock_count_rate ! clock ticks per second
  112. integer(iknd) :: sysclock_count_max ! maximum number of ticks
  113. real(rknd) :: sysclock_tick2sec
  114. #ifndef with_go
  115. ! message line:
  116. character(len=1024) :: gol
  117. #endif
  118. contains
  119. #ifndef with_go
  120. ! ********************************************************************
  121. ! ***
  122. ! *** GO surrogate
  123. ! ***
  124. ! ********************************************************************
  125. ! substitutes for message routines from GO modules
  126. ! display message:
  127. subroutine goPr
  128. write (*,'(a)') trim(gol)
  129. end subroutine goPr
  130. ! display error message:
  131. subroutine goErr
  132. write (*,'("ERROR - ",a)') trim(gol)
  133. end subroutine goErr
  134. ! free file unit:
  135. subroutine goGetFU( fu, status )
  136. integer, intent(out) :: fu
  137. integer, intent(out) :: status
  138. logical :: opened
  139. fu = 456
  140. do
  141. inquire( unit=fu, opened=opened )
  142. if ( .not. opened ) exit
  143. fu = fu + 1
  144. end do
  145. status = 0
  146. end subroutine goGetFU
  147. #endif
  148. ! ********************************************************************
  149. ! ***
  150. ! *** GO Timer Routines
  151. ! ***
  152. ! ********************************************************************
  153. subroutine GO_Timer_Init( status )
  154. ! --- in/out -------------------------
  155. integer, intent(out) :: status
  156. ! --- const --------------------------
  157. character(len=*), parameter :: rname = mname//'/GO_Timer_Init'
  158. ! --- local --------------------------
  159. integer(iknd) :: sysclock_count
  160. integer :: itimer, ichild
  161. ! --- begin --------------------------
  162. ! init system clock parameters:
  163. call system_clock( sysclock_count, sysclock_count_rate, sysclock_count_max )
  164. ! conversion from clock ticks to seconds:
  165. sysclock_tick2sec = 1.0/real(sysclock_count_rate,8)
  166. ! no timers defined yet:
  167. ntimer = 0
  168. ! dummy name for base, which might be used as parent:
  169. Timers(0)%name = '0'
  170. ! no children yet:
  171. !child = .false.
  172. ! no StopWatch yet:
  173. do itimer = 0, ntimer
  174. do ichild = 1, ntimer
  175. ! set accumulated time to zero:
  176. !StopWatch(itimer,itimer)%total_cpu = 0.0
  177. StopWatch(itimer,itimer)%total_sys = 0.0
  178. end do
  179. end do
  180. ! empty stack:
  181. stack = 0
  182. top = 0
  183. ! define root timer:
  184. call GO_Timer_Def( itim_root, 'root', status )
  185. IF_NOTOK_RETURN(status=1)
  186. ! start root:
  187. call GO_Timer_Start( itim_root, status )
  188. IF_NOTOK_RETURN(status=1)
  189. ! ok
  190. status = 0
  191. end subroutine GO_Timer_Init
  192. ! ***
  193. subroutine GO_Timer_Done( status, file )
  194. #ifdef with_go
  195. use GO_File, only : goGetFU
  196. #endif
  197. ! --- in/out -------------------------
  198. integer, intent(out) :: status
  199. character(len=*), intent(in), optional :: file
  200. ! --- const --------------------------
  201. character(len=*), parameter :: rname = mname//'/GO_Timer_Done'
  202. ! --- local --------------------------
  203. integer :: itimer, ichild
  204. character(len=40) :: label, child_label
  205. real(rknd) :: total, child_total
  206. real(rknd) :: children_total
  207. real :: frac
  208. integer :: fu
  209. real(rknd) :: child_totals(maxtimer)
  210. ! --- begin --------------------------
  211. ! stop root:
  212. call GO_Timer_End( itim_root, status )
  213. IF_NOTOK_RETURN(status=1)
  214. ! also to file ?
  215. if ( present(file) ) then
  216. ! free file unit:
  217. call goGetFU( fu, status )
  218. IF_NOTOK_RETURN(status=1)
  219. ! open file:
  220. open( fu, file=trim(file), form='formatted', iostat=status )
  221. if (status/=0) then
  222. write (gol,'("opening timer output file : ",a)') trim(file); call goPr
  223. TRACEBACK; status=1; return
  224. end if
  225. end if
  226. ! print table
  227. write (gol,'(" ")'); call goPr
  228. write (gol,'("------------------------------------------ ------------ ---------")'); call goPr
  229. write (gol,'("timer system_clock (%)")'); call goPr
  230. write (gol,'("------------------------------------------ ------------ ---------")'); call goPr
  231. ! also to file ?
  232. if ( present(file) ) then
  233. write (fu,'("#")')
  234. write (fu,'("# ------------------------------------------ ------------ ---------")')
  235. write (fu,'("# timer system_clock (%)")')
  236. write (fu,'("# ------------------------------------------ ------------ ---------")')
  237. end if
  238. ! loop over all timers:
  239. do itimer = 1, ntimer
  240. ! current values:
  241. label = trim(timers(itimer)%name)
  242. !total = timers(itimer)%total_cpu
  243. total = timers(itimer)%total_sys
  244. ! display:
  245. write (gol,'(" ")'); call goPr
  246. write (gol,'(a40," ",1(" ",f12.2," "))') label, total; call goPr
  247. ! also to file ?
  248. if ( present(file) ) then
  249. write (fu,'("#")')
  250. write (fu,'("# ",a40," ",1(" ",f12.2," "))') label, total
  251. end if
  252. ! loop over children:
  253. !children_total = 0.0
  254. children_total = 0.0
  255. do ichild = 1, ntimer
  256. ! child values:
  257. child_label = trim(timers(ichild)%name)
  258. !child_total = StopWatch(itimer,ichild)%total_cpu
  259. child_total = StopWatch(itimer,ichild)%total_sys
  260. ! no time spend here ? then skip:
  261. if ( child_total <= 0.0 ) cycle
  262. ! set fraction:
  263. if ( total > 0.0 ) then
  264. frac = child_total / total
  265. else
  266. frac = 1.0
  267. endif
  268. ! display:
  269. write (gol,'(" ",a40,1(" ",f12.2," (",f5.1," %)"))') child_label, child_total, frac*100.0; call goPr
  270. ! also to file ?
  271. if ( present(file) ) then
  272. write (fu,'("# ",a40,1(" ",f12.2," (",f5.1," %)"))') child_label, child_total, frac*100.0
  273. end if
  274. ! update sum:
  275. children_total = children_total + child_total
  276. end do ! child
  277. ! other ?
  278. if ( children_total > 0.0 ) then
  279. ! 'child' values:
  280. child_label = 'other'
  281. child_total = total - children_total
  282. ! check ...
  283. if ( child_total < 0.0 ) then
  284. ! tell the user to check the code ...
  285. write (gol,'("WARNING - total of children exceeds time spent by parent, probably a wrong start/end pair somewhere!")')
  286. ! next timer:
  287. cycle
  288. end if
  289. ! set fraction:
  290. if ( total > 0.0 ) then
  291. frac = child_total / total
  292. else
  293. frac = 1.0
  294. endif
  295. ! display:
  296. write (gol,'(" ",a40,1(" ",f12.2," (",f5.1," %)"))') child_label, child_total, frac*100.0; call goPr
  297. ! also to file ?
  298. if ( present(file) ) then
  299. write (fu,'("# ",a40,1(" ",f12.2," (",f5.1," %)"))') child_label, child_total, frac*100.0
  300. end if
  301. end if
  302. end do ! timers
  303. ! close table:
  304. write (gol,'(" ")'); call goPr
  305. write (gol,'("------------------------------------------ ------------ ---------")'); call goPr
  306. write (gol,'(" ")'); call goPr
  307. ! also to file ?
  308. if ( present(file) ) then
  309. write (fu,'("#")')
  310. write (fu,'("# ------------------------------------------ ------------ ---------")')
  311. write (fu,'("#")')
  312. end if
  313. ! write all data to the file:
  314. if ( present(file) ) then
  315. ! all data:
  316. write (fu,'("# number of timers:")')
  317. write (fu,*) ntimer
  318. write (fu,'("# index, total time, name")')
  319. do itimer = 1, ntimer
  320. write (fu,'(i4,f12.4," ",a)') itimer, timers(itimer)%total_sys, trim(timers(itimer)%name)
  321. end do
  322. write (fu,'("# for each timer, total times spent on child processes")')
  323. do itimer = 1, ntimer
  324. ! collect child totals:
  325. child_totals = 0.0
  326. do ichild = 1, ntimer
  327. child_totals(ichild) = StopWatch(itimer,ichild)%total_sys
  328. end do
  329. write (fu,'(1000f12.4)') child_totals(1:ntimer)
  330. end do
  331. end if
  332. ! close file if necessary:
  333. if ( present(file) ) then
  334. ! close:
  335. close( fu, iostat=status )
  336. if (status/=0) then
  337. write (fu,'("# closing timer output file : ",a)') trim(file); call goPr
  338. TRACEBACK; status=1; return
  339. end if
  340. end if
  341. ! ok
  342. status = 0
  343. end subroutine GO_Timer_Done
  344. ! ***
  345. subroutine GO_Timer_Def( itimer, name, status )
  346. ! --- in/out -------------------------
  347. integer, intent(out) :: itimer
  348. character(len=*), intent(in) :: name
  349. integer, intent(out) :: status
  350. ! --- const --------------------------
  351. character(len=*), parameter :: rname = mname//'/GO_Timer_Def'
  352. ! --- local --------------------------
  353. integer :: k
  354. ! --- begin --------------------------
  355. ! new number:
  356. ntimer = ntimer + 1
  357. ! check ...
  358. if ( ntimer > maxtimer ) then
  359. write (gol,'("could not define timer for `",a,"` ;")') trim(name); call goPr
  360. write (gol,'("reached maximum number of timers:")'); call goPr
  361. do k = 1, maxtimer
  362. write (gol,'(" ",i6," ",a)') k, trim(timers(k)%name); call goPr
  363. end do
  364. write (gol,'("increase value of parameter `maxtimer` in module `",a,"`")') trim(mname); call goPr
  365. TRACEBACK; status=1; return
  366. end if
  367. !! debug ...
  368. !print *, 'TTT def timer : ', ntimer, ' ', trim(name)
  369. ! current number:
  370. itimer = ntimer
  371. ! store:
  372. timers(itimer)%name = trim(name)
  373. ! init totals:
  374. !timers(itimer)%total_cpu = 0.0
  375. timers(itimer)%total_sys = 0.0
  376. ! ok:
  377. status = 0
  378. end subroutine GO_Timer_Def
  379. ! ***
  380. subroutine GO_Timer_Get( itimer, status, name )
  381. ! --- in/out -------------------------
  382. integer, intent(in) :: itimer
  383. integer, intent(out) :: status
  384. character(len=*), optional :: name
  385. ! --- const --------------------------
  386. character(len=*), parameter :: rname = mname//'/GO_Timer_Name'
  387. ! --- local --------------------------
  388. ! --- begin --------------------------
  389. ! extract values
  390. if ( present(name) ) name = trim(timers(itimer)%name)
  391. ! ok:
  392. status = 0
  393. end subroutine GO_Timer_Get
  394. ! ***
  395. subroutine GO_Timer_Start( itimer, status )
  396. ! --- in/out -------------------------
  397. integer, intent(in) :: itimer
  398. integer, intent(out) :: status
  399. ! --- local --------------------------
  400. integer :: i
  401. integer :: iparent
  402. ! --- begin --------------------------
  403. ! check ...
  404. if ( itimer < 1 ) then
  405. write (gol,'("timer id < 1 ; not defined ?")'); call goErr
  406. TRACEBACK; status=1; return
  407. end if
  408. ! check ...
  409. if ( top == size(stack) ) then
  410. write (gol,'("timer stack out of bounds:")'); call goErr
  411. do i = 1, top
  412. write (gol,'(i6," : ",i6," `",a,"`")') i, stack(i), trim(Timers(i)%name); call goErr
  413. end do
  414. write (gol,'("probably bug in start/end calls, please check ...")'); call goErr
  415. TRACEBACK; status=1; return
  416. end if
  417. ! check ...
  418. if ( top < 0 ) then
  419. write (gol,'("stack could not be lower than zero, but top is now : ",i6)') top; call goErr
  420. TRACEBACK; status=1; return
  421. end if
  422. ! add to stack:
  423. top = top + 1
  424. stack(top) = itimer
  425. ! current timer is on top of stack;
  426. ! parent code has timer stack(top-1):
  427. iparent = stack(top-1)
  428. ! set flag that parent calls this part of the code:
  429. !child(iparent,itimer) = .true.
  430. !! store time:
  431. !call cpu_time( StopWatch(iparent,itimer)%start_cpu )
  432. ! store ticks:
  433. call system_clock( StopWatch(iparent,itimer)%start_sys )
  434. ! ok:
  435. status = 0
  436. end subroutine GO_Timer_Start
  437. ! ***
  438. subroutine GO_Timer_End( itimer, status )
  439. ! --- in/out -------------------------
  440. integer, intent(in) :: itimer
  441. integer, intent(out) :: status
  442. ! --- local --------------------------
  443. integer :: iparent
  444. !real(rknd) :: dt_cpu
  445. real(rknd) :: dt_sys
  446. ! --- begin --------------------------
  447. ! check ..
  448. if ( stack(top) /= itimer ) then
  449. write (gol,'("end timer id not the same as start timer id:")'); call goErr
  450. write (gol,'(" start (top of stack) : ",i6," `",a,"`")') stack(top), trim(Timers(stack(top))%name); call goErr
  451. write (gol,'(" end : ",i6," `",a,"`")') itimer, trim(Timers(itimer)%name); call goErr
  452. write (gol,'("check if each timer start is followed by a correct timer end")'); call goErr
  453. TRACEBACK; status=1; return
  454. end if
  455. ! check ...
  456. if ( top < 1 ) then
  457. write (gol,'("timer end but stack empty ...")'); call goErr
  458. write (gol,'("check if each call to timer_end has a corresponding call to timer_start")'); call goErr
  459. TRACEBACK; status=1; return
  460. end if
  461. ! current timer is on top of stack;
  462. ! parent code has timer stack(top-1):
  463. iparent = stack(top-1)
  464. !! store time:
  465. !call cpu_time( stopwatch%end_cpu )
  466. !! add time increment:
  467. !dt_cpu = stopwatch%end_cpu - stopwatch%start_cpu
  468. !! add time increments:
  469. !StopWatch(iparent,itimer)%total_cpu = StopWatch(iparent,itimer)%total_cpu + dt_cpu
  470. !Timers ( itimer)%total_cpu = Timers ( itimer)%total_cpu + dt_cpu
  471. ! store time:
  472. call system_clock( StopWatch(iparent,itimer)%end_sys )
  473. ! trap reset:
  474. if ( StopWatch(iparent,itimer)%end_sys < StopWatch(iparent,itimer)%start_sys ) then
  475. ! set time increment:
  476. dt_sys = ( StopWatch(iparent,itimer)%end_sys + ( sysclock_count_max - StopWatch(iparent,itimer)%start_sys ) ) * &
  477. sysclock_tick2sec
  478. else
  479. ! set time increment:
  480. dt_sys = ( StopWatch(iparent,itimer)%end_sys - StopWatch(iparent,itimer)%start_sys ) * sysclock_tick2sec
  481. end if
  482. ! add time increments:
  483. StopWatch(iparent,itimer)%total_sys = StopWatch(iparent,itimer)%total_sys + dt_sys
  484. Timers ( itimer)%total_sys = Timers ( itimer)%total_sys + dt_sys
  485. ! debugging ...
  486. !write (*,'("xxx added ",f6.2," to timer `",a,"`; called from `",a,"`")') dt_sys, trim(Timers(itimer)%name), trim(Timers(iparent)%name)
  487. ! pop from stack:
  488. top = top - 1
  489. ! ok:
  490. status = 0
  491. end subroutine GO_Timer_End
  492. ! ***
  493. end module GO_Timer
  494. !! ##########################################################
  495. !! ###
  496. !! ### test
  497. !! ###
  498. !! ##########################################################
  499. !
  500. !program test
  501. !
  502. ! use GO_Timer
  503. !
  504. ! implicit none
  505. !
  506. ! ! timer id's:
  507. ! integer :: itim1, itim2, itim2a, itim2b
  508. !
  509. ! ! local:
  510. ! integer :: status
  511. !
  512. ! ! start timing:
  513. ! call GO_Timer_Init( status )
  514. ! if (status/=0) stop 'ERROR from GO_Timer_Init'
  515. !
  516. ! ! define timer names, return timer id's:
  517. ! call GO_Timer_Def( itim1 , 'part1' , status )
  518. ! call GO_Timer_Def( itim2 , 'part2' , status )
  519. ! call GO_Timer_Def( itim2a, 'part2a', status )
  520. ! call GO_Timer_Def( itim2b, 'part2b', status )
  521. !
  522. ! ! first task:
  523. ! call GO_Timer_Start(itim1,status)
  524. ! ! ...
  525. ! call Sleep( 1 )
  526. ! ! ...
  527. ! call GO_Timer_End(itim1,status)
  528. !
  529. ! ! second task:
  530. ! call GO_Timer_Start(itim2,status)
  531. ! ! ...
  532. ! call Sleep( 2 )
  533. ! ! ...
  534. ! ! child tasks:
  535. ! call GO_Timer_Start(itim2a,status)
  536. ! ! ...
  537. ! call Sleep( 2 )
  538. ! ! ...
  539. ! call GO_Timer_End(itim2a,status)
  540. ! call GO_Timer_Start(itim2b,status)
  541. ! ! ...
  542. ! call Sleep( 3 )
  543. ! ! ...
  544. ! call GO_Timer_End(itim2b,status)
  545. ! ! ...
  546. ! call Sleep( 1 )
  547. ! ! ...
  548. ! call GO_Timer_End(itim2,status)
  549. !
  550. ! ! stop timing, print profile
  551. ! call GO_Timer_Done( status )
  552. ! if (status/=0) stop 'ERROR from GO_Timer_Done'
  553. !
  554. !end program test
  555. !