!===============================================================================
!
! timing.F90 - measurements of execution times and performance
!
!-------------------------------------------------------------------------------
!
! Copyright (C) 1998-2016 Hinnerk Stueben, 
!               2006-2013 Yoshifumi Nakamura
!
! This file is part of BQCD -- Berlin Quantum ChromoDynamics program
!
! BQCD is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! BQCD is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with BQCD.  If not, see <http://www.gnu.org/licenses/>.
!
!-------------------------------------------------------------------------------
! Added some new timing bins (sc_cxpby, dsf_poly, etc.)
!
! Editor: Taylor Haar
! Last modified: 04/04/2017
!-------------------------------------------------------------------------------
# include "defs.h"

!-------------------------------------------------------------------------------
module module_timing_meas

  implicit none

  type type_timing
     SECONDS time
     SECONDS total_time
     integer n_call
     integer fill_the_cache_line(3)
  end type type_timing

  integer, parameter        :: n_timing = TIMING_N_BIN - 1
  integer, parameter        :: n_timing_flops = TIMING_BIN_SEPARATOR - 1
  integer, parameter        :: timing_bandwidth_i1 = TIMING_BIN_SEPARATOR + 1
  integer, parameter        :: timing_bandwidth_i2 = n_timing 

  integer, parameter        :: len_timing = 16

  type(type_timing), save   :: meas(n_timing)  ! measurements

  data meas /n_timing * type_timing(0.0, 0.0, 0, 0)/

  character(*), parameter :: ifmt = "(i8)"
  character(*), parameter :: ffmt = "(f8.2)"
  character(*), parameter :: tab_fmt ="(2(3(1x,a),2x),1x,a)"

end

!-------------------------------------------------------------------------------
module module_timing_write

  use      module_timing_meas
  implicit none

  character(len_timing), save :: text(n_timing_flops)  ! region name
  integer(8), save            :: n_op(n_timing_flops)  ! # operations

  data text(timing_bin_d_xf)       /"d_xf"/
  data text(timing_bin_d_xb)       /"d_xb"/
  data text(timing_bin_d_yf)       /"d_yf"/
  data text(timing_bin_d_yb)       /"d_yb"/
  data text(timing_bin_d_zf)       /"d_zf"/
  data text(timing_bin_d_zb)       /"d_zb"/
  data text(timing_bin_d_t)        /"d_t"/
  data text(timing_bin_d_fb)       /"d_fb"/
  data text(timing_bin_d_dag_fb)   /"d_dag_fb"/
  data text(timing_bin_d_xyzt)     /"d_xyzt"/
  data text(timing_bin_d)          /"D_TOTAL"/

  data text(timing_bin_d_dd)       /"d_dd"/
  data text(timing_bin_d_eo)       /"d_eo"/
  data text(timing_bin_bagel_d)    /"bagel_d"/

  data text(timing_bin_cg_projection)  /"cg_projection"/
  data text(timing_bin_cg_proj_x)  /"cg_proj_x"/
  data text(timing_bin_cg_proj_y)  /"cg_proj_y"/
  data text(timing_bin_cg_proj_z)  /"cg_proj_z"/
  data text(timing_bin_cg_proj_t)  /"cg_proj_t"/
  data text(timing_bin_cg_d_x)     /"cg_d_x"/
  data text(timing_bin_cg_d_y)     /"cg_d_y"/
  data text(timing_bin_cg_d_z)     /"cg_d_z"/
  data text(timing_bin_cg_d_t)     /"cg_d_t"/
  data text(timing_bin_cg_d)       /"cg_d"/
  data text(timing_bin_cg_clover_mult_ao) /"cg_clover_ao"/
  data text(timing_bin_cg_clover_mult_a2) /"cg_clover_a2"/
  data text(timing_bin_cg_loop1)   /"cg_loop1"/
  data text(timing_bin_cg_loop2)   /"cg_loop2"/
  data text(timing_bin_cg_loop3)   /"cg_loop3"/
  data text(timing_bin_cg_axpy)    /"cg_axpy"/
  data text(timing_bin_cg_xpby)    /"cg_xpby"/

  data text(timing_bin_cg_mre_mtmp)    /"cg_mre_mtmp"/
  data text(timing_bin_cg_global_sum)  /"cg_global_sum"/
  data text(timing_bin_global_sum)     /"global_sum"/
  data text(timing_bin_global_sum_vec) /"global_sum_vec"/
  data text(timing_bin_sc_zero)        /"sc_zero"/
  data text(timing_bin_sc_copy)        /"sc_copy"/
  data text(timing_bin_sc_scale)       /"sc_scale"/
  data text(timing_bin_sc_cscale)      /"sc_cscale"/
  data text(timing_bin_sc_add)         /"sc_add"/
  data text(timing_bin_sc_axpy)        /"sc_axpy"/
  data text(timing_bin_sc_caxpy)       /"sc_caxpy"/
  data text(timing_bin_sc_cxpby)       /"sc_cxpby"/
  data text(timing_bin_sc_caxpby)      /"sc_caxpby"/
  data text(timing_bin_sc_caxpy2)      /"sc_caxpy2"/
  data text(timing_bin_sc_cax2)        /"sc_cax2"/
  data text(timing_bin_sc_xpby)        /"sc_xpby"/
  data text(timing_bin_sc_axpby)       /"sc_axpby"/
  data text(timing_bin_sc_norm2)       /"sc_norm2"/
  data text(timing_bin_sc_dot)         /"sc_dot"/
  data text(timing_bin_sc_cdotc)       /"sc_cdotc"/

  data text(timing_bin_cg)            /"CG"/
  data text(timing_bin_cg_mcg)        /"CG_mcg"/
  data text(timing_bin_cg_mbcg)       /"CG_mbcg"/
  data text(timing_bin_cg_ritz)       /"CG_ritz"/
  data text(timing_bin_cg_mix)        /"CG_mix"/
  data text(timing_bin_bicgstab)      /"CG_bicgstab"/
  data text(timing_bin_bicgstab_mix)  /"CG_bicgstab_mix"/

  data text(timing_bin_cg_dd)         /"CG_DD"/
  data text(timing_bin_cg_hh)         /"CG_HH"/
  data text(timing_bin_mtdagmt)       /"MTDAGMT"/
  data text(timing_bin_mtdagmt_r4)    /"MTDAGMT_r4"/

  data text(timing_bin_clover_init)   /"clover_init"/
  data text(timing_bin_clover_mult_a) /"clover_mult_a"/
  data text(timing_bin_clover_mult_ao)/"clover_mult_ao"/
  data text(timing_bin_clover_mult_b) /"clover_mult_b"/
  data text(timing_bin_clover_dsd)    /"clover_dsd"/
  data text(timing_bin_clover_dsf)    /"clover_dsf"/
  data text(timing_bin_clover_bsa_w)  /"clover_bsa_w"/
  data text(timing_bin_clover_dsd_w)  /"clover_dsd_w"/
  data text(timing_bin_clover_d_w)    /"clover_d_w"/

  data text(timing_bin_h_mult_a)      /"h_mult_a"/
  data text(timing_bin_h_mult_b)      /"h_mult_b"/
  data text(timing_bin_h_mult_c)      /"h_mult_c"/

  data text(timing_bin_dsg)           /"dsg"/

  data text(timing_bin_dsf)           /"dsf"/
  data text(timing_bin_dsf_at_bt)     /"dsf_at_bt"/
  data text(timing_bin_dsf_xyztfb_w)  /"dsf_xyztfb_w"/
  data text(timing_bin_dsf_sum)       /"dsf_sum"/
  data text(timing_bin_dsf_w2gen)     /"dsf_w2gen"/
  data text(timing_bin_unprec_mmul)   /"unprec_mmul"/
  data text(timing_bin_unprec_dsf)    /"unprec_dsf"/
  data text(timing_bin_dsf_unprec_wilson) /"dsf_un"/
  data text(timing_bin_dsf_mtmp)      /"dsf_mtmp"/
  data text(timing_bin_wmul_r8)       /"dsf_wmul_r8"/
  data text(timing_bin_wdag_r8)       /"dsf_wdag_r8"/

  data text(timing_bin_hmc_init)      /"hmc_init"/
  data text(timing_bin_hmc_init_p)    /"hmc_init_p"/
  data text(timing_bin_hmc_u)         /"hmc_u"/
  data text(timing_bin_hmc_momenta)   /"hmc_momenta"/
  data text(timing_bin_hmc_init_phi)  /"hmc_phi"/
  data text(timing_bin_hmc_h_old)     /"hmc_h_old"/
  data text(timing_bin_hmc_backup)    /"hmc_backup"/
  data text(timing_bin_hmc_half_step0)/"hmc_half_step0"/
  data text(timing_bin_hmc_half_step1)/"hmc_half_step1"/
  data text(timing_bin_hmc_xbound_g)  /"hmc_xbound_g"/
  data text(timing_bin_hmc_steps)     /"hmc_steps"/
  data text(timing_bin_hmc_h_new)     /"hmc_h_new"/
  data text(timing_bin_hmc_rest)      /"hmc_rest"/
  data text(timing_bin_integrator)    /"integrator"/
  data text(timing_bin_hmc)           /"HMC"/

  data text(timing_bin_sc2_projection)/"sc2_projection"/
!  data text(timing_bin_sc2_projection_xh)/"sc2_projection_xh"/
!  data text(timing_bin_sc2_projection_yh)/"sc2_projection_yh"/
!  data text(timing_bin_sc2_projection_zh)/"sc2_projection_zh"/
!  data text(timing_bin_sc2_projection_th)/"sc2_projection_th"/

  data text(timing_bin_ran_gauss_volh)/"ran_gauss_volh"/
  data text(timing_bin_plaq)          /"plaquette"/
  data text(timing_bin_cooling)       /"cooling"/
  data text(timing_bin_chemical_determinant) /"chemidet"/
  data text(timing_bin_chemidet_dirac4)      /"chemidet_dirac4"/
  data text(timing_bin_chemidet_dirac3)      /"chemidet_dirac3"/
  data text(timing_bin_chemidet_moment)      /"chemidet_moment"/

  data text(timing_bin_dsig)          /"dsig"/
  data text(timing_bin_rect)          /"rectangle"/
  data text(timing_bin_chai)          /"chair"/
  data text(timing_bin_para)          /"parallelogram"/
  data text(timing_bin_stout_smear)   /"stout_smear"/
  data text(timing_bin_stout_diffe)   /"stout_diffe"/
  data text(timing_bin_solve_invmdagm_byqnohat)   /"ddbqnohat"/
  data text(timing_bin_vecd)          /"vecd"/
  data text(timing_bin_vecc)          /"vecc"/

  data text(timing_bin_dsf_poly)   /"dsf_poly"/
  data text(timing_bin_gen_chi_eta) /"dsf_gen_chi_eta"/
  data text(timing_bin_cg_mre_matrix) /"cg_mre_matrix"/

  data text(timing_bin_total)         /"TOTAL"/

  integer(8), parameter          :: op_add   = 2  ! operations per complex add.
  integer(8), parameter          :: op_mult  = 6  ! operations per complex mult.

  integer(8), parameter, private :: op_d_xyz = 18 * op_mult + 30 * op_add
  integer(8), parameter, private :: op_d_t   = 36 * op_mult + 24 * op_add + 24
  integer(8), parameter, private :: op_d2_xyz= 2 * op_d_xyz
#ifndef GAMMAC
  integer(8), parameter          :: op_d     = 6 * op_d_xyz + op_d_t
#else
  integer(8), parameter          :: op_d     = 8 * op_d_xyz - 12 * op_add
#endif
  integer(8), parameter          :: op_d_xyzt= op_d - 24
  integer(8), parameter          :: op_d_fb  = op_d_xyzt / 2

!!  integer(8), parameter, private :: op_uuu   = 162 * op_mult
  integer(8), parameter, private :: op_re_tr = 36
!!  integer(8), parameter, private :: op_plaq  = 2 * 6 * (op_uuu + op_re_tr)

  integer(8), parameter          :: op_sc_r    = NDIRAC * NCOL * SIZE_COMPLEX
  integer(8), parameter          :: op_sc_c    = NDIRAC * NCOL

  integer(8), parameter          :: op_blas_r1 = op_sc_r
  integer(8), parameter          :: op_blas_r2 = op_sc_r * 2
  integer(8), parameter          :: op_blas_r3 = op_sc_r * 3
  integer(8), parameter          :: op_blas_r4 = op_sc_r * 4
  integer(8), parameter          :: op_blas_r6 = op_sc_r * 6
  integer(8), parameter          :: op_blas_c1 = op_sc_c * op_mult
  integer(8), parameter          :: op_blas_c2 = op_sc_c * (op_mult + op_add)
  integer(8), parameter          :: op_blas_c3 = 2 * op_blas_c2

#ifdef GAMMAC
  integer(8), parameter          :: op_clov  = 60 * op_mult + 60 * op_add + 24
#else
  integer(8), parameter          :: op_clov  = 60 * op_mult + 84 * op_add + 24
  integer(8), parameter          :: op_clov2 = op_clov + op_blas_r2
#endif

  integer(8), parameter          :: op_h_mult= NDIRAC * NCOL * 3

  integer(8), parameter          :: op_sc2_proj = 36 * op_add
  integer(8), parameter          :: op_sc2_proj_i = 12 * op_add

  integer(8), parameter          :: op_cg_projection = 36 * op_add
  integer(8), parameter          :: op_cg_proj = 6 * op_add

!---------------------------------------------------------------------- Y.N. 
!---------------------------------------------------------------------- recalculated op
!---------------------------------------------------------------------- to be chacked
  integer(8), parameter, private :: op_u_sas = 18 ! scale add sub
  integer(8), parameter, private :: op_th = 18 + 6 + 6 ! traceless hermitial
  integer(8), parameter, private :: op_uu    = 3**2 * ( 3 * op_mult + 2 * op_add )
  integer(8), parameter, private :: op_uup   = op_uu + op_u_sas
  integer(8), parameter, private :: op_uuu   = op_uu*2
  integer(8), parameter, private :: op_uuup  = op_uu + op_uup
  integer(8), parameter, private :: op_uuuu  = op_uu*3
  integer(8), parameter, private :: op_uuuuu = op_uu*4
!!  integer(8), parameter, private :: op_re_tr = 36
  integer(8), parameter, private :: op_plaq  = 2 * 6 * (  op_uuu + op_re_tr + 1)
  integer(8), parameter, private :: op_rect  = 2 * 6 * (4*op_uuu + op_re_tr + 1)
  integer(8), parameter, private :: op_chai  = 2 * 48 * (op_uuuuu + op_re_tr)
  integer(8), parameter, private :: op_para  = 2 * 16 * (op_uuuuu + op_re_tr)
  integer(8), parameter, private :: op_staple = 6 * (op_uuu + op_u_sas)
  integer(8), parameter          :: op_gen_m_force = 8 * (op_uu + 19)
  !
  ! calculate op_dsf_xyztfb_w
  !
  integer(8), parameter, private :: op_spin_dcmp = 3 * 2 * op_add
                                   !transposed su3 conjg spin spin
  integer(8), parameter, private :: op_trans3_css =  9*(4*op_mult + 3*op_add)
  integer(8), parameter          :: op_dsf_xyztfb_w = 16 * (op_spin_dcmp + op_trans3_css) &
                                    + 8*(op_u_sas) *2 + 8*op_uu*2
  integer(8), parameter          :: op_clover_bsa_w = 6*(2*op_u_sas + op_trans3_css)
  integer(8), parameter          :: op_clover_dsd_w = 12*op_clov + 6*12*op_u_sas + 12*op_u_sas
  integer(8), parameter          :: op_clover_d_w = 6*(18+ 4*(18+ 8*op_uu + 3*op_u_sas ))  +8*op_u_sas
  !
  ! calculate op_stout
  !
  integer(8), parameter, private :: op_makeq1 = op_staple + op_uu + op_u_sas + op_th
  integer(8), parameter, private :: op_a0_a1xu1_a2xu2 = 3*op_add + 9*( 2* op_mult + op_add)
  integer(8), parameter, private :: op_projsu3 = 3 * op_a0_a1xu1_a2xu2 + op_uu + op_re_tr + 8 + 265
  integer(8), parameter, private :: op_stout = 2 * 4 * (op_makeq1 + op_uu + op_projsu3)
  !
  ! calculate op_stout_d
  !
  integer(8), parameter, private :: op_lambda  = 2*op_uu + op_th + 9 * (2*op_mult + 4*op_add + 4)
  integer(8), parameter, private :: op_ulam_space  = 3 * (4*op_uu + op_u_sas + 3*op_uuup + 2*op_uup)
  integer(8), parameter, private :: op_stout_force_update  = 9*6
  integer(8), parameter, private :: op_stout_d1 = op_uu + 2*op_re_tr + op_lambda
  integer(8), parameter, private :: op_stout_d2 = 2*op_uu + op_staple + op_ulam_space + op_stout_force_update
  integer(8), parameter          :: op_stout_d = 2 * 4 * (op_stout_d1 + op_stout_d2)


  data n_op(timing_bin_d_xf)    /op_d_xyz/
  data n_op(timing_bin_d_xb)    /op_d_xyz/
  data n_op(timing_bin_d_yf)    /op_d_xyz/
  data n_op(timing_bin_d_yb)    /op_d_xyz/
  data n_op(timing_bin_d_zf)    /op_d_xyz/
  data n_op(timing_bin_d_zb)    /op_d_xyz/
  data n_op(timing_bin_d_t)     /op_d_t/
  data n_op(timing_bin_d_fb)    /op_d_fb/
  data n_op(timing_bin_d_dag_fb) /op_d_fb/
  data n_op(timing_bin_d_xyzt)  /op_d_xyzt/
  data n_op(timing_bin_d)       /op_d/
  data n_op(timing_bin_d_dd)    /0/
  data n_op(timing_bin_d_eo)    /0/
  data n_op(timing_bin_bagel_d)  /op_d/

  data n_op(timing_bin_cg_projection) /op_cg_projection/
  data n_op(timing_bin_cg_proj_x)  /op_cg_proj/
  data n_op(timing_bin_cg_proj_y)  /op_cg_proj/
  data n_op(timing_bin_cg_proj_z)  /op_cg_proj/
  data n_op(timing_bin_cg_proj_t)  /0/

  data n_op(timing_bin_cg_d_x)     /op_d2_xyz/
  data n_op(timing_bin_cg_d_y)     /op_d2_xyz/
  data n_op(timing_bin_cg_d_z)     /op_d2_xyz/
  data n_op(timing_bin_cg_d_t)     /op_d_t/
  data n_op(timing_bin_cg_d)       /op_d/
  data n_op(timing_bin_cg_clover_mult_ao) /op_clov/
  data n_op(timing_bin_cg_clover_mult_a2) /op_clov2/
  data n_op(timing_bin_cg_loop1)   /op_blas_r3/
  data n_op(timing_bin_cg_loop2)   /op_blas_r2/
  data n_op(timing_bin_cg_loop3)   /op_blas_r6/
  data n_op(timing_bin_cg_axpy)    /op_blas_r2/
  data n_op(timing_bin_cg_xpby)    /op_blas_r2/

  data n_op(timing_bin_cg_global_sum)  /0/
  data n_op(timing_bin_global_sum)     /0/
  data n_op(timing_bin_global_sum_vec) /0/
  data n_op(timing_bin_sc_zero)        /0/
  data n_op(timing_bin_sc_copy)        /0/
  data n_op(timing_bin_sc_scale)       /op_blas_r1/
  data n_op(timing_bin_sc_add)         /op_blas_r1/
  data n_op(timing_bin_sc_norm2)       /op_blas_r2/
  data n_op(timing_bin_sc_dot)         /op_blas_r2/
  data n_op(timing_bin_sc_axpy)        /op_blas_r2/
  data n_op(timing_bin_sc_xpby)        /op_blas_r2/
  data n_op(timing_bin_sc_axpby)       /op_blas_r3/
  data n_op(timing_bin_sc_cscale)      /op_blas_c1/
  data n_op(timing_bin_sc_cdotc)       /op_blas_c2/
  data n_op(timing_bin_sc_caxpy)       /op_blas_c2/
  data n_op(timing_bin_sc_cxpby)       /op_blas_c2/
  data n_op(timing_bin_sc_caxpby)      /op_blas_c3/
  data n_op(timing_bin_sc_caxpy2)      /op_blas_c3/
  data n_op(timing_bin_sc_cax2)        /op_blas_c3/

  data n_op(timing_bin_plaq)       /op_plaq/
  data n_op(timing_bin_cooling)    /0/
  data n_op(timing_bin_chemical_determinant)    /0/
  data n_op(timing_bin_chemidet_dirac4)    /0/
  data n_op(timing_bin_chemidet_dirac3)    /0/
  data n_op(timing_bin_chemidet_moment)    /0/

  data n_op(timing_bin_cg)            /0/
  data n_op(timing_bin_cg_mcg)        /0/
  data n_op(timing_bin_cg_mbcg)       /0/
  data n_op(timing_bin_cg_ritz)       /0/
  data n_op(timing_bin_cg_mix)        /0/
  data n_op(timing_bin_bicgstab)      /0/
  data n_op(timing_bin_bicgstab_mix)  /0/
  data n_op(timing_bin_cg_dd)         /0/
  data n_op(timing_bin_cg_hh)         /0/
  data n_op(timing_bin_mtdagmt)       /0/
  data n_op(timing_bin_mtdagmt_r4)    /0/

  data n_op(timing_bin_dsg)           /0/
  data n_op(timing_bin_dsf)           /0/

  data n_op(timing_bin_clover_init)   /0/
  data n_op(timing_bin_clover_mult_a) /op_clov/
  data n_op(timing_bin_clover_mult_ao)/op_clov/
  data n_op(timing_bin_clover_mult_b) /op_clov/
  data n_op(timing_bin_clover_dsd)    /0/
  data n_op(timing_bin_clover_dsf)    /0/

  data n_op(timing_bin_hmc_init)      /0/
  data n_op(timing_bin_hmc_init_p)    /0/
  data n_op(timing_bin_hmc_u)         /0/
  data n_op(timing_bin_hmc_momenta)   /0/
  data n_op(timing_bin_hmc_init_phi)  /0/
  data n_op(timing_bin_hmc_h_old)     /0/
  data n_op(timing_bin_hmc_backup)    /0/
  data n_op(timing_bin_hmc_half_step0)/0/
  data n_op(timing_bin_hmc_half_step1)/0/
  data n_op(timing_bin_hmc_xbound_g)  /0/
  data n_op(timing_bin_hmc_steps)     /0/
  data n_op(timing_bin_hmc_h_new)     /0/
  data n_op(timing_bin_hmc_rest)      /0/
  data n_op(timing_bin_integrator)    /0/
  data n_op(timing_bin_hmc)           /0/

  data n_op(timing_bin_h_mult_a)      /op_h_mult/
  data n_op(timing_bin_h_mult_b)      /op_h_mult/
  data n_op(timing_bin_h_mult_c)      /op_h_mult/
  data n_op(timing_bin_sc2_projection)/op_sc2_proj/
!  data n_op(timing_bin_sc2_projection_xh)/op_sc2_proj_i/
!  data n_op(timing_bin_sc2_projection_yh)/op_sc2_proj_i/
!  data n_op(timing_bin_sc2_projection_zh)/op_sc2_proj_i/
!  data n_op(timing_bin_sc2_projection_th)/0/
  data n_op(timing_bin_ran_gauss_volh)/0/

  data n_op(timing_bin_dsig)       /0/
  data n_op(timing_bin_rect)       /op_rect/
  data n_op(timing_bin_chai)       /op_chai/
  data n_op(timing_bin_para)       /op_para/
  data n_op(timing_bin_stout_smear)       /op_stout/
  data n_op(timing_bin_stout_diffe)       /op_stout_d/
  data n_op(timing_bin_clover_bsa_w   ) /op_clover_bsa_w/
  data n_op(timing_bin_clover_dsd_w   ) /op_clover_dsd_w/
  data n_op(timing_bin_clover_d_w   )   /op_clover_d_w/
  data n_op(timing_bin_dsf_at_bt     ) /0/
  data n_op(timing_bin_dsf_xyztfb_w  ) /op_dsf_xyztfb_w/
  data n_op(timing_bin_dsf_sum       ) /0/
  data n_op(timing_bin_dsf_w2gen     ) /0/
  data n_op(timing_bin_solve_invmdagm_byqnohat ) /0/
  data n_op(timing_bin_unprec_mmul )             /0/
  data n_op(timing_bin_unprec_dsf )              /0/
  data n_op(timing_bin_dsf_unprec_wilson )        /0/
  data n_op(timing_bin_dsf_mtmp)    /0/
  data n_op(timing_bin_wmul_r8)     /0/
  data n_op(timing_bin_wdag_r8)     /0/
  data n_op(timing_bin_cg_mre_mtmp) /0/
  data n_op(timing_bin_vecd)  /op_d/
  data n_op(timing_bin_vecc)  /op_clov/

  ! # ops for PFHMC timing bins (0 -> just time)
  data n_op(timing_bin_dsf_poly)    /0/
  data n_op(timing_bin_gen_chi_eta) /0/
  data n_op(timing_bin_cg_mre_matrix) /0/

  data n_op(timing_bin_total)         /0/
end

!-------------------------------------------------------------------------------
module module_timing_write_bandwidth

  use      module_timing_meas
  use      module_vol
  implicit none

  character(len_timing), save :: text(timing_bandwidth_i1:timing_bandwidth_i2)
  integer(8), save            :: size(timing_bandwidth_i1:timing_bandwidth_i2) = 0

  data text(timing_bin_xbound_g)         /"xbound_g"/
  data text(timing_bin_xbound_u1gen)     /"xbound_u1gen"/
  data text(timing_bin_xbound_axion)     /"xbound_axion"/
  data text(timing_bin_xbound_sc_x)      /"xbound_sc_x"/
  data text(timing_bin_xbound_sc_y)      /"xbound_sc_y"/
  data text(timing_bin_xbound_sc_z)      /"xbound_sc_z"/
  data text(timing_bin_xbound_sc_t)      /"xbound_sc_t"/
  data text(timing_bin_xbound_sc)        /"XBOUND_SC"/
  data text(timing_bin_xbound_sc2_x)     /"xbound_sc2_x"/
  data text(timing_bin_xbound_sc2_y)     /"xbound_sc2_y"/
  data text(timing_bin_xbound_sc2_z)     /"xbound_sc2_z"/
  data text(timing_bin_xbound_sc2_t)     /"xbound_sc2_t"/
  data text(timing_bin_xbound_sc2)       /"XBOUND_SC2"/

  data text(timing_bin_xbound_cg_proj_x) /"xbound_cg_proj_x"/
  data text(timing_bin_xbound_cg_proj_y) /"xbound_cg_proj_y"/
  data text(timing_bin_xbound_cg_proj_z) /"xbound_cg_proj_z"/
  data text(timing_bin_xbound_cg_proj_t) /"xbound_cg_proj_t"/
  data text(timing_bin_xbound_cg_proj)   /"XBOUND_CG_PROJ"/

  data text(timing_bin_u_read_bqcd)      /"u_read_bqcd"/
  data text(timing_bin_u_write_bqcd)     /"u_write_bqcd"/
  data text(timing_bin_u_read_ildg)      /"u_read_ildg"/
  data text(timing_bin_u_write_ildg)     /"u_write_ildg"/

end

!-------------------------------------------------------------------------------
subroutine timing_start(bin)

  use      module_timing_meas
  implicit none
  integer  bin
  SECONDS  sekunden

  meas(bin)%time = sekunden()
end

!-------------------------------------------------------------------------------
subroutine timing_stop(bin)

  use      module_timing_meas
  implicit none
  integer  bin
  SECONDS  sekunden

  meas(bin)%total_time = meas(bin)%total_time + sekunden() - meas(bin)%time
  meas(bin)%n_call = meas(bin)%n_call + 1
end

!-------------------------------------------------------------------------------
subroutine timing_write(unit)

  use      module_timing_write
  use      module_cg
  use      module_function_decl
  use      module_switches
  use      module_thread
  use      module_vol
  implicit none

  integer  unit, i
  integer  ierror
  integer(8)  op_mtdagmt, op_dsf_at_bt, op_dsf_sum, op_dsf_w2gen
  integer(8)  cg_calls, cg_iter
  integer(8) :: cgm_calls, cgm_iter, cgr_calls, cgr_iter, bicgstab_calls, bicgstab_iter
  REAL     mflops, mflops_mean, mflops_min, mflops_max
  REAL     total_gflops, time_mean

  character(len = 8) :: a_mflops_mean, a_mflops_min, a_mflops_max, &
                        a_total_gflops, a_time_mean, a_n_call
  
  if (version_of_d() >= 2) then
     n_op(timing_bin_d_xf) = n_op(timing_bin_d_xf) * 2
     n_op(timing_bin_d_yf) = n_op(timing_bin_d_yf) * 2
     n_op(timing_bin_d_zf) = n_op(timing_bin_d_zf) * 2
  endif

                       op_mtdagmt = 2 * (2 * op_d + op_blas_r2)
  if (switches%clover) op_mtdagmt = op_mtdagmt + 4 * op_clov
  if (switches%h_ext)  op_mtdagmt = op_mtdagmt + 4 * op_h_mult

  select case (version_of_d())
     case(21,22,23,24,210)
        n_op(timing_bin_d_xf) = n_op(timing_bin_d_xf) - 12 * op_add
        n_op(timing_bin_d_yf) = n_op(timing_bin_d_yf) - 12 * op_add
        n_op(timing_bin_d_zf) = n_op(timing_bin_d_zf) - 12 * op_add
     case(40)
        n_op(timing_bin_d_xf) = op_d - op_sc2_proj - 24
     case(100,101,102,103,104,105,106,107,108,109)
        n_op(timing_bin_d_xf) = 36 * op_mult + 36 * op_add
        n_op(timing_bin_d_yf) = 36 * op_mult + 48 * op_add
        n_op(timing_bin_d_zf) = 36 * op_mult + 48 * op_add
#ifdef GAMMAC
        n_op(timing_bin_d_t ) = n_op(timing_bin_d_zf)
#else
        n_op(timing_bin_d_t ) = n_op(timing_bin_d_t ) + 12 * op_add
#endif
  end select

!------------------------------- Y
                       op_mtdagmt = 2 * (2 * op_d + op_blas_r2)
  if (switches%clover) op_mtdagmt = op_mtdagmt + 4 * op_clov
  if (switches%h_ext)  op_mtdagmt = op_mtdagmt + 4 * op_h_mult
                       op_dsf_at_bt = 2 * op_d
  if (switches%clover) op_dsf_at_bt = op_dsf_at_bt + 2 * op_clov
  if (switches%h_ext)  op_dsf_at_bt = op_dsf_at_bt + 2 * op_h_mult
                       op_dsf_sum = op_dsf_at_bt + op_dsf_xyztfb_w
  if (switches%clover) op_dsf_sum = op_dsf_sum + 2 * op_clover_bsa_w
  op_dsf_w2gen = op_gen_m_force
  if (switches%clover) then
     if (switches%stout_ds .and. switches%stout_cl) then
        op_dsf_w2gen = op_dsf_w2gen + 2*op_clover_d_w + op_stout_d
     elseif((.not. switches%stout_ds) .and. (.not. switches%stout_cl)) then
        op_dsf_w2gen = op_dsf_w2gen + 2*op_clover_d_w
     else
        op_dsf_w2gen = op_dsf_w2gen + 2*op_clover_d_w + op_stout_d + 144
     endif
  else
     if (switches%stout_ds) op_dsf_w2gen = op_dsf_w2gen + op_stout_d
  endif
   n_op(timing_bin_dsf_at_bt) = op_dsf_at_bt
   n_op(timing_bin_dsf_sum  ) = op_dsf_sum
   n_op(timing_bin_dsf_w2gen) = op_dsf_w2gen
  !
  ! calculate op_cg
  !
  cgm_calls = meas(timing_bin_cg_mcg)%n_call
  cgr_calls = meas(timing_bin_cg_ritz)%n_call
  bicgstab_calls = meas(timing_bin_bicgstab)%n_call
  cgm_iter  = cgm_stat%niter_tot_run
  cgr_iter  = cg_ritz_stat%niter_tot_run
  bicgstab_iter  = bicgstab_stat%niter_tot_run
  n_op(timing_bin_cg_mcg) = &
           cgm_iter  * (op_mtdagmt + 4 * op_blas_r2) &
         + cgm_calls *  op_blas_r2 &
         + op_blas_r2 * cgm_stat%niter_tot_run_sub  &
         + op_blas_r3 * (cgm_stat%niter_tot_run_sub - cgm_iter)
  n_op(timing_bin_cg_ritz) = &
           cgr_iter  * (op_mtdagmt + 7 * op_blas_r2 + 2 * op_blas_r3) &
         + cgr_calls * (op_mtdagmt + op_blas_r1 + 4 * op_blas_r2)
  n_op(timing_bin_bicgstab) = &
           bicgstab_iter  * (2*op_mtdagmt + 6 * op_blas_r2 + 2 * op_blas_r4) &
         + bicgstab_calls * (  op_mtdagmt + op_blas_r1 + 2 * op_blas_r2)
if (cgm_calls/=0)      n_op(timing_bin_cg_mcg  )= nint(real(n_op(timing_bin_cg_mcg),   kind = RKIND) / real(cgm_calls), kind = RKIND)
if (cgr_calls/=0)      n_op(timing_bin_cg_ritz )= nint(real(n_op(timing_bin_cg_ritz ), kind = RKIND) / real(cgr_calls), kind = RKIND)
if (bicgstab_calls/=0) n_op(timing_bin_bicgstab)= nint(real(n_op(timing_bin_bicgstab), kind = RKIND) / real(bicgstab_calls), kind = RKIND)
  n_op(timing_bin_mtdagmt_r4) = op_mtdagmt
!-----------------------------------Y

  cg_calls = meas(timing_bin_cg)%n_call
  cg_iter  = cg_iterations_total

  n_op(timing_bin_mtdagmt) = op_mtdagmt
  n_op(timing_bin_cg)      = cg_iter * (op_mtdagmt + 5 * op_blas_r2) &
                           + cg_calls * (op_mtdagmt + op_blas_r1 + 2*op_blas_r2)
if (cg_calls/=0)  n_op(timing_bin_cg)      = nint( real(n_op(timing_bin_cg), kind = RKIND) / real(cg_calls), kind = RKIND)


  call begin(unit, "Timing")
  if (my_pe() == 0) then
     write(unit,"(48x,a)") "Performance"
     write(unit, tab_fmt) "region          ", "  #calls", "    time", &
          "    mean", "     min", "     max", "   Total"
     write(unit, tab_fmt) "                ", "        ", "       s", &
          " Mflop/s", " Mflop/s", " Mflop/s", " Gflop/s"
     write(unit, *)
  endif

  do i = 1, n_timing_flops

     write(a_n_call, ifmt) meas(i)%n_call
     a_time_mean    = " "
     a_mflops_mean  = " " 
     a_mflops_min   = " " 
     a_mflops_max   = " " 
     a_total_gflops = " " 

     if (meas(i)%n_call /= 0) then  ! must be true on all PEs !!

        time_mean = global_sum(meas(i)%total_time) / num_pes()
        write(a_time_mean, ffmt) time_mean

        if (n_op(i) /= 0 .and. time_mean /=ZERO .and. meas(i)%total_time /=ZERO) then
         !!mflops = 1d-6 * n_op(i) * volh * meas(i)%n_call / meas(i)%total_time
         !!mflops_mean = global_sum(mflops) / num_pes()
        
           mflops      = 1d-6 * n_op(i) * volh * meas(i)%n_call
           mflops_mean = mflops / time_mean
           mflops      = mflops / meas(i)%total_time

           mflops_min = global_min(mflops)
           mflops_max = global_max(mflops)
        
           total_gflops = 1d-3 * mflops_mean * num_pes()

           write(a_mflops_mean,  ffmt) mflops_mean / n_thread
           write(a_mflops_min,   ffmt) mflops_min / n_thread
           write(a_mflops_max,   ffmt) mflops_max / n_thread
           write(a_total_gflops, ffmt) total_gflops
        endif
     endif

     if (my_pe() == 0) then
        write(unit, tab_fmt) text(i), a_n_call, a_time_mean, &
                             a_mflops_mean, a_mflops_min, a_mflops_max, &
                             a_total_gflops
     endif
  enddo

  if (my_pe() == 0) write(unit,*)

  call timing_write_bandwidth(unit)

  call end(unit, "Timing")
end

!-------------------------------------------------------------------------------
subroutine timing_write_bandwidth(unit)

  use      module_timing_write_bandwidth
  use      module_function_decl
  use      module_input
  use      module_lattice
  use      module_switches
  use      module_thread
  use      module_vol
  implicit none

  integer, intent(in) :: unit

  integer    :: i
  integer(8) :: vol_g, vol_sc, vol_(DIM), vol_d_proj, vol_cg_proj
  integer(8) :: size_u_bqcd, size_u_ildg
  integer(8) :: size_g, size_u1gen, size_sc, size_sc2

  REAL       :: mbytes, mbytes_mean, mbytes_min, mbytes_max
  REAL       :: total_gbytes, time_mean

  character(len = 8) :: a_mbytes_mean, a_mbytes_min, a_mbytes_max, &
                        a_total_gbytes, a_time_mean, a_n_call

  vol_g = 1
  do i = 1, DIM
     if (npe(i) > 1) then
        vol_g = vol_g * (n(i) + 2)
     else
        vol_g = vol_g * n(i)
     endif
  enddo
  vol_g = vol_g - vol

  vol_sc = 0
  do i = 1, DIM
     if (npe(i) > 1) then
        vol_sc = vol_sc + FWD_BWD * (volh / nh(i))
     endif
  enddo

  do i = 1, DIM
     if (npe(i) > 1) then
        vol_(i) = FWD_BWD * (volh / nh(i))
     else
        vol_(i) = 0
     endif
  enddo

  vol_d_proj = vol_(1) / EVEN_ODD + vol_(2) + vol_(3) + vol_(4)
  vol_cg_proj = vol_(1) + vol_(2) + vol_(3) + vol_(4)

  size_u_bqcd = SIZE_COMPLEX * NCOL * (NCOL-1) * DIM
  size_u_ildg = SIZE_COMPLEX * NCOL *  NCOL    * DIM
  size_g      = SIZE_COMPLEX * NCOL *  NCOL    * DIM
  size_u1gen  = DIM
  size_sc     = SIZE_COMPLEX * NDIRAC * NCOL
  size_sc2    = SIZE_COMPLEX * NDIRAC2 * NCOL

  size(timing_bin_xbound_g)     = size_g * vol_g * RKIND
  size(timing_bin_xbound_u1gen) = size_u1gen * vol_g * RKIND

  size(timing_bin_xbound_sc_x)  = size_sc * vol_(1) * RKIND
  size(timing_bin_xbound_sc_y)  = size_sc * vol_(2) * RKIND
  size(timing_bin_xbound_sc_z)  = size_sc * vol_(3) * RKIND
  size(timing_bin_xbound_sc_t)  = size_sc * vol_(4) * RKIND
  size(timing_bin_xbound_sc)    = size_sc * vol_sc  * RKIND

  size(timing_bin_xbound_sc2_x) = size_sc2 * vol_(1) * RKIND
  size(timing_bin_xbound_sc2_y) = size_sc2 * vol_(2) * RKIND
  size(timing_bin_xbound_sc2_z) = size_sc2 * vol_(3) * RKIND
  size(timing_bin_xbound_sc2_t) = size_sc2 * vol_(4) * RKIND
  size(timing_bin_xbound_sc2)   = size_sc2 * vol_sc  * RKIND

  size(timing_bin_xbound_cg_proj_x) = size_sc2 * vol_(1) * input%tuning_cg_precision / 8
  size(timing_bin_xbound_cg_proj_y) = size_sc2 * vol_(2) * input%tuning_cg_precision / 8
  size(timing_bin_xbound_cg_proj_z) = size_sc2 * vol_(3) * input%tuning_cg_precision / 8
  size(timing_bin_xbound_cg_proj_t) = size_sc2 * vol_(4) * input%tuning_cg_precision / 8
  size(timing_bin_xbound_cg_proj)   = size_sc2 * vol_cg_proj * input%tuning_cg_precision / 8

  size(timing_bin_u_read_bqcd)  = size_u_bqcd * vol * RKIND
  size(timing_bin_u_read_ildg)  = size_u_ildg * vol * input%ildg_precision / 8

  size(timing_bin_u_write_bqcd) = size(timing_bin_u_read_bqcd)
  size(timing_bin_u_write_ildg) = size(timing_bin_u_read_ildg)


  if (my_pe() == 0) then
     write(unit,"(48x,a)") "Performance"
     write(unit, tab_fmt) "region          ", "  #calls", "    time", &
          "    mean", "     min", "     max", "   Total"
     write(unit, tab_fmt) "                ", "        ", "       s", &
          " MByte/s", " MByte/s", " MByte/s", " GByte/s"
     write(unit, *)
  endif

  do i = timing_bandwidth_i1, timing_bandwidth_i2

     write(a_n_call, ifmt) meas(i)%n_call
     a_time_mean    = " "
     a_mbytes_mean  = " " 
     a_mbytes_min   = " " 
     a_mbytes_max   = " " 
     a_total_gbytes = " " 

     if (meas(i)%n_call /= 0) then  ! must be true on all PEs !!

        time_mean = global_sum(meas(i)%total_time) / num_pes()

        if (i == timing_bin_u_read_bqcd .or. &
            i == timing_bin_u_read_ildg .or. &
            i == timing_bin_u_write_bqcd .or. &
            i == timing_bin_u_write_ildg) then
           time_mean = global_max(meas(i)%total_time)
        endif

        write(a_time_mean, ffmt) time_mean
        
        if (size(i) /= 0) then
           mbytes = 1d-6 * size(i) * meas(i)%n_call 

           if (i == timing_bin_u_read_bqcd .or. &
               i == timing_bin_u_read_ildg .or. &
               i == timing_bin_u_write_bqcd .or. &
               i == timing_bin_u_write_ildg) then
              mbytes      = mbytes / meas(i)%total_time 
              mbytes_mean = global_max(mbytes)
           else
              mbytes_mean = mbytes / time_mean
              mbytes      = mbytes / meas(i)%total_time  
          endif
        
           mbytes_min = global_min(mbytes)
           mbytes_max = global_max(mbytes)
        
           total_gbytes = 1d-3 * mbytes_mean * num_pes()

           write(a_mbytes_mean,  ffmt) mbytes_mean / n_thread
           write(a_mbytes_min,   ffmt) mbytes_min / n_thread
           write(a_mbytes_max,   ffmt) mbytes_max / n_thread
           write(a_total_gbytes, ffmt) total_gbytes
        endif
     endif

     if (my_pe() == 0) then
        write(unit, tab_fmt) text(i), a_n_call, a_time_mean, &
                             a_mbytes_mean, a_mbytes_min, a_mbytes_max, &
                             a_total_gbytes
     endif
  enddo
end

!===============================================================================
