User Tools

Site Tools


dev:profiling

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
profiling [2013/07/02 05:00] – Initial page with profiling information vondeledev:profiling [2020/08/21 10:15] (current) – external edit 127.0.0.1
Line 4: Line 4:
 In order to understand and improve the run-time performance of CP2K it is often needed to collect detailed timing information. This provides answers to questions such as 'how long does this subroutine take?' or 'who calls this subroutine'. This is most useful for developers, (non-expert) users rarely ever need this detailed info. In order to understand and improve the run-time performance of CP2K it is often needed to collect detailed timing information. This provides answers to questions such as 'how long does this subroutine take?' or 'who calls this subroutine'. This is most useful for developers, (non-expert) users rarely ever need this detailed info.
  
-Probably the most important part of a profiling exercise is taken a reasonable test case. Does it run quickly enough to be practical, long enough to be accurate ? Most importantly, does it exercise the interesting or relevant parts of the code. By selecting a suitable input, profiling can become much more precise, interesting, ...+Probably the most important part of a profiling exercise is finding a reasonable test case. Does it run quickly enough to be practical, long enough to be accurate ? Most importantly, does it exercise the interesting or relevant parts of the code. By selecting a suitable input, profiling can become much more precise, interesting, ...
  
 ===== The basics ===== ===== The basics =====
Line 66: Line 66:
   - SUBROUTINE: name, usually easily found in the CP2K code by 'grep qs_ks_build_kohn_sham_matrix' or similar. Sometimes a suffix is used to time sub-parts of a subroutine or to provide details.   - SUBROUTINE: name, usually easily found in the CP2K code by 'grep qs_ks_build_kohn_sham_matrix' or similar. Sometimes a suffix is used to time sub-parts of a subroutine or to provide details.
   - CALLS: the number of calls to this timer   - CALLS: the number of calls to this timer
-  - ASD: The 'Average Stack Depth', i.e. the average number of entries on the call stack. In the example above, it can be used to guess that qs_energies_scf is called by qs_forces is called by CP2K. However, see also the CALLGRAPH section below.+  - ASD: The 'Average Stack Depth', i.e. the average number of entries on the call stack. In the example above, it can be used to guess that qs_energies_scf is called by qs_forces is called by CP2K. However, see also the [[#the_cp2k_callgraph | CALLGRAPH section]] below.
   - SELF TIME: How much time is spent in this subroutine, or in non-timed subroutines called by this subroutine. AVERAGE and MAXIMUM correspond to this quantity compared between different MPI ranks, and can be used to locate load-imbalance or synchronization points.   - SELF TIME: How much time is spent in this subroutine, or in non-timed subroutines called by this subroutine. AVERAGE and MAXIMUM correspond to this quantity compared between different MPI ranks, and can be used to locate load-imbalance or synchronization points.
   - TOTAL TIME: How much time is spent in this subroutine, including time spent in timed subroutines. AVERAGE and MAXIMUM as defined above   - TOTAL TIME: How much time is spent in this subroutine, including time spent in timed subroutines. AVERAGE and MAXIMUM as defined above
  
-Note thatfor the threaded codeonly the master thread is instrumented.+By defaultonly routines contributing up to 2% of the total runtime are included in the timing report.  To see smaller routinesset a smaller cut-off with the [[http://manual.cp2k.org/trunk/CP2K_INPUT/GLOBAL/TIMINGS.html#desc_THRESHOLD|GLOBAL%TIMINGS%THRESHOLD]] keyword
  
 +Note that, for the threaded code, only the master thread is instrumented.
 ==== Modifying the timing report ==== ==== Modifying the timing report ====
  
Line 119: Line 120:
  
 As the trace tends to be very verbose, further keywords (TRACE_MAX, TRACE_ROUTINES) allow for restricting the amount of information. As the trace tends to be very verbose, further keywords (TRACE_MAX, TRACE_ROUTINES) allow for restricting the amount of information.
 +
 +===== Using oprofile for sampling based results =====
 +
 +Detailed information, in particular line based, can be obtained with external tools. [[http://oprofile.sourceforge.net/news/ | oprofile ]] is particularly useful, especially from version 0.9.8 onwards. This version allows users to straightforwardly profile their executables. For full details, consult the operf man-page.
 +
 +First, this tool works best with good debug info, so compile your full code using '-O2 -g -fno-omit-frame-pointer' (provided gcc/gfortran is the employed compiler).
 +
 +Next, perform a profiling run, removing any old data first:
 +
 +<code>
 +rm -fR oprofile_data/
 +operf --callgraph ../../../exe/cuda/cp2k.ssmp -i bb.inp -o bb.out
 +</code>
 +
 +After execution, the data can be analyzed in various ways. Three options are particularly useful. The first two provide a general overview, listing the routines that consume most CPU time.
 +
 +<code>
 +opreport --exclude-dependent --demangle=smart --symbols > report.symbols
 +opreport -cg > report.callgraph
 +</code>
 +
 +The output of the first option is rather obvious:
 +
 +<code>
 +CPU: Intel Sandy Bridge microarchitecture, speed 2.001e+06 MHz (estimated)
 +Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (No unit mask) count 90000
 +samples  %        symbol name
 +78561    10.4904  __dbcsr_data_methods_low_MOD_set_data_area_area
 +72344     9.6603  __dbcsr_mm_csr_MOD_dbcsr_mm_csr_multiply_low
 +47158     6.2971  __dbcsr_mm_cudadrv_MOD_dbcsr_mm_cudadrv_process
 +41821     5.5845  __dbcsr_block_access_MOD_dbcsr_remove_block
 +41761     5.5764  __dbcsr_block_operations_MOD_dbcsr_data_copy_aa
 +37830     5.0515  __dbcsr_data_methods_low_MOD_dbcsr_data_get_size
 +19554     2.6111  __dbcsr_message_passing_MOD_mp_alltoall_d11v._omp_fn.3
 +18609     2.4849  __dbcsr_mm_csr_MOD_hash_table_get
 +17633     2.3546  __dbcsr_block_operations_MOD_dbcsr_data_clear0
 +16991     2.2688  __dbcsr_mm_cannon_MOD_make_images._omp_fn.2
 +16982     2.2676  __dbcsr_block_access_MOD_dbcsr_put_block_d
 +15819     2.1123  __dbcsr_methods_MOD_dbcsr_get_data_type_obj
 +15678     2.0935  __dbcsr_iterator_operations_MOD_iterator_advance
 +15382     2.0540  __dbcsr_block_access_MOD_dbcsr_put_block_area
 +</code>
 +
 + for the second the manual needs to be studied :-)
 +
 +The third option delivers line based profiling results, which can pinpoint individual statements as consuming a lot of time:
 +
 +<code>
 +opannotate --source --include-file dbcsr_mm_csr.F > operf.annotate
 +</code>
 +
 +which results in a copy of the source code with lines annotated. However, keep in mind that the annotation is only as good as the quality of the debug info, and that compiler optimization might result in surprising timings.
 +
 +<code>
 +[...]
 +   339  0.0260 :    a_row_cycle: DO a_row_l = mi, mf
 +    85  0.0065 :       m_size = m_sizes(a_row_l)
 +               :
 +   235  0.0180 :       a_row_eps = row_max_epss (a_row_l)
 +   162  0.0124 :       mapped_row_size = row_size_maps(m_size)
 +               :
 +  1728  0.1325 :       a_blk_cycle: DO a_blk = a_row_p(a_row_l)+1, a_row_p(a_row_l+1)
 +   397  0.0304 :          a_col_l = a_blk_info(1, a_blk)
 +               :          IF (debug_mod) WRITE(*,*)ithread,routineN//" A col", a_col_l,";",a_row_l
 +  1104  0.0846 :          k_size = k_sizes (a_col_l)
 +  3194  0.2448 :          mapped_k_size = k_size_maps(k_size)
 +               :
 +   125  0.0096 :          a_norm = left_norms(a_blk)
 +  5763  0.4418 :          b_blk_cycle: DO b_blk = b_row_p(a_col_l)+1, b_row_p(a_col_l+1)
 +               :             IF (dbg) THEN
 +               :                WRITE(*,'(1X,A,3(1X,I7),1X,A,1X,I16)')routineN//" trying B",&
 +               :                     a_row_l, b_blk_info(1,b_blk), a_col_l, "at", b_blk_info(2,b_blk)
 +               :             ENDIF
 +   768  0.0589 :             b_norm = right_norms(b_blk)
 +  8289  0.6354 :             IF (a_norm * b_norm .LT. a_row_eps) THEN
 +               :                CYCLE
 +               :             ENDIF
 +  3073  0.2356 :             b_col_l = b_blk_info(1,b_blk)
 +[...]
 +</code>
 +
 +In principle oprofile output can be converted to kcachegrind readable files, figuring this out is a TODO.
 +
 +===== Valgrind =====
 +
 +In some cases, a very detailed callgraph and timing info is required, and it is better to employ the [[http://valgrind.org/|valgrind]] tool. Valgrind is essentially a simulator, which allows for obtaining exact counts in the callgraph, and details about cache misses, branch mispredictions etc. The disadvantage is two-fold. First, profiling takes a long time (50x slowdown under this simulator is common), and second, it is a simulated architecture which might be (slightly) different from a real CPU (in rare cases, instructions for very new CPUs are not supported. In that case, compile without '-march=native' or optimized (e.g. mkl) libraries).
 +
 +Basic profiling is easy:
 +<code>
 +valgrind --tool=callgrind ./cp2k.sopt -i test.inp -o test.out
 +</code>
 +The result, a file named callgrind.out.XXX, can be visualized with [[http://kcachegrind.sourceforge.net/html/Home.html|kcachegrind]]
 +
 +===== nvprof =====
 +
 +Profiling the CUDA code can be done quite nicely using the nvprof tool. To do so, it is useful to enable user events which requires compiling cp2k with <code> -D__CUDA_PROFILING </code> and linking against <code> -lnvToolsExt </code> library. For the serial code things are easy just run
 +<code>
 +nvprof -o log.nvprof ./cp2k.sopt -i test.inp -o test.out
 +</code>
 +and visualize log.nvprof with the nvvp tool, which might take several minutes to open the data. 
 +
 +An example profile for a linear scaling benchmark (TiO2) is shown here
 +{{ ::screenshot_nvvp_tio2.png?direct&800 | Sample profile from CP2K on TiO2}}
 +
 +To run on CRAY architectures in parallel the following additional tricks are needed
 +<code>
 +export PMI_NO_FORK=1
 +# no cuda proxy
 +# export CRAY_CUDA_MPS=1
 +# use all cores with OMP
 +export OMP_NUM_THREADS=8
 +# use aprun in MPMD mode to have only the output from the master rank (here 169 nodes are used)
 +COMMAND="./cp2k.psmp -i test.inp -o test.out-profile"
 +PART1="-N 1  -n 1 -d ${OMP_NUM_THREADS} nvprof -o log.nvprof ${COMMAND}"
 +PART2="-N 1  -n 168 -d ${OMP_NUM_THREADS} ${COMMAND}"
 +aprun ${PART1} : ${PART2}
 +</code>
  
  
dev/profiling.1372741257.txt.gz · Last modified: 2020/08/21 10:14 (external edit)