├── RunViewConfig ├── schedule.ccl ├── param.ccl ├── interface.ccl ├── configuration.ccl ├── src │ └── make.code.defn ├── README ├── config.pl └── doc │ └── documentation.tex ├── RunView ├── interface.ccl ├── param.ccl ├── configuration.ccl ├── src │ ├── make.code.defn │ ├── util.cc │ ├── util.h │ ├── PatternFinder.h │ ├── timelineSimpleScript.txt │ ├── ctimers.h │ ├── rv_papi.cc │ ├── simpleGraphScript.txt │ ├── rv_papi.h │ ├── ctimers.cc │ ├── PatternFinder.cc │ └── runview.cc ├── README ├── schedule.ccl └── doc │ └── documentation.tex └── README.md /RunViewConfig/schedule.ccl: -------------------------------------------------------------------------------- 1 | # Schedule definitions for thorn RunViewConfig 2 | -------------------------------------------------------------------------------- /RunViewConfig/param.ccl: -------------------------------------------------------------------------------- 1 | # Parameter definitions for thorn RunViewConfig 2 | 3 | shares: IO 4 | 5 | -------------------------------------------------------------------------------- /RunView/interface.ccl: -------------------------------------------------------------------------------- 1 | # Interface definition for thorn RunView 2 | implements: RunView 3 | inherits: 4 | 5 | -------------------------------------------------------------------------------- /RunView/param.ccl: -------------------------------------------------------------------------------- 1 | # Parameter definitions for thorn RunView 2 | 3 | shares: IO 4 | 5 | USES STRING out_dir 6 | -------------------------------------------------------------------------------- /RunViewConfig/interface.ccl: -------------------------------------------------------------------------------- 1 | # Interface definition for thorn RunViewConfig 2 | implements: RunViewConfig 3 | inherits: 4 | 5 | -------------------------------------------------------------------------------- /RunView/configuration.ccl: -------------------------------------------------------------------------------- 1 | # Configuration definitions for thorn RunView 2 | 3 | PROVIDES RunView 4 | { 5 | } 6 | 7 | REQUIRES RunViewConfig 8 | -------------------------------------------------------------------------------- /RunViewConfig/configuration.ccl: -------------------------------------------------------------------------------- 1 | # Configuration definitions for thorn RunView 2 | 3 | PROVIDES RunViewConfig 4 | { 5 | SCRIPT config.pl 6 | LANG perl 7 | } 8 | 9 | -------------------------------------------------------------------------------- /RunViewConfig/src/make.code.defn: -------------------------------------------------------------------------------- 1 | # Main make.code.defn file for thorn RunView -*- makefile -*- 2 | 3 | # Source files in this directory 4 | SRCS = 5 | 6 | # Subdirectories containing source files 7 | SUBDIRS = 8 | 9 | -------------------------------------------------------------------------------- /RunView/src/make.code.defn: -------------------------------------------------------------------------------- 1 | # Main make.code.defn file for thorn RunView -*- makefile -*- 2 | 3 | # Source files in this directory 4 | SRCS = runview.cc util.cc ctimers.cc rv_papi.cc PatternFinder.cc 5 | 6 | # Subdirectories containing source files 7 | SUBDIRS = 8 | -------------------------------------------------------------------------------- /RunView/README: -------------------------------------------------------------------------------- 1 | Cactus Code Thorn RunView 2 | Author(s) : The Chemora Group 3 | Maintainer(s): The Chemora Group 4 | Licence : GPL 5 | -------------------------------------------------------------------------- 6 | 7 | 1. Purpose 8 | 9 | not documented 10 | -------------------------------------------------------------------------------- /RunView/schedule.ccl: -------------------------------------------------------------------------------- 1 | # Schedule definitions for thorn RunView 2 | 3 | 4 | SCHEDULE RunView_init at CCTK_STARTUP 5 | { 6 | LANG: C 7 | } "Initialize performance data collection." 8 | 9 | SCHEDULE RunView_atend in CCTK_SHUTDOWN 10 | { 11 | LANG: C 12 | } "Report performance information." 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RunView 2 | A tool to provide visual performance feedback about a Cactus application. 3 | 4 | Use the code block below to include RunView in your thornlist. 5 | 6 | ``` 7 | # RunView 8 | !TARGET = $ARR 9 | !TYPE = git 10 | !URL = https://github.com/stevenrbrandt/RunView.git 11 | !REPO_PATH = $2 12 | !CHECKOUT = 13 | RunView/RunView 14 | RunView/RunViewConfig 15 | ``` 16 | -------------------------------------------------------------------------------- /RunViewConfig/README: -------------------------------------------------------------------------------- 1 | Cactus Code Thorn RunViewConfig 2 | Author(s) : The Chemora Group 3 | Maintainer(s): The Chemora Group 4 | Licence : 5 | -------------------------------------------------------------------------- 6 | 7 | 1. Purpose 8 | 9 | Provides build information to the RunView thorn. As of this writing, 10 | that build information includes a definition that indicates the 11 | presense of the PAPI library. 12 | -------------------------------------------------------------------------------- /RunViewConfig/config.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -W 2 | 3 | $r = int( rand 2000000000 ); 4 | 5 | $tfile = "/tmp/runview-papi-$$-$r.cc"; 6 | 7 | open T, ">$tfile" || die "Could not open $tfile for output.\n"; 8 | 9 | print T <<"EOS;"; 10 | #include 11 | int main(int argc, char **argv) 12 | { PAPI_library_init(PAPI_VER_CURRENT); return 1; } 13 | EOS; 14 | close T; 15 | 16 | $cmd = "g++ $tfile -o /dev/null -lpapi &> /dev/null "; 17 | $exit_status = system $cmd; 18 | $exit_val = $exit_status / 256; 19 | 20 | unlink $tfile; 21 | 22 | print <<"EOS;" if $exit_val == 0; 23 | BEGIN MAKE_DEFINITION 24 | CXXFLAGS += -DHAVE_PAPI 25 | END MAKE_DEFINITION 26 | LIBRARY -lpapi 27 | EOS; 28 | 29 | -------------------------------------------------------------------------------- /RunView/src/util.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | Strings 6 | split(const char *cs, char sep) 7 | { 8 | vector rv; 9 | rv.push_back(string("")); 10 | while ( const char c = *cs++ ) 11 | if ( c == sep ) rv.push_back(string("")); else rv.back() += c; 12 | 13 | return rv; 14 | } 15 | 16 | string 17 | escapeForXML(const string&& s) 18 | { 19 | // This function based on a namesake in carpet/Timers/src/TimerTree.cc. 20 | // See that file for copyright and license. 21 | string rv; 22 | for ( char c: s ) 23 | switch ( c ) { 24 | case '\'': rv += "'"; break; 25 | case '"': rv += """; break; 26 | case '&': rv += "&"; break; 27 | case '<': rv += "<"; break; 28 | case '>': rv += ">"; break; 29 | default: rv += c; break; } 30 | return rv; 31 | } 32 | -------------------------------------------------------------------------------- /RunView/src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNVIEW_UTIL_H 2 | #define RUNVIEW_UTIL_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template bool 9 | set_min(T& lhs, T rhs) 10 | { if ( rhs < lhs ) { lhs = rhs; return true; } return false; } 11 | template bool 12 | set_max(T1& lhs, T2 rhs) 13 | { if ( rhs > lhs ) { lhs = rhs; return true; } return false; } 14 | 15 | typedef std::vector Strings; 16 | 17 | Strings split(const char *cs, char sep); 18 | 19 | std::string escapeForXML(const std::string&& s); 20 | 21 | inline double 22 | time_wall_fp() 23 | { 24 | struct timespec now; 25 | clock_gettime(CLOCK_REALTIME,&now); 26 | return now.tv_sec + ((double)now.tv_nsec) * 1e-9; 27 | } 28 | 29 | inline int64_t 30 | time_wall_ns() 31 | { 32 | struct timespec now; 33 | clock_gettime(CLOCK_REALTIME,&now); 34 | return 1000000000 * int64_t(now.tv_sec) + now.tv_nsec; 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /RunView/src/PatternFinder.h: -------------------------------------------------------------------------------- 1 | // Anna Neshyba, this is the header file for PatternFinder, c++ 2 | 3 | #ifndef PATTERNFINER_H 4 | #define PATTERNFINER_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ctimers.h" 10 | 11 | using namespace std; 12 | 13 | class PatternFinder { 14 | public: 15 | PatternFinder 16 | (vector event_indices, vector& events, 17 | int patternLength); 18 | vector& events; 19 | vector list; 20 | int list_size; 21 | vector patterns; 22 | vector patternTracker; 23 | vector repeats; 24 | vector instance_next; 25 | int pattern_len_limit; 26 | int pattern_start_pidx; 27 | int pattern_repeats; 28 | void findPatterns(); 29 | void findRepeatNums(); 30 | void buildVector(); 31 | void sort(); 32 | vector getPatterns(); 33 | vector getPatternTracker(); 34 | vector getRepeatNums(); 35 | vector getBackPairs(size_t list_idx); 36 | 37 | }; 38 | 39 | const int pattern_finder_tshift = 5; 40 | 41 | 42 | inline int PatternFinderEncodeStart(int timer_idx, int level) 43 | { 44 | assert( level < (1 << pattern_finder_tshift - 1 ) ); 45 | return ( timer_idx << pattern_finder_tshift ) + ( level << 1 ); 46 | } 47 | inline int PatternFinderEncodeStop(int timer_idx, int level) 48 | { 49 | return ( timer_idx << pattern_finder_tshift ) + ( level << 1 ) + 1; 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /RunView/src/timelineSimpleScript.txt: -------------------------------------------------------------------------------- 1 | 35 | 36 | -------------------------------------------------------------------------------- /RunView/src/ctimers.h: -------------------------------------------------------------------------------- 1 | // -*- c++ -*- 2 | #ifndef CTIMERS_H 3 | #define CTIMERS_H 4 | 5 | #include 6 | #include "rv_papi.h" 7 | 8 | enum RV_Event_Type 9 | { RET_Unset, RET_Timer_Start, RET_Timer_Stop, RET_Timer_Reset, RET_ENUM_SIZE}; 10 | 11 | struct RV_Timer_Event { 12 | RV_Timer_Event(RV_PAPI_Sample *ps, int idx, RV_Event_Type et); 13 | RV_Timer_Event(int64_t etime, int idx, RV_Event_Type et); 14 | union { 15 | RV_PAPI_Sample *papi_sample; // If have_papi_sample true; 16 | int64_t etime; // either cycles or nanoseconds. 17 | }; 18 | papi_long etime_get() const 19 | { return have_papi_sample ? papi_sample->cyc : etime; } 20 | double etime_s_get() const; 21 | const int timer_index; 22 | const RV_Event_Type event_type; 23 | const bool have_papi_sample; 24 | }; 25 | 26 | class RV_CTimer { 27 | public: 28 | RV_CTimer() 29 | :event_last(RET_Unset), event_count(RET_ENUM_SIZE), duration_s(0), 30 | h_last_start_event(NULL), u_last_start_event(NULL), n_papi_samples(0), 31 | missequence_count(0){} 32 | RV_Event_Type event_last; 33 | std::vector event_count; 34 | double duration_s; 35 | int64_t duration; // Either cycles or nanoseconds. 36 | const RV_Timer_Event *h_last_start_event, *u_last_start_event; 37 | RV_Event_Type papi_event_last; 38 | int n_papi_samples; 39 | RV_PAPI_Sample papi_sample_at_start; 40 | RV_PAPI_Sample papi_sample; 41 | int missequence_count; // E.g., double start, stop when not running. 42 | }; 43 | 44 | class RV_CTimers { 45 | public: 46 | RV_CTimers(){ 47 | num_events = 0; n_papi_samples = 0; pm = NULL; 48 | event_sec_per_unit = 0; 49 | } 50 | 51 | void init(); 52 | void papi_init(std::vector papi_events); 53 | void handle_timer_event(int timer_index, RV_Event_Type et); 54 | void timers_update(); 55 | RV_CTimer& get(int cactus_timer_index); 56 | double event_sec_per_unit_get(){ return event_sec_per_unit; } 57 | 58 | std::vector events; 59 | int64_t epoch_ns; 60 | int n_papi_samples; // Number of RV_PAPI_Sample pointers in events. 61 | private: 62 | std::vector timers; 63 | RV_PAPI_Manager* pm; 64 | double event_sec_per_unit; 65 | size_t num_events; // Number of events reflected in timers. See timers_update. 66 | }; 67 | 68 | extern RV_CTimers rv_ctimers; 69 | 70 | inline double 71 | RV_Timer_Event::etime_s_get() const 72 | { return rv_ctimers.event_sec_per_unit_get() * etime_get(); } 73 | 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /RunView/src/rv_papi.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "rv_papi.h" 7 | 8 | using namespace std; 9 | 10 | void overflow_handler(int eset, void *pc, papi_long ovector, void *ctx); 11 | 12 | RV_PAPI_Manager rv_papi_manager; 13 | 14 | void 15 | RV_PAPI_Manager::setup(vector eventsp) 16 | { 17 | periodic_period = 0; 18 | 19 | # ifdef HAVE_PAPI 20 | 21 | PAPI_library_init(PAPI_VER_CURRENT); 22 | 23 | ncounters = PAPI_num_counters(); 24 | ncallbacks = 0; 25 | 26 | // Low-Level API 27 | PE( PAPI_create_eventset( &eset ) ); 28 | 29 | for ( auto e: eventsp ) 30 | { 31 | const int rv = PAPI_add_event( eset, e ); 32 | if ( rv >= PAPI_OK ) continue; 33 | PAPI_event_info_t ei; 34 | PE( PAPI_get_event_info( e, &ei ) ); 35 | CCTK_VInfo 36 | (CCTK_THORNSTRING, 37 | "Could not initialize PAPI event %#x %s: %s\n", 38 | e, ei.symbol, PAPI_strerror(rv)); 39 | } 40 | 41 | if ( periodic_period ) 42 | PE( PAPI_overflow 43 | ( eset, PAPI_TOT_INS, periodic_period, 0, overflow_handler) ); 44 | 45 | int n2 = n_events = PAPI_num_events( eset ); 46 | events.resize(n_events); 47 | PE( PAPI_list_events( eset, events.data(), &n2 ) ); 48 | assert( n_events == n2 ); 49 | 50 | for ( int i=0; i ebad; 53 | for ( auto e: eventsp ) 54 | if ( event_to_idx.find(e) == event_to_idx.end() ) ebad.push_back(e); 55 | 56 | if ( periodic_period ) 57 | samples.reserve((1+n_events)*100); 58 | 59 | PE( PAPI_start( eset ) ); 60 | cyc_start = PAPI_get_real_cyc(); 61 | 62 | # endif 63 | } 64 | 65 | double 66 | RV_PAPI_Manager::cpu_clock_max_hz_get() 67 | { 68 | # ifdef HAVE_PAPI 69 | const PAPI_hw_info_t* const hw_info = PAPI_get_hardware_info(); 70 | if ( !hw_info ) return 0; 71 | return hw_info->cpu_max_mhz * 1e6; 72 | # else 73 | // Actually, there are many ways to get the clock without papi, 74 | // but ATM this function is only called when PAPI is available. 75 | return 0; 76 | # endif 77 | } 78 | 79 | void 80 | RV_PAPI_Manager::callback(int eset, void *pc) 81 | { 82 | samples.push_back(PAPI_get_real_cyc()); 83 | const size_t n_s = samples.size(); 84 | if ( n_s + n_events > samples.capacity() ) 85 | samples.reserve( n_s + 100 * ( 1 + n_events ) ); 86 | samples.resize(n_s+n_events); 87 | PE( PAPI_accum(eset, samples.data() + n_s ) ); 88 | } 89 | 90 | void 91 | RV_PAPI_Manager::destroy() 92 | { 93 | PE( PAPI_cleanup_eventset( eset ) ); eset = PAPI_NULL; 94 | } 95 | 96 | void 97 | overflow_handler(int eset, void *pc, papi_long ovector, void *ctx) 98 | { 99 | rv_papi_manager.callback(eset,pc); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /RunView/src/simpleGraphScript.txt: -------------------------------------------------------------------------------- 1 | 78 | -------------------------------------------------------------------------------- /RunView/src/rv_papi.h: -------------------------------------------------------------------------------- 1 | // -*- c++ -*- 2 | 3 | #ifndef RV_PAPI_H 4 | #define RV_PAPI_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef long long papi_long; 12 | 13 | #ifdef HAVE_PAPI 14 | 15 | #include 16 | 17 | #define PE( c ) \ 18 | { \ 19 | const int __pe_rv = c; \ 20 | if ( __pe_rv < PAPI_OK ) \ 21 | CCTK_VWarn \ 22 | (CCTK_WARN_ABORT, __LINE__,__FILE__, CCTK_THORNSTRING, \ 23 | "Internal error using PAPI API: %s\n", PAPI_strerror(__pe_rv) ); \ 24 | } 25 | #else 26 | 27 | #define PE( c ) 28 | inline papi_long PAPI_get_real_cyc(){ return 0; } 29 | 30 | // WARNING: Values are meaningless. They are here to avoid compilation errors. 31 | enum { PAPI_OK = 0, PAPI_NULL = 0, 32 | PAPI_TOT_INS, PAPI_L3_TCM, PAPI_PRF_DM, PAPI_TLB_DM, PAPI_STL_CCY, PAPI_FUL_CCY }; 33 | 34 | #endif 35 | 36 | class RV_PAPI_Sample { 37 | public: 38 | RV_PAPI_Sample():cyc(0){}; 39 | RV_PAPI_Sample(papi_long c, int n_events):cyc(c),values(n_events){}; 40 | RV_PAPI_Sample(const RV_PAPI_Sample& s):cyc(s.cyc),values(s.values){}; 41 | RV_PAPI_Sample(papi_long c, std::vector&& vals): 42 | cyc(c),values(vals){}; 43 | bool filled() { return !values.empty(); } 44 | inline bool available(int papi_event) const; 45 | inline papi_long operator [](int papi_event) const; 46 | papi_long* values_ptr() { return values.data(); } 47 | void operator +=(const RV_PAPI_Sample& s) 48 | { 49 | if ( s.values.empty() ) return; 50 | if ( values.empty() ) { *this = s; return; } 51 | cyc += s.cyc; 52 | for ( int i=0; i values; 73 | }; 74 | 75 | 76 | class RV_PAPI_Manager { 77 | public: 78 | 79 | RV_PAPI_Manager(){ eset = PAPI_NULL; ncounters = -1; } 80 | 81 | int ncounters; 82 | int eset; 83 | std::vector events; 84 | std::map event_to_idx; 85 | int n_events; 86 | 87 | papi_long cyc_start; 88 | 89 | int n_samples; 90 | std::vector samples; 91 | 92 | int periodic_period; 93 | 94 | void setup(std::vector eventsp); 95 | 96 | RV_PAPI_Sample sample_get() 97 | { 98 | RV_PAPI_Sample rv(PAPI_get_real_cyc()-cyc_start,n_events); 99 | PE( PAPI_read(eset, rv.values_ptr()) ); 100 | return rv; 101 | } 102 | 103 | double cpu_clock_max_hz_get(); 104 | 105 | int ncallbacks; 106 | void callback(int eset, void *pc); 107 | void destroy(); 108 | 109 | }; 110 | 111 | extern RV_PAPI_Manager rv_papi_manager; 112 | 113 | inline papi_long 114 | RV_PAPI_Sample::operator [](int papi_event) const 115 | { 116 | if ( !available(papi_event) ) return 0; 117 | auto idxi = rv_papi_manager.event_to_idx.find(papi_event); 118 | return values[idxi->second]; 119 | } 120 | 121 | inline bool 122 | RV_PAPI_Sample::available(int papi_event) const 123 | { 124 | if ( values.empty() ) return false; 125 | auto idxi = rv_papi_manager.event_to_idx.find(papi_event); 126 | return idxi != rv_papi_manager.event_to_idx.end(); 127 | } 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /RunView/doc/documentation.tex: -------------------------------------------------------------------------------- 1 | % *======================================================================* 2 | % Cactus Thorn template for ThornGuide documentation 3 | % Author: Ian Kelley 4 | % Date: Sun Jun 02, 2002 5 | % $Header$ 6 | % 7 | % Thorn documentation in the latex file doc/documentation.tex 8 | % will be included in ThornGuides built with the Cactus make system. 9 | % The scripts employed by the make system automatically include 10 | % pages about variables, parameters and scheduling parsed from the 11 | % relevant thorn CCL files. 12 | % 13 | % This template contains guidelines which help to assure that your 14 | % documentation will be correctly added to ThornGuides. More 15 | % information is available in the Cactus UsersGuide. 16 | % 17 | % Guidelines: 18 | % - Do not change anything before the line 19 | % % START CACTUS THORNGUIDE", 20 | % except for filling in the title, author, date, etc. fields. 21 | % - Each of these fields should only be on ONE line. 22 | % - Author names should be separated with a \\ or a comma. 23 | % - You can define your own macros, but they must appear after 24 | % the START CACTUS THORNGUIDE line, and must not redefine standard 25 | % latex commands. 26 | % - To avoid name clashes with other thorns, 'labels', 'citations', 27 | % 'references', and 'image' names should conform to the following 28 | % convention: 29 | % ARRANGEMENT_THORN_LABEL 30 | % For example, an image wave.eps in the arrangement CactusWave and 31 | % thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps 32 | % - Graphics should only be included using the graphicx package. 33 | % More specifically, with the "\includegraphics" command. Do 34 | % not specify any graphic file extensions in your .tex file. This 35 | % will allow us to create a PDF version of the ThornGuide 36 | % via pdflatex. 37 | % - References should be included with the latex "\bibitem" command. 38 | % - Use \begin{abstract}...\end{abstract} instead of \abstract{...} 39 | % - Do not use \appendix, instead include any appendices you need as 40 | % standard sections. 41 | % - For the benefit of our Perl scripts, and for future extensions, 42 | % please use simple latex. 43 | % 44 | % *======================================================================* 45 | % 46 | % Example of including a graphic image: 47 | % \begin{figure}[ht] 48 | % \begin{center} 49 | % \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure} 50 | % \end{center} 51 | % \caption{Illustration of this and that} 52 | % \label{MyArrangement_MyThorn_MyLabel} 53 | % \end{figure} 54 | % 55 | % Example of using a label: 56 | % \label{MyArrangement_MyThorn_MyLabel} 57 | % 58 | % Example of a citation: 59 | % \cite{MyArrangement_MyThorn_Author99} 60 | % 61 | % Example of including a reference 62 | % \bibitem{MyArrangement_MyThorn_Author99} 63 | % {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999), 64 | % 1--16. {\tt http://www.nowhere.com/}} 65 | % 66 | % *======================================================================* 67 | 68 | % If you are using CVS use this line to give version information 69 | % $Header$ 70 | 71 | \documentclass{article} 72 | 73 | % Use the Cactus ThornGuide style file 74 | % (Automatically used from Cactus distribution, if you have a 75 | % thorn without the Cactus Flesh download this from the Cactus 76 | % homepage at www.cactuscode.org) 77 | \usepackage{../../../../doc/latex/cactus} 78 | 79 | \begin{document} 80 | 81 | % The author of the documentation 82 | \author{The Chemora Group \textless none\textgreater} 83 | 84 | % The title of the document (not necessarily the name of the Thorn) 85 | \title{RunView} 86 | 87 | % the date your document was last changed, if your document is in CVS, 88 | % please use: 89 | % \date{$ $Date$ $} 90 | \date{May 25 2017} 91 | 92 | \maketitle 93 | 94 | % Do not delete next line 95 | % START CACTUS THORNGUIDE 96 | 97 | % Add all definitions used in this documentation here 98 | % \def\mydef etc 99 | 100 | % Add an abstract for this thorn's documentation 101 | \begin{abstract} 102 | 103 | \end{abstract} 104 | 105 | % The following sections are suggestive only. 106 | % Remove them or add your own. 107 | 108 | \section{Introduction} 109 | 110 | \section{Physical System} 111 | 112 | \section{Numerical Implementation} 113 | 114 | \section{Using This Thorn} 115 | 116 | \subsection{Obtaining This Thorn} 117 | 118 | \subsection{Basic Usage} 119 | 120 | \subsection{Special Behaviour} 121 | 122 | \subsection{Interaction With Other Thorns} 123 | 124 | \subsection{Examples} 125 | 126 | \subsection{Support and Feedback} 127 | 128 | \section{History} 129 | 130 | \subsection{Thorn Source Code} 131 | 132 | \subsection{Thorn Documentation} 133 | 134 | \subsection{Acknowledgements} 135 | 136 | 137 | \begin{thebibliography}{9} 138 | 139 | \end{thebibliography} 140 | 141 | % Do not delete next line 142 | % END CACTUS THORNGUIDE 143 | 144 | \end{document} 145 | -------------------------------------------------------------------------------- /RunViewConfig/doc/documentation.tex: -------------------------------------------------------------------------------- 1 | % *======================================================================* 2 | % Cactus Thorn template for ThornGuide documentation 3 | % Author: Ian Kelley 4 | % Date: Sun Jun 02, 2002 5 | % $Header$ 6 | % 7 | % Thorn documentation in the latex file doc/documentation.tex 8 | % will be included in ThornGuides built with the Cactus make system. 9 | % The scripts employed by the make system automatically include 10 | % pages about variables, parameters and scheduling parsed from the 11 | % relevant thorn CCL files. 12 | % 13 | % This template contains guidelines which help to assure that your 14 | % documentation will be correctly added to ThornGuides. More 15 | % information is available in the Cactus UsersGuide. 16 | % 17 | % Guidelines: 18 | % - Do not change anything before the line 19 | % % START CACTUS THORNGUIDE", 20 | % except for filling in the title, author, date, etc. fields. 21 | % - Each of these fields should only be on ONE line. 22 | % - Author names should be separated with a \\ or a comma. 23 | % - You can define your own macros, but they must appear after 24 | % the START CACTUS THORNGUIDE line, and must not redefine standard 25 | % latex commands. 26 | % - To avoid name clashes with other thorns, 'labels', 'citations', 27 | % 'references', and 'image' names should conform to the following 28 | % convention: 29 | % ARRANGEMENT_THORN_LABEL 30 | % For example, an image wave.eps in the arrangement CactusWave and 31 | % thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps 32 | % - Graphics should only be included using the graphicx package. 33 | % More specifically, with the "\includegraphics" command. Do 34 | % not specify any graphic file extensions in your .tex file. This 35 | % will allow us to create a PDF version of the ThornGuide 36 | % via pdflatex. 37 | % - References should be included with the latex "\bibitem" command. 38 | % - Use \begin{abstract}...\end{abstract} instead of \abstract{...} 39 | % - Do not use \appendix, instead include any appendices you need as 40 | % standard sections. 41 | % - For the benefit of our Perl scripts, and for future extensions, 42 | % please use simple latex. 43 | % 44 | % *======================================================================* 45 | % 46 | % Example of including a graphic image: 47 | % \begin{figure}[ht] 48 | % \begin{center} 49 | % \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure} 50 | % \end{center} 51 | % \caption{Illustration of this and that} 52 | % \label{MyArrangement_MyThorn_MyLabel} 53 | % \end{figure} 54 | % 55 | % Example of using a label: 56 | % \label{MyArrangement_MyThorn_MyLabel} 57 | % 58 | % Example of a citation: 59 | % \cite{MyArrangement_MyThorn_Author99} 60 | % 61 | % Example of including a reference 62 | % \bibitem{MyArrangement_MyThorn_Author99} 63 | % {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999), 64 | % 1--16. {\tt http://www.nowhere.com/}} 65 | % 66 | % *======================================================================* 67 | 68 | % If you are using CVS use this line to give version information 69 | % $Header$ 70 | 71 | \documentclass{article} 72 | 73 | % Use the Cactus ThornGuide style file 74 | % (Automatically used from Cactus distribution, if you have a 75 | % thorn without the Cactus Flesh download this from the Cactus 76 | % homepage at www.cactuscode.org) 77 | \usepackage{../../../../doc/latex/cactus} 78 | 79 | \begin{document} 80 | 81 | % The author of the documentation 82 | \author{The Chemora Group \textless none\textgreater} 83 | 84 | % The title of the document (not necessarily the name of the Thorn) 85 | \title{RunView} 86 | 87 | % the date your document was last changed, if your document is in CVS, 88 | % please use: 89 | % \date{$ $Date$ $} 90 | \date{May 25 2017} 91 | 92 | \maketitle 93 | 94 | % Do not delete next line 95 | % START CACTUS THORNGUIDE 96 | 97 | % Add all definitions used in this documentation here 98 | % \def\mydef etc 99 | 100 | % Add an abstract for this thorn's documentation 101 | \begin{abstract} 102 | 103 | \end{abstract} 104 | 105 | % The following sections are suggestive only. 106 | % Remove them or add your own. 107 | 108 | \section{Introduction} 109 | 110 | \section{Physical System} 111 | 112 | \section{Numerical Implementation} 113 | 114 | \section{Using This Thorn} 115 | 116 | \subsection{Obtaining This Thorn} 117 | 118 | \subsection{Basic Usage} 119 | 120 | \subsection{Special Behaviour} 121 | 122 | \subsection{Interaction With Other Thorns} 123 | 124 | \subsection{Examples} 125 | 126 | \subsection{Support and Feedback} 127 | 128 | \section{History} 129 | 130 | \subsection{Thorn Source Code} 131 | 132 | \subsection{Thorn Documentation} 133 | 134 | \subsection{Acknowledgements} 135 | 136 | 137 | \begin{thebibliography}{9} 138 | 139 | \end{thebibliography} 140 | 141 | % Do not delete next line 142 | % END CACTUS THORNGUIDE 143 | 144 | \end{document} 145 | -------------------------------------------------------------------------------- /RunView/src/ctimers.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rv_papi.h" 3 | #include "ctimers.h" 4 | #include "util.h" 5 | 6 | using namespace std; 7 | 8 | RV_Timer_Event::RV_Timer_Event 9 | (RV_PAPI_Sample *ps, int idx, RV_Event_Type et): 10 | papi_sample(ps), timer_index(idx), event_type(et), have_papi_sample(true){} 11 | RV_Timer_Event::RV_Timer_Event 12 | (int64_t etimep, int idx, RV_Event_Type et): 13 | etime(etimep), timer_index(idx), event_type(et), have_papi_sample(false){} 14 | 15 | void 16 | RV_CTimers::init() 17 | { 18 | assert( this == &rv_ctimers ); 19 | 20 | static const char* const clock_name = "RunView"; 21 | 22 | epoch_ns = time_wall_ns(); 23 | // Changed to clock period if papi_init called. 24 | event_sec_per_unit = 1e-9; 25 | 26 | cClockFuncs funcs; 27 | funcs.n_vals = 1; 28 | 29 | funcs.create = [](int idx) -> void* { return NULL; }; 30 | funcs.destroy = [](int idx, void *data){ }; 31 | funcs.set = [](int idx, void *data, cTimerVal *tv){ assert( false ); }; 32 | funcs.get = [](int idx, void *data, cTimerVal *tv) 33 | { 34 | rv_ctimers.timers_update(); 35 | const RV_CTimer& t = rv_ctimers.timers[idx]; 36 | tv->val.d = tv->seconds = t.duration_s; 37 | tv->type = val_double; 38 | tv->units = "s"; 39 | tv->heading = clock_name; // Must be name of clock. 40 | }; 41 | 42 | #define MAKE_FUNC(et) \ 43 | [](int idx, void *data){ rv_ctimers.handle_timer_event(idx,et); } 44 | funcs.start = MAKE_FUNC(RET_Timer_Start); 45 | funcs.stop = MAKE_FUNC(RET_Timer_Stop); 46 | funcs.reset = MAKE_FUNC(RET_Timer_Reset); 47 | #undef MAKE_FUNC 48 | 49 | funcs.seconds = 50 | [](int idx, void *data, cTimerVal *tv) 51 | { 52 | assert( false ); // Congratulations, you've found a test case! 53 | return 0.0; 54 | }; 55 | 56 | CCTK_ClockRegister(clock_name, &funcs); 57 | } 58 | 59 | void 60 | RV_CTimers::papi_init(vector papi_events) 61 | { 62 | assert( !pm ); 63 | assert(events.empty()); // Because pre-papi events would be ns, post are cyc. 64 | pm = &rv_papi_manager; 65 | pm->setup(papi_events); 66 | const double cpu_clock = pm->cpu_clock_max_hz_get(); 67 | event_sec_per_unit = 1.0 / cpu_clock; 68 | } 69 | 70 | RV_CTimer& 71 | RV_CTimers::get(int cactus_timer_index) 72 | { 73 | timers_update(); 74 | return timers[cactus_timer_index]; 75 | } 76 | 77 | void 78 | RV_CTimers::handle_timer_event(int timer_index, RV_Event_Type et) 79 | { 80 | if ( !pm ) 81 | { 82 | events.emplace_back( time_wall_ns()-epoch_ns, timer_index, et ); 83 | return; 84 | } 85 | 86 | if ( timer_index >= timers.size() ) timers.resize(timer_index+1); 87 | RV_CTimer& t = timers[timer_index]; 88 | 89 | const int limit_p_sample_per_timer = 1000; 90 | const int limit_p_sample_soft = 1; 91 | 92 | const bool event_keeps_sample = 93 | t.h_last_start_event 94 | || t.n_papi_samples < limit_p_sample_per_timer 95 | || n_papi_samples < limit_p_sample_soft; 96 | 97 | RV_PAPI_Sample papi_sample(pm->sample_get()); 98 | 99 | if ( event_keeps_sample ) 100 | { 101 | RV_PAPI_Sample* const ps_ptr = new RV_PAPI_Sample(move(papi_sample)); 102 | events.emplace_back(ps_ptr,timer_index,et); 103 | n_papi_samples++; 104 | t.n_papi_samples++; 105 | t.h_last_start_event = et == RET_Timer_Start ? &events.back() : NULL; 106 | return; 107 | } 108 | 109 | events.emplace_back(papi_sample.cyc, timer_index, et ); 110 | 111 | switch ( et ) { 112 | case RET_Timer_Start: 113 | if ( t.papi_event_last == RET_Timer_Start ) t.missequence_count++; 114 | else t.papi_sample_at_start = move(papi_sample); 115 | break; 116 | case RET_Timer_Stop: 117 | if ( t.papi_event_last != RET_Timer_Start ) t.missequence_count++; 118 | else t.papi_sample.accumulate(papi_sample,t.papi_sample_at_start); 119 | break; 120 | case RET_Timer_Reset: 121 | t.papi_sample.reset(); 122 | break; 123 | default: assert( false ); } 124 | t.papi_event_last = et; 125 | } 126 | 127 | void 128 | RV_CTimers::timers_update() 129 | { 130 | const size_t n_events = events.size(); 131 | if ( n_events == num_events ) return; 132 | const size_t ntimers = CCTK_NumTimers(); 133 | assert( ntimers >= timers.size() ); 134 | timers.resize(ntimers); 135 | while ( num_events < n_events ) 136 | { 137 | const RV_Timer_Event& e = events[num_events++]; 138 | assert( (unsigned int)(e.timer_index) < ntimers ); 139 | RV_CTimer& t = timers[e.timer_index]; 140 | switch ( e.event_type ) { 141 | case RET_Timer_Start: 142 | if ( t.event_last == RET_Timer_Start ) t.missequence_count++; 143 | else t.u_last_start_event = &e; 144 | break; 145 | case RET_Timer_Stop: 146 | if ( t.event_last != RET_Timer_Start ) t.missequence_count++; 147 | else 148 | { 149 | if ( e.have_papi_sample ) 150 | t.papi_sample.accumulate 151 | (*e.papi_sample,*t.u_last_start_event->papi_sample); 152 | else if ( !pm ) 153 | t.duration += e.etime - t.u_last_start_event->etime; 154 | } 155 | break; 156 | case RET_Timer_Reset: t.duration = 0; break; 157 | default: assert( false ); } 158 | t.event_last = e.event_type; 159 | t.event_count[e.event_type]++; 160 | } 161 | for ( auto& t: timers ) 162 | { 163 | if ( pm ) t.duration = t.papi_sample.cyc; 164 | t.duration_s = t.duration * event_sec_per_unit; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /RunView/src/PatternFinder.cc: -------------------------------------------------------------------------------- 1 | // Anna Neshyba, cc file for PatternFinder class, c++ 2 | 3 | #include "PatternFinder.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | // methods 18 | PatternFinder::PatternFinder 19 | (vector v, vector& eventsp, int patternLength): 20 | events(eventsp) 21 | { 22 | pattern_len_limit = patternLength; 23 | list = v; 24 | list_size = v.size(); 25 | for ( int i=0; i PatternFinder::getPatterns() { 32 | return patterns; 33 | } 34 | 35 | vector PatternFinder::getPatternTracker() { 36 | return patternTracker; 37 | } 38 | 39 | vector PatternFinder::getRepeatNums() { 40 | return repeats; 41 | } 42 | 43 | void PatternFinder::findRepeatNums() { 44 | int n = patternTracker.size(); 45 | if (patternTracker[0] == 0) {repeats.push_back(1); } 46 | for (int i = 1; i < n; i++) { 47 | repeats.push_back(patternTracker[i] - patternTracker[i-1]); 48 | } 49 | 50 | 51 | printf("\n repeats: \n" ); 52 | for (int i = 0; i < repeats.size(); i++) { 53 | printf("%d | %d \n", i, repeats[i]); 54 | } 55 | 56 | printf("\n Repeats size: %zd!", repeats.size()); 57 | } 58 | 59 | void PatternFinder::findPatterns() 60 | { 61 | double max_cover = 0; 62 | int max_cover_pidx = -1; 63 | int max_cover_len = -1; 64 | int max_repeats = -1; 65 | 66 | auto submatch = [&](int idx1, int idx2, int len ) 67 | { 68 | for ( int i=0; i run_start; 81 | 82 | const int levmask = ( 1 << pattern_finder_tshift ) - 1; 83 | 84 | auto get_level = [&](int a) { return ( a & levmask ) >> 1 ; }; 85 | auto is_start = [&](int a) { return ! ( a & 1 ); }; 86 | auto get_duration = [&](int pidx, int plen) 87 | { 88 | const int idx = patterns[pidx]; 89 | return events[idx+plen].etime_get() - events[idx].etime_get(); 90 | }; 91 | 92 | const double duration = events.back().etime_get() - events[0].etime_get(); 93 | 94 | for ( int i=1; i level_start ) continue; 111 | prefix = j+1; 112 | } 113 | 114 | int nearest_pidx = i; 115 | 116 | while ( !run_start.empty() && run_start.back().len > prefix ) 117 | { 118 | Run_Start done_run = run_start.back(); 119 | run_start.pop_back(); 120 | assert( done_run.pidx < nearest_pidx ); 121 | nearest_pidx = done_run.pidx; 122 | 123 | const double duration_n = get_duration(done_run.pidx,done_run.len); 124 | const int repeats = i - done_run.pidx; 125 | const double cover = duration_n * repeats; 126 | 127 | assert( cover > 0 ); 128 | if ( cover > max_cover && !done_run.overlap ) 129 | { 130 | max_cover = cover; 131 | max_repeats = repeats; 132 | max_cover_pidx = done_run.pidx; 133 | max_cover_len = done_run.len; 134 | if ( true ) 135 | printf("Max cover %7.3f%%, idx %d, len %d, rpt %d\n", 136 | 100.0 * max_cover / duration, 137 | max_cover_pidx, max_cover_len, 138 | max_repeats); 139 | } 140 | } 141 | 142 | if ( prefix > 0 143 | && ( run_start.empty() || run_start.back().len < prefix ) ) 144 | { 145 | bool overlap = false; 146 | for ( int pl = 1; pl <= prefix/2 && !overlap; pl++ ) 147 | overlap = submatch(idx1,idx1+prefix-pl,pl); 148 | run_start.emplace_back( nearest_pidx, prefix, overlap ); 149 | } 150 | } 151 | 152 | pattern_start_pidx = max_cover_pidx; 153 | pattern_repeats = max_repeats; 154 | 155 | instance_next.resize(list_size,-1); 156 | 157 | vector pidx_sorted; 158 | for ( int i=0; i curr_st_lidx ); 169 | for ( int j=0; j 182 | PatternFinder::getBackPairs(size_t list_idx) 183 | { 184 | int idx = list_idx; 185 | vector back_pairs; 186 | assert( idx < list.size() ); 187 | while ( idx >= 0 ) 188 | { 189 | back_pairs.push_back( idx ); 190 | idx = instance_next[idx]; 191 | } 192 | return move(back_pairs); 193 | } 194 | 195 | #if 0 196 | void PatternFinder::findPatterns(int patternLength) { 197 | int n = patterns.size(); 198 | int tracker = 0; 199 | int counter = 0; 200 | 201 | while (true) { 202 | int start = patterns[tracker]; 203 | vector currPat; 204 | for (int i = start; i < start + patternLength; i++) { 205 | if (i > list.size() - 1) {break;} 206 | currPat.push_back(list[i]); 207 | } 208 | 209 | while (true) { 210 | vector comPat; 211 | for (int i = patterns[tracker+1]; i < patterns[tracker+1]+patternLength; i++) { 212 | if (i > list.size() - 1) {break;} 213 | comPat.push_back(list[i]); 214 | } 215 | 216 | if (currPat == comPat) { 217 | tracker++; 218 | if (tracker > n-1) {patternTracker.push_back(tracker); break;} 219 | } else { 220 | patternTracker.push_back(tracker); 221 | tracker++; 222 | break; 223 | } 224 | } 225 | if (tracker > n-1) break; 226 | } 227 | 228 | 229 | printf("\n Pattern Tracker: \n" ); 230 | for (int i = 0; i < patternTracker.size(); i++) { 231 | printf("%d | %d \n", i, patternTracker[i]); 232 | } 233 | 234 | printf("\n Pattern Tracker size: %d \n", patternTracker.size()); 235 | 236 | 237 | } 238 | #endif 239 | 240 | void PatternFinder::buildVector() { 241 | for ( int i=0; i* elm = &list; 252 | while (!elm->empty()) { 253 | patterns.push_back(elm); 254 | // Checking to see if elm is being pushed correctly 255 | // It seems to be! 256 | /* 257 | for (int i = 0; i < patterns.back()->size(); i++) { 258 | printf(" %d", patterns.back()->at(i)); 259 | } 260 | printf("\n"); 261 | */ 262 | elm->pop_back(); 263 | } 264 | 265 | // DMK: This loop stores copies of "list" in "patterns" and so would 266 | // work correctly. But it would be slow because of the amount of copying 267 | // that needs to be done. For this code to work patterns must 268 | // be declared type vector >. 269 | vector elm = list; 270 | while (!elm.empty()) { 271 | patterns.push_back(elm); 272 | elm.pop_back(); 273 | } 274 | // 275 | // DMK: It would work except for one thing: To find repeating 276 | // patterns we need to sort the suffixes of list, but the code 277 | // immediately above stores prefixes. 278 | 279 | 280 | #endif 281 | } 282 | 283 | void PatternFinder::sort() { 284 | 285 | auto compare = [&](const int a, const int b) 286 | { 287 | if ( a == b ) return true; 288 | 289 | if ( a < b ) { 290 | 291 | for (int i = b; i < b+pattern_len_limit; i++) { 292 | const int aidx = i - b + a; 293 | if ( list[aidx] == list[i] ) continue; 294 | return list[aidx] < list[i]; 295 | } 296 | return true; 297 | 298 | } else { 299 | 300 | for (int i = a; i < a+pattern_len_limit; i++) { 301 | const int bidx = i - a + b; 302 | if ( list[i] < list[bidx] ) { 303 | return true; 304 | } else if ( list[bidx] < list[i] ) { 305 | return false; 306 | } 307 | } 308 | return false; 309 | 310 | } 311 | return false; // Unreachable 312 | }; 313 | 314 | // Testing 315 | 316 | /* vector c = {1,2,3,5}; 317 | vector e = {1,2,3}; 318 | vector d = {3,4,5}; 319 | vector f = {5, 1, 7}; 320 | vector g = {2,3,10}; 321 | vector h = {2,3,11}; 322 | vector> all = {d,h,e,c,f,g}; 323 | 324 | for (int i = 0; i < all.size(); i++) { 325 | for (int j = i+1; j < all.size(); j++) { 326 | int com = compare(all.at(i), all.at(j)); 327 | printf("\n"); 328 | } 329 | } 330 | 331 | */ 332 | /* 333 | printf("--------BEFORE SORT-------- \n"); 334 | 335 | for (int i = 0; i < patterns.size(); i++) { 336 | printf("%d | ", i); 337 | for (int j = patterns.at(i); j < n; j++) { 338 | printf(" %d ", list[j] ); 339 | } 340 | printf("\n"); 341 | } 342 | */ 343 | printf("\n"); 344 | std::sort(patterns.begin(), patterns.end(), compare); 345 | 346 | /* 347 | printf("\n--------AFTER SORT-------- \n"); 348 | 349 | for (int i = 0; i < patterns.size(); i++) { 350 | printf("%d | ", i); 351 | for (int j = patterns.at(i); j < n; j++) { 352 | printf(" %d ", list[j] ); 353 | } 354 | printf("\n"); 355 | } 356 | */ 357 | printf("\n Patterns size: %zd\n", patterns.size()); 358 | 359 | } 360 | 361 | -------------------------------------------------------------------------------- /RunView/src/runview.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "ctimers.h" 14 | #include "util.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "rv_papi.h" 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace std; 26 | 27 | string getfilepath(string fN) 28 | { 29 | char result[ PATH_MAX ]; 30 | ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX ); 31 | string exePath = string(result, (count > 0) ? count : 0); 32 | 33 | // redirect to directory holding javascript file 34 | size_t found = exePath.find("exe"); 35 | string fileName = exePath.substr(0,found) + 36 | "arrangements/RunView/RunView/src/" + fN; 37 | 38 | return fileName; 39 | } 40 | 41 | void 42 | copy_to_server(string path) 43 | { 44 | // Comment out the line below to enable copying to Web server. 45 | return; 46 | const string target_root = "/free/apache/htdocs/rvdemo"; 47 | const char* const target_dn = "ecelsrv3.ece.lsu.edu"; 48 | Strings pieces = split(path.c_str(),'/'); 49 | assert( pieces.size() > 1 ); 50 | string fname = pieces.back(); pieces.pop_back(); 51 | string pname = pieces.back(); 52 | string target_name = pname + "-" + fname; 53 | string target_path = target_root + "/" + target_name; 54 | string cmd = "scp -p " + path + " " + target_dn + ":" + target_path; 55 | pid_t pid = fork(); 56 | if ( pid != 0 ) return; 57 | printf("Command \"%s\"\n",cmd.c_str()); 58 | chmod(path.c_str(),0644); 59 | execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL); 60 | assert( false ); 61 | } 62 | 63 | 64 | class RV_Timer_Node { 65 | public: 66 | RV_Timer_Node(string node_name) 67 | { 68 | dur_node_s = dur_kids_s = 0; 69 | dur_node_set = true; 70 | name = node_name; 71 | level = 0; 72 | id = rand()%10000000; 73 | } 74 | RV_Timer_Node() 75 | { 76 | dur_kids_s = 0; 77 | dur_node_set = false; 78 | level = -1; 79 | id = rand()%10000000; 80 | } 81 | void insert(Strings& path, int levelp, double time, const RV_PAPI_Sample &ps) 82 | { 83 | // Add node at LEVELP for timer described by PATH with duration 84 | // TIME. 85 | // 86 | // Example: 87 | // 88 | // Consider two timers: main/a and main/a/b 89 | // 90 | // Calling root.insert( {main,a}, 0, 10.1 ) 91 | // Adds nodes: 92 | // name: main, level: 0, dur_node_s: not set, dur_kid_s: 10.1 93 | // name: a, level: 1, dur_node_s: 10.1 94 | // 95 | // Calling root.insert( {main,a,b}, 0, 20.2 ) 96 | // Adds new node: 97 | // name: b, level: 2, dur_node_s: 20.2 98 | // And updates exiting node: 99 | // name: a, level: 1, dur_kid_s: 20.2 100 | 101 | if ( name.size() == 0 ) 102 | { 103 | // This is the first time this node has been visited. 104 | // Initialize the level and name components. 105 | level = levelp; 106 | name = path[level-1]; 107 | } 108 | else if ( level ) 109 | { 110 | // This node has been visited before, make sure the names match. 111 | assert( name == path[level-1] ); 112 | } 113 | 114 | // Check if this is the leaf node for the timer. If so, 115 | // record the time and end recursion. 116 | // 117 | if ( path.size() == level ) 118 | { 119 | assert( !dur_node_set ); 120 | dur_node_set = true; 121 | dur_node_s = time; 122 | papi_node = ps; 123 | return; 124 | } 125 | 126 | // Check if this is the parent of the leaf node of the timer. 127 | // If so, collect time of children. 128 | // 129 | if ( path.size() == level + 1 ) 130 | { 131 | dur_kids_s += time; 132 | papi_kids += ps; 133 | } 134 | 135 | // Insert a node for next component. 136 | // 137 | children[path[level]].insert(path,level+1,time,ps); 138 | } 139 | 140 | double dur_node_s; // Duration recorded by Cactus timer. 141 | double dur_kids_s; // Sum of durations of immediate children. 142 | double percent_op; // Percent of parent, set by immediate parent 143 | double percent_pp; // percent of the program runtime that is dedicated to this node 144 | 145 | RV_PAPI_Sample papi_node; 146 | RV_PAPI_Sample papi_kids; 147 | 148 | double pseudo_time_start; // Start time, set for a particular image. 149 | 150 | // Component of timer name. For example, for timer 151 | // main/Evolve/PrintTimers there would be three nodes, named main, 152 | // Evolve, and PrintTimers, at levels 1, 2, and 3, respectively. 153 | string name; 154 | int id; 155 | int level; 156 | 157 | bool dur_node_set; // If true, dur_node_s set to some value. 158 | map children; 159 | }; 160 | 161 | // 162 | // Structure and container for holding information on segments to plot. 163 | // 164 | // There is a segment for each time a timer is stopped. It will be 165 | // plotted as a rectangle with the left side based on the timer 166 | // start time and the right side based on the timer stop time. 167 | // 168 | struct Seg_Info { 169 | Seg_Info(int e_idx, int ti, int l, double ss, double es): 170 | start_event_idx(e_idx), 171 | timer_idx(ti), level(l), start_s(ss), end_s(es),composite(false){}; 172 | int start_event_idx; 173 | int timer_idx, level; 174 | double start_s, end_s; 175 | bool composite; 176 | RV_PAPI_Sample papi; 177 | }; 178 | 179 | 180 | class RV_Data { 181 | public: 182 | RV_Data():timer_tree("root"){}; 183 | void init(); 184 | int atend(); 185 | double generate_rect(double y, double w, double scaler, double Psize, RV_Timer_Node* curr, FILE* fh); 186 | vector generate_segments 187 | (const vector& events, int& max_level); 188 | void generate_text_tree(); 189 | void generate_graph_simple(); 190 | void generate_timeline_simple(); 191 | RV_Timer_Node timer_tree; 192 | int id_serial; 193 | }; 194 | 195 | 196 | RV_Data rv_data; 197 | RV_CTimers rv_ctimers; 198 | 199 | 200 | 201 | CCTK_INT 202 | RunView_init() 203 | { 204 | // Initialize the collection of timer event data 205 | // 206 | rv_data.init(); 207 | 208 | return 0; 209 | } 210 | 211 | CCTK_INT 212 | RunView_atend() 213 | { 214 | 215 | // Print runview timer data. 216 | // 217 | return rv_data.atend(); 218 | } 219 | 220 | void 221 | RV_Data::init() 222 | { 223 | // Initialize the collection of timer event data 224 | // 225 | rv_ctimers.init(); 226 | 227 | // Initialize collection of CPU performance counter data. 228 | // 229 | #ifdef HAVE_PAPI 230 | rv_ctimers.papi_init 231 | ( { PAPI_TOT_INS, PAPI_L3_TCM, PAPI_RES_STL, 232 | PAPI_STL_CCY, PAPI_FUL_CCY } ); 233 | #endif 234 | } 235 | 236 | int 237 | RV_Data::atend() 238 | { 239 | /// Print Timer Data 240 | 241 | DECLARE_CCTK_PARAMETERS; 242 | 243 | // If true, show lots of data. Intended for debugging and 244 | // familiarization. 245 | // 246 | const bool show_all_timers = false; 247 | 248 | string file_path = string(out_dir) + "/run-view-all-timers.txt"; 249 | FILE* const fh = show_all_timers ? fopen(file_path.c_str(),"w") : NULL; 250 | 251 | const int ntimers = CCTK_NumTimers(); 252 | cTimerData* const td = CCTK_TimerCreateData(); 253 | 254 | // Note: Each timer has multiple clocks. In most cases a clock 255 | // measures time and so only one would be necessary. 256 | const int nclocks = CCTK_NumClocks(); 257 | 258 | if ( show_all_timers ) 259 | { 260 | fprintf(fh,"Found %d timers.\n", ntimers); 261 | fprintf(fh,"Found %zd events.\n", rv_ctimers.events.size()); 262 | fprintf(fh,"Allocated %d papi structures.\n",rv_ctimers.n_papi_samples); 263 | for ( int i=0; iheading, t.missequence_count, 300 | t.papi_sample[PAPI_TOT_INS], 301 | name); 302 | } 303 | 304 | // 305 | // Check whether timer is a tree timer. A timer is considered a 306 | // tree timer if its name is of the form 307 | // main/PARTA/PARTB/.../LEAFPAR, where PARTA, etc., consist of 308 | // alphanumeric characters. 309 | // 310 | // For example: 311 | // 312 | // Timer main/CarpetStartup/CheckRegions is a tree timer, its 313 | // leaf name is CheckRegions. 314 | // 315 | // Timer [0017] SymBase: SymBase_Startup in CCTK_STARTUP is not 316 | // a tree timer. 317 | // 318 | // This code assumes that the start and stop events for tree 319 | // timers properly nest and do so as suggested by their 320 | // names. For example, consider timers main/a, main/a/b, and 321 | // main/a/c. The following is a proper nesting: start-main/a, 322 | // start-main/a/b stop-main/a/b, start-main/a/c, stop-main/a/c, 323 | // stop-main/a. The following *is not* a proper nesting: 324 | // start-main/a, start-main/a/b stop-main/a stop-main/a/b, 325 | // start-main/a/c, stop-main/a/c; it's improper because 326 | // stop-main/a occurred while main-a/b was still running. 327 | // 328 | Strings pieces = split(name,'/'); 329 | if ( pieces[0] != "main" ) continue; 330 | timer_tree.insert(pieces,0,t.duration_s,t.papi_sample); 331 | } 332 | 333 | CCTK_TimerDestroyData(td); 334 | 335 | if ( fh ) fclose(fh); 336 | 337 | // Write out text and graphical representations of timer trees. 338 | // 339 | generate_text_tree(); 340 | generate_graph_simple(); 341 | generate_timeline_simple(); 342 | 343 | return 0; 344 | } 345 | 346 | vector 347 | RV_Data::generate_segments 348 | (const vector& events, int& max_level) 349 | { 350 | vector seg_info; 351 | 352 | // Maximum depth of tree. 353 | max_level = 0; 354 | 355 | // List of start events for currently active timers. 356 | // 357 | vector timer_stack = { NULL }; 358 | 359 | for ( int i=0; itimer_index == idx ); 377 | timer_stack.pop_back(); 378 | seg_info.emplace_back 379 | (i,idx,timer_stack.size(),ev_last->etime_s_get(),ev.etime_s_get()); 380 | if ( ev_last->have_papi_sample ) 381 | seg_info.back().papi.accumulate(ev.papi_sample,ev_last->papi_sample); 382 | break; 383 | 384 | case RET_Timer_Reset: break; 385 | 386 | default: assert( false ); 387 | } 388 | } 389 | return move(seg_info); 390 | } 391 | 392 | 393 | double 394 | RV_Data::generate_rect(double y, double w, double scaler, 395 | double Psize, RV_Timer_Node* curr, FILE* fh) 396 | { 397 | double h = Psize *( curr->percent_op); 398 | const double x = curr->level*scaler; 399 | 400 | // Random number for tracking 401 | int name = id_serial++; 402 | 403 | string dNames = to_string(name) + "D"; 404 | string tName = to_string(name) + "TXT"; 405 | string pName = curr->name; 406 | 407 | if (pName.length() > 13) { // shortens print name if larger than 13 characters 408 | pName = pName.substr(0,10); 409 | } 410 | 411 | // Declaring colors 412 | string red; 413 | string green; 414 | string blue = "100"; 415 | 416 | // Declaring values for drawing 417 | double __attribute__((unused)) mpkiC; 418 | double full = 0; 419 | double fullns = 0; 420 | double stall = 0; 421 | 422 | // Color 423 | string fill_color = "rgb(179, 225, 255)"; 424 | 425 | if ( curr->papi_node.filled() ) 426 | { 427 | const papi_long n_insn = 428 | max(papi_long(1),curr->papi_node[PAPI_TOT_INS]); 429 | const papi_long n_cyc = max(papi_long(1),curr->papi_node.cyc); 430 | const double mpki = 1000.0 * curr->papi_node[PAPI_L3_TCM] / n_insn; 431 | 432 | const papi_long n_cyc_stall = curr->papi_node[PAPI_STL_CCY]; 433 | const papi_long n_cyc_full = curr->papi_node[PAPI_FUL_CCY]; 434 | 435 | // Scaling colors based on full and fullns :: EXCEPT IT DOES NOT, CHECK IT OUT 436 | fullns = 100.0 * n_cyc_full / max(papi_long(1),n_cyc-n_cyc_stall); 437 | full = 100.0 * n_cyc_full / n_cyc; 438 | mpkiC = mpki; 439 | stall = 100.00 * double(n_cyc_stall) / n_cyc; 440 | 441 | int r = abs(255*full / 1000 - 255); 442 | int g = abs((255 * fullns) / 100 + 20); 443 | green = to_string(g); 444 | red = to_string(r); 445 | 446 | 447 | fill_color = "rgb(" + red +"," +green+"," + blue + ")"; 448 | } 449 | 450 | 451 | 452 | // Drawing rectangle 453 | fprintf(fh, " \n ", 455 | name, fill_color.c_str(), x, y, w, h); 456 | 457 | // Calculating and drawing bubbles based on stall & rectangle width 458 | if (h > 10) { 459 | double bubble_total_radius = sqrt((mpkiC) * (w*(h-15)) * (4*atan(1))); 460 | 461 | while (bubble_total_radius != 0) { 462 | // establishing radius 463 | double radius; 464 | if ((bubble_total_radius > (w / 3)) || (bubble_total_radius > ((h-15)/3))) { 465 | if (w > h) {radius = (h-15)/3;} 466 | else {radius = w/3;} 467 | bubble_total_radius = bubble_total_radius - radius; 468 | } else { 469 | radius = bubble_total_radius; 470 | bubble_total_radius = 0; 471 | } 472 | 473 | // establishing x and y coordinates 474 | double circle_x = rand()%max(1,int((x+w)-x+1)) + x; 475 | if ((circle_x + radius) > (x+w)) {circle_x = (x+w) - radius;} 476 | else if (circle_x - radius < x) {circle_x = x + radius;} 477 | double circle_y = rand()%max(1,int((y+h)-(y+15)+1)) + (y+15); 478 | if ((circle_y + radius) > (y+h)) {circle_y = (y+h) - radius;} 479 | else if (circle_y - radius < (y+15)) {circle_y = (y+15) + radius;} 480 | 481 | 482 | fprintf(fh, " \n", circle_x, circle_y, radius); 484 | } 485 | } 486 | 487 | // For testing: prints out data in associated rectangle: 488 | /* 489 | if ( h >= 20 ) { 490 | fprintf(fh, " \n" 491 | " %s \n" 492 | "%%PP: %.3f \n" 493 | "%%Stall: %.3f \n" 494 | "%%Full: %.3f \n" 495 | "%%Fullns: %.3f \n" 496 | " \n", tName.c_str(), y+10, x+4, pName.c_str(), x+4, curr->percent_pp*100, 497 | x+4, stall, x+4, full, x+4, fullns); 498 | } else { 499 | fprintf(fh, " \n" 500 | " %s \n" 501 | "%%PP: %.3f \n" 502 | "%%Stall: %.3f \n" 503 | "%%Full: %.3f \n" 504 | "%%Fullns: %.3f \n" 505 | " \n", tName.c_str(), pName.c_str(), curr->percent_pp*100, 506 | stall, full, fullns); 507 | } 508 | */ 509 | 510 | // For regular printing 511 | if ( h >= 20 ) { 512 | fprintf(fh, " \n" 513 | " %s \n" 514 | " \n", tName.c_str(), y+10, x+4, pName.c_str()); 515 | } else { 516 | fprintf(fh, " \n" 517 | " %s \n" 518 | " \n", tName.c_str(), pName.c_str()); 519 | } 520 | 521 | if (curr->children.size() != 0) { 522 | fprintf(fh, " \n", dNames.c_str()); 523 | 524 | // drawing rectangles of children 525 | for (auto& elt: curr->children) { 526 | RV_Timer_Node* const ch = &elt.second; 527 | double t = generate_rect(y,w,scaler,h,ch, fh); 528 | y += t; 529 | } 530 | fprintf(fh, " \n"); 531 | } 532 | 533 | 534 | return h; 535 | } 536 | 537 | 538 | void 539 | RV_Data::generate_text_tree() 540 | { 541 | DECLARE_CCTK_PARAMETERS; 542 | 543 | // Write out a text representation of timer tree. 544 | 545 | string file_path = string(out_dir) + "/run-view-timer-tree.txt"; 546 | FILE* const fh = fopen(file_path.c_str(),"w"); 547 | assert(fh); 548 | 549 | // Put root of tree on stack. 550 | vector stack = { &timer_tree }; 551 | 552 | while ( stack.size() ) 553 | { 554 | RV_Timer_Node* const nd = stack.back(); stack.pop_back(); 555 | 556 | fprintf 557 | (fh, "%10.6f %10.6f %10lld %s %s\n", 558 | nd->dur_node_s, nd->dur_kids_s, nd->papi_node[PAPI_TOT_INS], 559 | string(nd->level*2,' ').c_str(), nd->name.c_str()); 560 | 561 | for ( auto& ch: nd->children ) stack.push_back(&ch.second); 562 | } 563 | 564 | fclose(fh); 565 | } 566 | 567 | void 568 | RV_Data::generate_graph_simple() 569 | { 570 | // Write out SVG image showing timer values. 571 | 572 | // Declare Cactus parameters. (Needed for out_dir.) 573 | DECLARE_CCTK_PARAMETERS; 574 | 575 | string svg_file_path = string(out_dir) + "/run-view.html"; 576 | // 577 | // Note: out_dir is directory in which to write output for this 578 | // Cactus run. 579 | 580 | FILE* const fh = fopen(svg_file_path.c_str(), "w"); 581 | 582 | if ( !fh ) 583 | { 584 | CCTK_VWarn 585 | (CCTK_WARN_ALERT, __LINE__,__FILE__, CCTK_THORNSTRING, 586 | "Could not open RunView output file \"%s\" for output: %s\n", 587 | svg_file_path.c_str(), strerror(errno)); 588 | return; 589 | } 590 | 591 | CCTK_VInfo 592 | (CCTK_THORNSTRING, 593 | "Writing RunView plot to file \"%s\"\n", svg_file_path.c_str()); 594 | // 595 | // Note: This is the correct way to print messages in Cactus. 596 | 597 | const double font_size = 10; // All units in points. 598 | 599 | // Dimension of entire SVG graphic. 600 | // 601 | const double image_wpt = 800; 602 | const double image_hpt = 600; 603 | 604 | // Compute dimensions of plot area. 605 | // 606 | const double plot_area_left_xpt = 5; 607 | const double plot_area_top_ypt = 5; 608 | const double plot_area_wpt = image_wpt - 2 * plot_area_left_xpt; 609 | const double plot_area_hpt = image_hpt - 2 * plot_area_top_ypt; 610 | 611 | // Write SVG Header 612 | // 613 | /* 614 | fprintf(fh,"%s",R"~~( 615 | 617 | )~~"); 618 | 619 | // Set SVG so that one user unit is one point. This is assuming that 620 | // user has adjusted font rendering so that a ten-point font is the 621 | // smallest size that's comfortably readable for substantial amounts 622 | // of text. 623 | // 624 | fprintf(fh,R"~~( 627 | )~~", 628 | image_wpt, image_hpt, image_wpt, image_hpt); 629 | 630 | fprintf(fh,"%s\n","Created by RunView"); 631 | fprintf(fh,R"--( 632 | )--", 633 | font_size); 634 | 635 | */ 636 | // Convenience function for emitting SVG rectangles. 637 | // 638 | 639 | // Write to html file 640 | // Write SVG Header 641 | fprintf(fh,"%s", 642 | " \n \n" 643 | " \n" 644 | "\n" 645 | " \n \n" 646 | "\n" 647 | "\n" 649 | ); 650 | 651 | // Set SVG so that one user unit is one point. This is assuming that 652 | // user has adjusted font rendering so that a ten-point font is the 653 | // smallest size that's comfortably readable for substantial amounts 654 | // of text. 655 | // 656 | fprintf(fh,"\n", 659 | image_wpt, image_hpt, image_wpt, image_hpt); 660 | 661 | fprintf(fh,"%s\n","Created by RunView"); 662 | fprintf(fh,"\n", 663 | font_size); 664 | 665 | // Emit a box around the entire plot area. 666 | // rect( plot_area_left_xpt, plot_area_top_ypt, plot_area_wpt-4, plot_area_hpt); 667 | 668 | double max_time = 0; 669 | int max_level = 0; 670 | 671 | // 672 | // Determine y-axis position, pseudo_time_start, for each node in 673 | // timer tree. The value for pseudo_time_start is computed assuming 674 | // that each timer has one start/stop pair with dur_node_s the 675 | // duration from the start to the stop. Also determine the height 676 | // of the tree. 677 | // 678 | 679 | vector stack = { &timer_tree }; 680 | timer_tree.pseudo_time_start = 0; 681 | 682 | 683 | 684 | // NOT A ROBUST SYSTEM YET. ONLY WORKS IF CHILD OF ROOT IS CALLED MAIN 685 | // WHICH MAY NOT ALWAYS BE THE CASE? 686 | 687 | timer_tree.dur_node_s = timer_tree.dur_kids_s; 688 | RV_Timer_Node* main = &timer_tree.children.find("main")->second; 689 | double total_time = main->dur_kids_s; 690 | main->dur_node_s = main->dur_kids_s; 691 | 692 | while ( stack.size() ) 693 | { 694 | RV_Timer_Node* const nd = stack.back(); stack.pop_back(); 695 | 696 | // Get this node's pseudo_time_start, which was set by its parent. 697 | // 698 | double pseudo_time_start = nd->pseudo_time_start; 699 | 700 | if ( false ) 701 | printf 702 | ("%10.6f %10.6f %10.6f %s %s\n", 703 | nd->dur_node_s, nd->dur_kids_s, pseudo_time_start, 704 | string(nd->level*2,' ').c_str(), nd->name.c_str()); 705 | 706 | nd->percent_pp = ((nd->dur_node_s - nd->dur_kids_s)/total_time); 707 | 708 | // Set the pseudo_time_start members of this node's children. 709 | // 710 | for ( auto& pair: nd->children ) 711 | { 712 | RV_Timer_Node* const ch = &pair.second; 713 | ch->pseudo_time_start = pseudo_time_start; 714 | 715 | // calculating relevant percentages 716 | ch->percent_op = (ch->dur_node_s / nd->dur_node_s); 717 | 718 | 719 | // Push ch on the stack so pseudo_time_start can be set for 720 | // its children. 721 | // 722 | stack.push_back(ch); 723 | 724 | // Get time for the next sibling of ch. 725 | // 726 | pseudo_time_start += ch->dur_node_s; 727 | } 728 | 729 | set_max( max_time, pseudo_time_start ); 730 | set_max( max_level, nd->level ); 731 | } 732 | 733 | main->dur_node_s = 0; 734 | 735 | // Compute scale factors 736 | // 737 | // Level to points. 738 | const double level_to_pt = plot_area_wpt / ( max_level + 1 ); 739 | 740 | stack.push_back(&timer_tree); 741 | 742 | // setting up main for drawRectangles function 743 | main->percent_op = 1; 744 | 745 | // draw rectangle around entire space 746 | 747 | fprintf(fh, "\n", plot_area_left_xpt, 749 | plot_area_top_ypt, plot_area_wpt -4, plot_area_hpt); 750 | 751 | 752 | // Draw root rectangle 753 | fprintf(fh, "\n",timer_tree.name.c_str(), 755 | plot_area_left_xpt, plot_area_top_ypt, level_to_pt, plot_area_hpt); 756 | 757 | fprintf(fh, " \n" 758 | " %s \n" 759 | " \n", plot_area_top_ypt+10, plot_area_left_xpt+4, timer_tree.name.c_str()); 760 | 761 | fprintf(fh, " \n"); 762 | 763 | // Draw rectangles 764 | generate_rect(plot_area_top_ypt, level_to_pt,level_to_pt, 765 | plot_area_hpt, main, fh); 766 | 767 | fprintf(fh, "%s", " \n"); 768 | fprintf(fh, "%s", " \n"); 769 | 770 | 771 | // Add javascript features from file 772 | string fN = "simpleGraphScript.txt"; 773 | string fileName = getfilepath(fN); 774 | 775 | // Open and read from file 776 | string line; 777 | std::ifstream scriptFile; 778 | scriptFile.open(fileName.c_str(), std::ifstream::in); 779 | if (scriptFile.is_open()) { 780 | while (!scriptFile.eof()) { 781 | getline(scriptFile, line); 782 | fprintf(fh, "%s \n", line.c_str()); 783 | } 784 | } 785 | 786 | // Close 787 | fprintf(fh, "%s", " \n \n \n"); 788 | 789 | fclose(fh); 790 | 791 | copy_to_server(svg_file_path); 792 | } 793 | 794 | 795 | 796 | void 797 | RV_Data::generate_timeline_simple() 798 | { 799 | DECLARE_CCTK_PARAMETERS; 800 | 801 | string svg_file_path = string(out_dir) + "/run-view-timeline.html"; 802 | FILE* const fh = fopen(svg_file_path.c_str(), "w"); 803 | if ( !fh ) 804 | { 805 | CCTK_VWarn 806 | (CCTK_WARN_ALERT, __LINE__,__FILE__, CCTK_THORNSTRING, 807 | "Could not open RunView output file \"%s\" for output: %s\n", 808 | svg_file_path.c_str(), strerror(errno)); 809 | return; 810 | } 811 | 812 | CCTK_VInfo 813 | (CCTK_THORNSTRING, 814 | "Writing RunView timeline plot to file \"%s\"\n", svg_file_path.c_str()); 815 | 816 | const int ntimers = CCTK_NumTimers(); 817 | 818 | // 819 | // Identify timer-tree timers (participants) and extract their leaf names. 820 | // 821 | // For example: 822 | // 823 | // Timer main/CarpetStartup/CheckRegions is a tree timer, its 824 | // leaf name is CheckRegions. 825 | // 826 | // Timer [0017] SymBase: SymBase_Startup in CCTK_STARTUP is not a tree timer. 827 | // 828 | 829 | vector participant(ntimers); 830 | vector leaf_name(ntimers); 831 | for ( int i=0; i events; 842 | for ( auto& ev: rv_ctimers.events ) 843 | if ( participant[ev.timer_index] ) events.push_back(ev); 844 | vector timer_is_composite(events.size()); 845 | 846 | vector seg_info = generate_segments(events,max_level); 847 | 848 | /// Write SVG Image of Segments 849 | // 850 | 851 | const double font_size = 10; // All units in points. 852 | const double baselineskip_pt = font_size * 1.5; 853 | 854 | // Plot Area (area holding segments) Width 855 | // 856 | const double plot_area_wpt = 800; 857 | 858 | // Compute scale factors. 859 | // 860 | // Seconds to Points 861 | const double s_to_pt = plot_area_wpt / seg_info.back().end_s; 862 | 863 | // Total time 864 | double duration = seg_info.back().end_s; 865 | printf("\n\n duration: %.3f \n \nf", duration); 866 | // 867 | // Level to Point 868 | const double level_ht_lines = 3.5; //5.7 869 | const double level_to_pt = level_ht_lines * baselineskip_pt; 870 | 871 | // Height of a Segment. 872 | const double seg_hpt = level_to_pt; 873 | 874 | // Plot Area (area holding segments) Height 875 | // 876 | const double plot_area_hpt = max_level * level_to_pt * 2; 877 | 878 | const double image_wpt = plot_area_wpt; 879 | const double image_hpt = plot_area_hpt; 880 | 881 | fprintf(fh, "%s", 882 | "\n" 883 | "\n" 884 | " \n" 885 | " \n"); 886 | 887 | 888 | fprintf(fh,"%s", 889 | "\n" 890 | "\n"); 893 | // InfoBox information 894 | fprintf(fh,"\n", 898 | image_wpt, image_wpt); 899 | 900 | const char* const name = getenv("USER") ?: ""; 901 | 902 | fprintf(fh, " \n" 903 | " \n" 905 | " \n" 906 | "Summary of Execution: \n " 907 | "Author: %s \n" 908 | "Duration: %.4f \n " 909 | " \n \n \n", image_wpt, name, duration); 910 | 911 | // Drawing the timeline 912 | double secMarks = floor (duration); 913 | double spaceBtwTicks = ((image_wpt * secMarks) / duration) / secMarks; 914 | 915 | fprintf(fh,"\n", 918 | image_wpt, image_wpt); 919 | 920 | fprintf(fh, " \n" 921 | " \n" 922 | , image_wpt); 923 | 924 | // setting tick mark locations 925 | double xLocation = 0; 926 | 927 | for (int i = 0; i <= secMarks; i++) { 928 | fprintf(fh, " \n", xLocation, xLocation); 929 | 930 | if (i > 0) { 931 | fprintf(fh, " %d \n", xLocation-3, i); 932 | } 933 | 934 | xLocation = xLocation + spaceBtwTicks; 935 | } 936 | 937 | fprintf(fh, " \n "); 938 | 939 | 940 | 941 | // Set SVG so that one user unit is one point. This is assuming that 942 | // user has adjusted font rendering so that a ten-point font is the 943 | // smallest size that's comfortably readable for substantial amounts 944 | // of text. 945 | // 946 | fprintf(fh,"\n", 950 | image_wpt, image_hpt, image_wpt, image_hpt); 951 | 952 | fprintf(fh,"%s\n","Created by RunView"); 953 | 954 | // Make background of plot area gray. 955 | // 956 | fprintf 957 | (fh, 958 | R"~~()~~", 960 | 0.0, 0.0, plot_area_wpt, plot_area_hpt); 961 | 962 | fprintf(fh,R"~~()~~", font_size); 964 | 965 | // Make main rectangle 966 | fprintf 967 | (fh, 968 | "\n", 970 | 0.0, 0.0, plot_area_wpt, seg_hpt); 971 | 972 | fprintf(fh, "Main\n", 973 | 0 + 0.5*font_size, 0 + font_size ); 974 | 975 | 976 | 977 | 978 | // Generate SVG for individual segments. 979 | // Also tracking for pattern recognition 980 | 981 | //typedef std::function& seg_info, bool pattern_plot)> emit_timeline_type; 982 | 983 | //emit_timeline_type 984 | auto emit_timeline = [&] 985 | (double y_offset, vector& seg_info, bool pattern_plot)->void 986 | { 987 | 988 | for ( auto& s: seg_info ) 989 | { 990 | const double xpt = s.start_s * s_to_pt; 991 | const double ypt = s.level * level_to_pt + y_offset; 992 | const double wpt = ( s.end_s - s.start_s ) * s_to_pt; 993 | 994 | string clsName = "timer-" + to_string(s.timer_idx); 995 | 996 | auto emit_rect = [&](string fill, string stroke) 997 | { 998 | fprintf 999 | (fh, 1000 | R"--( 1002 | )--", 1003 | clsName.c_str(), fill.c_str(), stroke.c_str(), 1004 | xpt, ypt, wpt, seg_hpt); 1005 | }; 1006 | 1007 | // Estimate width assuming that character width is font_size/1.2. 1008 | const int width_char = 1.2 * wpt / font_size; 1009 | const string name = 1010 | escapeForXML( leaf_name[s.timer_idx].substr(0,width_char) ); 1011 | 1012 | if ( ! s.papi.filled() ) 1013 | { 1014 | string fill_color = "rgb(179, 225, 255)"; 1015 | emit_rect(fill_color,"white"); 1016 | if ( width_char > 2 ) 1017 | fprintf(fh, R"--(%s)--", 1018 | xpt + 0.5*font_size, ypt + font_size, name.c_str() ); 1019 | continue; 1020 | } 1021 | #ifdef HAVE_PAPI 1022 | 1023 | // Declaring colors 1024 | string red; 1025 | string green; 1026 | string blue = "50"; 1027 | 1028 | const double baselineskip_ypt = font_size * 1.2; 1029 | const double text_limit_ypt = ypt + level_to_pt - baselineskip_ypt; 1030 | double curr_text_ypt = ypt + font_size; 1031 | 1032 | const papi_long n_insn = 1033 | max(papi_long(1),s.papi[PAPI_TOT_INS]); 1034 | const papi_long n_cyc = max(papi_long(1),s.papi.cyc); 1035 | const double mpki = 1000.0 * s.papi[PAPI_L3_TCM] / n_insn; 1036 | 1037 | const papi_long n_cyc_stall = s.papi[PAPI_STL_CCY]; 1038 | const papi_long n_cyc_stall_r = s.papi[PAPI_RES_STL]; 1039 | const papi_long n_cyc_full = s.papi[PAPI_FUL_CCY]; 1040 | 1041 | double fullns = 100.0 * n_cyc_full / max(papi_long(1),n_cyc-n_cyc_stall); 1042 | double full = 100.0 * n_cyc_full / n_cyc; 1043 | double mpkiC = mpki; 1044 | double stall = 100.00 * double(n_cyc_stall) / n_cyc; 1045 | 1046 | int r = abs(255*round(full) / 100 - 255); 1047 | int g = (255 * round(fullns)) / 100; 1048 | green = to_string(g); 1049 | red = to_string(r); 1050 | 1051 | const bool composite = timer_is_composite[s.start_event_idx]; 1052 | const char* const stroke_color = "white"; 1053 | 1054 | // If not part of a pattern draw in gray 1055 | const string fill_color = 1056 | !pattern_plot || composite 1057 | ? "rgb(" + red +"," +green+"," + blue + ")" : "#ccc"; 1058 | 1059 | emit_rect(fill_color,stroke_color); 1060 | 1061 | // Calculating and drawing bubbles based on stall & rectangle width 1062 | if (wpt > 10) { 1063 | double bubble_total_radius = sqrt((mpki) * (wpt*seg_hpt) * (4*atan(1))); 1064 | 1065 | while (bubble_total_radius != 0) { 1066 | // establishing radius 1067 | double radius; 1068 | if ((bubble_total_radius > (wpt / 2.5)) || (bubble_total_radius > (seg_hpt/2.5))) { 1069 | if (wpt > seg_hpt) {radius = seg_hpt/3;} 1070 | else {radius = wpt/3;} 1071 | bubble_total_radius = bubble_total_radius - radius; 1072 | } else { 1073 | radius = bubble_total_radius; 1074 | bubble_total_radius = 0; 1075 | } 1076 | 1077 | // establishing x and y coordinates 1078 | double circle_x = rand()%(int)((xpt+wpt)-xpt+1) + xpt; 1079 | if ((circle_x + radius) > (xpt+wpt)) {circle_x = (xpt+wpt) - radius;} 1080 | else if (circle_x - radius < xpt) {circle_x = xpt + radius;} 1081 | double circle_y = rand()%(int)((ypt+seg_hpt-(ypt+baselineskip_ypt))+1) 1082 | + (ypt+baselineskip_ypt); 1083 | if ((circle_y + radius) > (ypt+seg_hpt)) 1084 | {circle_y = (ypt+seg_hpt) - radius;} 1085 | else if (circle_y - radius < (ypt+baselineskip_ypt)) 1086 | {circle_y = (ypt+baselineskip_ypt) + radius;} 1087 | 1088 | 1089 | fprintf(fh, " \n", circle_x, circle_y, radius); 1092 | } 1093 | } 1094 | 1095 | if ( width_char < 2 ) continue; 1096 | const double text_xpt = xpt + 0.5 * font_size; 1097 | fprintf(fh, R"--(%s)--", 1098 | text_xpt, ypt + font_size, name.c_str() ); 1099 | 1100 | const bool show_values = false; 1101 | 1102 | if ( !show_values ) continue; 1103 | 1104 | if ( curr_text_ypt < text_limit_ypt ) 1105 | fprintf(fh, R"--(L3 %.3f MPKI)--", 1106 | text_xpt, curr_text_ypt += baselineskip_ypt, 1107 | mpki ); 1108 | if ( curr_text_ypt < text_limit_ypt ) 1109 | fprintf(fh, R"--(%.1f IPC)--", 1110 | text_xpt, curr_text_ypt += baselineskip_ypt, 1111 | double(n_insn) / n_cyc ); 1112 | if ( curr_text_ypt < text_limit_ypt ) 1113 | fprintf(fh, R"--(Stallr %.1f%%)--", 1114 | text_xpt, curr_text_ypt += baselineskip_ypt, 1115 | 100.0 * double(n_cyc_stall_r) / n_cyc ); 1116 | 1117 | 1118 | if ( curr_text_ypt < text_limit_ypt ) 1119 | fprintf(fh, R"--(Stall %.1f%%)--", 1120 | text_xpt, curr_text_ypt += baselineskip_ypt, 1121 | 100.0 * double(n_cyc_stall) / n_cyc ); 1122 | if ( curr_text_ypt < text_limit_ypt ) 1123 | fprintf 1124 | (fh, R"--(Full %.1f%%)--", 1125 | text_xpt, curr_text_ypt += baselineskip_ypt, 1126 | 100.0 * n_cyc_full / n_cyc ); 1127 | if ( curr_text_ypt < text_limit_ypt ) 1128 | fprintf 1129 | (fh, R"--(Fullns %.1f%%)--", 1130 | text_xpt, curr_text_ypt += baselineskip_ypt, 1131 | 100.0 * n_cyc_full / max(papi_long(1),n_cyc-n_cyc_stall) ); 1132 | 1133 | #endif 1134 | } 1135 | 1136 | }; 1137 | 1138 | emit_timeline(0,seg_info,false); 1139 | 1140 | vector event_indices; 1141 | { 1142 | int level = 0; 1143 | for ( auto& ev: events ) 1144 | switch ( ev.event_type ) { 1145 | case RET_Timer_Start: 1146 | event_indices.push_back 1147 | ( PatternFinderEncodeStart(ev.timer_index,level) ); 1148 | level++; 1149 | break; 1150 | case RET_Timer_Stop: 1151 | level--; 1152 | assert( level >= 0 ); 1153 | event_indices.push_back 1154 | ( PatternFinderEncodeStop(ev.timer_index,level) ); 1155 | break; 1156 | case RET_Timer_Reset: event_indices.push_back( -2 ); break; 1157 | default: assert( false ); 1158 | } 1159 | } 1160 | 1161 | assert( event_indices.size() == events.size() ); 1162 | 1163 | PatternFinder pf2(move(event_indices), events, 2000); 1164 | 1165 | timer_is_composite.clear(); 1166 | vector timer_compact; 1167 | 1168 | vector events_done(events.size()); 1169 | timer_compact.push_back( events[0] ); 1170 | events_done[0] = true; 1171 | 1172 | for ( int i=1; iaccumulate(curr.papi_sample,prev.papi_sample); 1190 | else 1191 | duration += curr.etime_get() - prev.etime_get(); 1192 | } 1193 | timer_is_composite.push_back( back_pairs.size() > 1 ); 1194 | if ( papi_sample ) 1195 | { 1196 | papi_sample->cyc += duration; 1197 | timer_compact.emplace_back(papi_sample,ev.timer_index,ev.event_type); 1198 | } 1199 | else 1200 | timer_compact.emplace_back 1201 | (duration+ev_last.etime_get(),ev.timer_index,ev.event_type); 1202 | } 1203 | 1204 | vector seg_compact_info = 1205 | generate_segments(timer_compact,max_level); 1206 | 1207 | emit_timeline(level_to_pt*max_level,seg_compact_info,true); 1208 | 1209 | fprintf(fh,"%s","\n"); 1210 | 1211 | 1212 | // Add javascript features from file 1213 | string fN = "timelineSimpleScript.txt"; 1214 | string fileName = getfilepath(fN); 1215 | 1216 | string line; 1217 | std::ifstream scriptFile; 1218 | scriptFile.open(fileName.c_str(), std::ifstream::in); 1219 | if (scriptFile.is_open()) { 1220 | while (!scriptFile.eof()) { 1221 | getline(scriptFile, line); 1222 | fprintf(fh, "%s \n", line.c_str()); 1223 | } 1224 | } 1225 | 1226 | fprintf(fh, "%s"," \n "); 1227 | fclose(fh); 1228 | 1229 | copy_to_server(svg_file_path); 1230 | } 1231 | --------------------------------------------------------------------------------