From def154a8f7cd3893d3c5acca441596c3af2e015d Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 24 Aug 2022 11:41:45 +0100 Subject: [PATCH 001/128] Farting the current output into a hard-coded file (#52) --- src/c++/hashtable.cpp | 28 +++++++++++++--------------- src/c++/hashtable.h | 5 +++-- src/c++/profiler.cpp | 12 +++++++----- src/c++/profiler.h | 5 +++-- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index bf710ae2..722501ea 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -13,7 +13,7 @@ #include /** - * @brief Constructs a new entry in the hash table. + * @brief Constructs a new entry in the hash table. * */ @@ -26,7 +26,7 @@ HashEntry::HashEntry(std::string_view region_name) /** * @brief Hashtable constructor - * + * */ HashTable::HashTable(int const tid) @@ -88,7 +88,7 @@ void HashTable::add_child_time(size_t hash, double time_delta) * */ -void HashTable::write() +void HashTable::write(std::ofstream& outstream) { this->compute_self_times(); @@ -96,30 +96,30 @@ void HashTable::write() std::string routine_at_thread = "Thread: " + std::to_string(tid_); // Write headings - std::cout << "\n"; - std::cout + outstream << "\n"; + outstream << std::setw(40) << std::left << routine_at_thread << " " << std::setw(15) << std::right << "Self (s)" << " " << std::setw(15) << std::right << "Total (s)" << "\n"; - - std::cout << std::setfill('-'); - std::cout + + outstream << std::setfill('-'); + outstream << std::setw(40) << "-" << " " << std::setw(15) << "-" << " " << std::setw(15) << "-" << "\n"; - std::cout << std::setfill(' '); + outstream << std::setfill(' '); // Create a vector from the hashtable and sort the entries according to self // walltime. If optimisation of this is needed, it ought to be possible to // acquire a vector of hash-selftime pairs in the correct order, then use the // hashes to look up other information directly from the hashtable. auto hashvec = std::vector>(begin(table_), end(table_)); - std::sort(begin(hashvec), end(hashvec), + std::sort(begin(hashvec), end(hashvec), [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_;}); - + // Data entries for (auto& [hash, entry] : hashvec) { - std::cout + outstream << std::setw(40) << std::left << entry.region_name_ << " " << std::setw(15) << std::right << entry.self_walltime_ << " " << std::setw(15) << std::right << entry.total_walltime_ << "\n"; @@ -155,12 +155,10 @@ std::vector HashTable::list_keys() /** * @brief Get the total (inclusive) time corresponding to the input hash. - * + * */ double HashTable::get_total_walltime(size_t const hash) { return table_.at(hash).total_walltime_; } - - diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 2000cc80..4f60b5f1 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -28,6 +28,7 @@ #include #include #include +#include /** * @brief Structure to hold information for a particular routine. @@ -60,7 +61,7 @@ struct HashEntry{ */ class HashTable{ - + private: // Members @@ -77,7 +78,7 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; void update(size_t, double); - void write(); + void write(std::ofstream&); // Member functions std::vector list_keys(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 5ec34e87..b1d5e771 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -33,7 +33,7 @@ Profiler::Profiler(){ thread_traceback_.push_back(new_list); } - // Assertions + // Assertions assert ( static_cast (thread_hashtables_.size()) == max_threads_); assert ( static_cast (thread_traceback_.size() ) == max_threads_); @@ -116,17 +116,20 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - // Write each one + // Write each one to file + output_stream.open("profiler-output.txt"); for (auto& it : thread_hashtables_) { - it.write(); + it.write(output_stream); } + output_stream.flush(); + output_stream.close(); } /** * @brief Get the total (inclusive) time of everything below the specified hash. * - * @param[in] hash The hash corresponding to the region of interest. + * @param[in] hash The hash corresponding to the region of interest. * * @note This function is normally expected to be used to return the total * wallclock time for whole run. Since this value is required only from @@ -141,4 +144,3 @@ double Profiler::get_thread0_walltime(size_t const hash) auto tid = static_cast(0); return thread_hashtables_[tid].get_total_walltime(hash); } - diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 9668b6cb..dc0dc7e8 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -11,7 +11,7 @@ * * Contains the top-level class, whose methods are called from client code. Also * declares a top-level, global, profiler object. - * + * */ #ifndef PROFILER_H @@ -35,12 +35,13 @@ class Profiler { - private: + private: // Data members int max_threads_; std::vector thread_hashtables_; std::vector>> thread_traceback_; + std::ofstream output_stream; // Type definitions for vector array indexing. typedef std::vector::size_type hashtable_iterator_t_; From 2ed6823efdde9ed66827efaeab28a00ac35482ed Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 24 Aug 2022 11:41:45 +0100 Subject: [PATCH 002/128] Farting the current output into a hard-coded file (#52) --- src/c++/hashtable.cpp | 20 +++++++++----------- src/c++/hashtable.h | 3 ++- src/c++/profiler.cpp | 7 +++++-- src/c++/profiler.h | 1 + 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 77b48b89..5aeda7cd 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -91,7 +91,7 @@ void HashTable::add_child_time(size_t hash, double time_delta) * */ -void HashTable::write() +void HashTable::write(std::ofstream& outstream) { this->compute_self_times(); @@ -99,20 +99,18 @@ void HashTable::write() std::string routine_at_thread = "Thread: " + std::to_string(tid_); // Write headings - std::cout << "\n"; - std::cout + outstream << "\n"; + outstream << std::setw(40) << std::left << routine_at_thread << " " << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (s)" << " " - << std::setw(10) << std::right << "Calls" << "\n"; + << std::setw(15) << std::right << "Total (s)" << "\n"; - std::cout << std::setfill('-'); - std::cout + outstream << std::setfill('-'); + outstream << std::setw(40) << "-" << " " << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(10) << "-" << "\n"; - std::cout << std::setfill(' '); + << std::setw(15) << "-" << "\n"; + outstream << std::setfill(' '); // Create a vector from the hashtable and sort the entries according to self // walltime. If optimisation of this is needed, it ought to be possible to @@ -124,7 +122,7 @@ void HashTable::write() // Data entries for (auto& [hash, entry] : hashvec) { - std::cout + outstream << std::setw(40) << std::left << entry.region_name_ << " " << std::setw(15) << std::right << entry.self_walltime_ << " " << std::setw(15) << std::right << entry.total_walltime_ << " " diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 7f885e2c..e0c097e1 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -28,6 +28,7 @@ #include #include #include +#include /** * @brief Structure to hold information for a particular routine. @@ -78,7 +79,7 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; void update(size_t, double); - void write(); + void write(std::ofstream&); // Member functions std::vector list_keys(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 24fcde97..f2bef123 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -116,11 +116,14 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - // Write each one + // Write each one to file + output_stream.open("profiler-output.txt"); for (auto& it : thread_hashtables_) { - it.write(); + it.write(output_stream); } + output_stream.flush(); + output_stream.close(); } /** diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 1238d979..aeb58ba9 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -41,6 +41,7 @@ class Profiler int max_threads_; std::vector thread_hashtables_; std::vector>> thread_traceback_; + std::ofstream output_stream; // Type definitions for vector array indexing. typedef std::vector::size_type hashtable_iterator_t_; From e43679313438a05fe6fa4eeac97053f1c7831d84 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 1 Sep 2022 10:38:56 +0100 Subject: [PATCH 003/128] Support for setting the outfile name via an environment variable (#52) --- src/c++/hashtable.cpp | 6 ++++-- src/c++/profiler.cpp | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 5aeda7cd..a180853e 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -103,13 +103,15 @@ void HashTable::write(std::ofstream& outstream) outstream << std::setw(40) << std::left << routine_at_thread << " " << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (s)" << "\n"; + << std::setw(15) << std::right << "Total (s)" << " " + << std::setw(10) << std::right << "Calls" << "\n"; outstream << std::setfill('-'); outstream << std::setw(40) << "-" << " " << std::setw(15) << "-" << " " - << std::setw(15) << "-" << "\n"; + << std::setw(15) << "-" << " " + << std::setw(10) << "-" << "\n"; outstream << std::setfill(' '); // Create a vector from the hashtable and sort the entries according to self diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index f2bef123..bac27b1f 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -110,18 +110,35 @@ void Profiler::stop(size_t const hash) } /** - * @brief Write profile information. + * @brief Write profile information to file. + * + * @note The default file that the profiler will spit information into is + * called "profiler-output.txt". There also exists the option to set a + * custom name via the environment variable "ProfOut". * */ void Profiler::write() { - // Write each one to file - output_stream.open("profiler-output.txt"); + // Pickup environment variable filename if it exists, if not use the default + // name of "profiler-output.txt" + const char* filename = getenv("ProfOut"); + if (filename != NULL) + { + output_stream.open(filename); + } + else + { + output_stream.open("profiler-output.txt"); + delete filename; + } + + // Write to file for (auto& it : thread_hashtables_) { it.write(output_stream); } + output_stream.flush(); output_stream.close(); } From 778b5dee25c492a7b9cce47c433f37086d2d3bbd Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 9 Sep 2022 11:29:58 +0100 Subject: [PATCH 004/128] MPI support, one output file per rank (#52) --- CMakeLists.txt | 6 +++++- src/c++/CMakeLists.txt | 3 ++- src/c++/profiler.cpp | 20 ++++++++++++++------ src/c++/profiler.h | 1 + 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea48fdf9..c7b3dc7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,11 @@ include(cmake/Doxygen.cmake) enable_doxygen() # OpenMP required -find_package(OpenMP 2.5 REQUIRED) +find_package(OpenMP 2.5 REQUIRED) + +# MPI required +find_package(MPI REQUIRED) +include_directories(SYSTEM ${MPI_INCLUDE_PATH}) # Defines some standard install directories such as $(prefix)/include. include(GNUInstallDirs) diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index 18e05ac1..4324398b 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -13,7 +13,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED # Link library to and external libs (also use project warnings and options). target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE - OpenMP::OpenMP_CXX) + OpenMP::OpenMP_CXX + MPI::MPI_CXX) set_project_warnings(${CMAKE_PROJECT_NAME}) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index bac27b1f..b684898a 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -120,17 +120,25 @@ void Profiler::stop(size_t const hash) void Profiler::write() { + // Find current MPI rank + int current_rank; + MPI_Comm comm = MPI_COMM_WORLD; + MPI_Comm_rank(comm, ¤t_rank); + // Pickup environment variable filename if it exists, if not use the default - // name of "profiler-output.txt" - const char* filename = getenv("ProfOut"); - if (filename != NULL) + // name of "profiler-output.txt". In either case, include the MPI rank in the + // name of the file. + const char* env_variable = std::getenv("PROFILER_OUTFILE"); + if (env_variable != NULL) { - output_stream.open(filename); + const char* user_filename = (env_variable + ("-" + std::to_string(current_rank))).c_str(); + output_stream.open(user_filename); } else { - output_stream.open("profiler-output.txt"); - delete filename; + delete env_variable; + std::string default_filename = "profiler-output-" + std::to_string(current_rank); + output_stream.open(default_filename); } // Write to file diff --git a/src/c++/profiler.h b/src/c++/profiler.h index aeb58ba9..4452af1b 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -22,6 +22,7 @@ #include #include "omp.h" +#include "mpi.h" #include "hashtable.h" From da26ea170c6ea6ac87788eea856ceda8e3c7ad99 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 24 Aug 2022 11:41:45 +0100 Subject: [PATCH 005/128] Farting the current output into a hard-coded file (#52) --- src/c++/hashtable.cpp | 20 +++++++++----------- src/c++/hashtable.h | 6 +++++- src/c++/profiler.cpp | 7 +++++-- src/c++/profiler.h | 1 + 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index eb52fa91..130da8be 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -91,7 +91,7 @@ void HashTable::add_child_time(size_t hash, time_duration_t time_delta) * */ -void HashTable::write() +void HashTable::write(std::ofstream& outstream) { this->compute_self_times(); @@ -99,20 +99,18 @@ void HashTable::write() std::string routine_at_thread = "Thread: " + std::to_string(tid_); // Write headings - std::cout << "\n"; - std::cout + outstream << "\n"; + outstream << std::setw(40) << std::left << routine_at_thread << " " << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (s)" << " " - << std::setw(10) << std::right << "Calls" << "\n"; + << std::setw(15) << std::right << "Total (s)" << "\n"; - std::cout << std::setfill('-'); - std::cout + outstream << std::setfill('-'); + outstream << std::setw(40) << "-" << " " << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(10) << "-" << "\n"; - std::cout << std::setfill(' '); + << std::setw(15) << "-" << "\n"; + outstream << std::setfill(' '); // Create a vector from the hashtable and sort the entries according to self // walltime. If optimisation of this is needed, it ought to be possible to @@ -124,7 +122,7 @@ void HashTable::write() // Data entries for (auto& [hash, entry] : hashvec) { - std::cout + outstream << std::setw(40) << std::left << entry.region_name_ << " " << std::setw(15) << std::right << entry.self_walltime_.count() << " " << std::setw(15) << std::right << entry.total_walltime_.count() << " " diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index b8a0ff64..887b69d1 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -28,12 +28,16 @@ #include #include #include +<<<<<<< HEAD #include // Type definitions for chrono steady clock time points and durations using time_duration_t = std::chrono::duration; using time_point_t = std::chrono::time_point; +======= +#include +>>>>>>> Farting the current output into a hard-coded file (#52) /** * @brief Structure to hold information for a particular routine. @@ -85,7 +89,7 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; void update(size_t, time_duration_t); - void write(); + void write(std::ofstream&); // Member functions std::vector list_keys(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 9d1c090d..ad7ff5b1 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -117,11 +117,14 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - // Write each one + // Write each one to file + output_stream.open("profiler-output.txt"); for (auto& it : thread_hashtables_) { - it.write(); + it.write(output_stream); } + output_stream.flush(); + output_stream.close(); } /** diff --git a/src/c++/profiler.h b/src/c++/profiler.h index a09544b9..813bf360 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -41,6 +41,7 @@ class Profiler int max_threads_; std::vector thread_hashtables_; std::vector>> thread_traceback_; + std::ofstream output_stream; // Type definitions for vector array indexing. typedef std::vector::size_type hashtable_iterator_t_; From 4ca8a4b82d5e26d0a368d5cd11873e1561748a5d Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 24 Aug 2022 11:41:45 +0100 Subject: [PATCH 006/128] Farting the current output into a hard-coded file (#52) --- src/c++/hashtable.cpp | 2 +- src/c++/hashtable.h | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 130da8be..b901d082 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -116,7 +116,7 @@ void HashTable::write(std::ofstream& outstream) // walltime. If optimisation of this is needed, it ought to be possible to // acquire a vector of hash-selftime pairs in the correct order, then use the // hashes to look up other information directly from the hashtable. - hashvec = std::vector>(table_.cbegin(), table_.cend()); + auto hashvec = std::vector>(table_.cbegin(), table_.cend()); std::sort(begin(hashvec), end(hashvec), [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_;}); diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 887b69d1..7b18a330 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -28,17 +28,13 @@ #include #include #include -<<<<<<< HEAD #include +#include // Type definitions for chrono steady clock time points and durations using time_duration_t = std::chrono::duration; using time_point_t = std::chrono::time_point; -======= -#include ->>>>>>> Farting the current output into a hard-coded file (#52) - /** * @brief Structure to hold information for a particular routine. * @@ -78,7 +74,6 @@ class HashTable{ int tid_; std::unordered_map table_; std::hash hash_function_; - std::vector> hashvec; public: From 5edf81929db04df90fdd21135c2753988613d9b6 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 1 Sep 2022 10:38:56 +0100 Subject: [PATCH 007/128] Support for setting the outfile name via an environment variable (#52) --- src/c++/hashtable.cpp | 6 ++++-- src/c++/profiler.cpp | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index b901d082..29ed9fdc 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -103,13 +103,15 @@ void HashTable::write(std::ofstream& outstream) outstream << std::setw(40) << std::left << routine_at_thread << " " << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (s)" << "\n"; + << std::setw(15) << std::right << "Total (s)" << " " + << std::setw(10) << std::right << "Calls" << "\n"; outstream << std::setfill('-'); outstream << std::setw(40) << "-" << " " << std::setw(15) << "-" << " " - << std::setw(15) << "-" << "\n"; + << std::setw(15) << "-" << " " + << std::setw(10) << "-" << "\n"; outstream << std::setfill(' '); // Create a vector from the hashtable and sort the entries according to self diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index ad7ff5b1..6129e677 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -111,18 +111,35 @@ void Profiler::stop(size_t const hash) } /** - * @brief Write profile information. + * @brief Write profile information to file. + * + * @note The default file that the profiler will spit information into is + * called "profiler-output.txt". There also exists the option to set a + * custom name via the environment variable "ProfOut". * */ void Profiler::write() { - // Write each one to file - output_stream.open("profiler-output.txt"); + // Pickup environment variable filename if it exists, if not use the default + // name of "profiler-output.txt" + const char* filename = getenv("ProfOut"); + if (filename != NULL) + { + output_stream.open(filename); + } + else + { + output_stream.open("profiler-output.txt"); + delete filename; + } + + // Write to file for (auto& it : thread_hashtables_) { it.write(output_stream); } + output_stream.flush(); output_stream.close(); } From 338ddb1acf96fc5130ff08d705bf9bdb560f2c2e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 9 Sep 2022 11:29:58 +0100 Subject: [PATCH 008/128] MPI support, one output file per rank (#52) --- CMakeLists.txt | 6 +++++- src/c++/CMakeLists.txt | 3 ++- src/c++/profiler.cpp | 20 ++++++++++++++------ src/c++/profiler.h | 1 + 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea48fdf9..c7b3dc7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,11 @@ include(cmake/Doxygen.cmake) enable_doxygen() # OpenMP required -find_package(OpenMP 2.5 REQUIRED) +find_package(OpenMP 2.5 REQUIRED) + +# MPI required +find_package(MPI REQUIRED) +include_directories(SYSTEM ${MPI_INCLUDE_PATH}) # Defines some standard install directories such as $(prefix)/include. include(GNUInstallDirs) diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index 18e05ac1..4324398b 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -13,7 +13,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED # Link library to and external libs (also use project warnings and options). target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE - OpenMP::OpenMP_CXX) + OpenMP::OpenMP_CXX + MPI::MPI_CXX) set_project_warnings(${CMAKE_PROJECT_NAME}) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 6129e677..23851429 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -121,17 +121,25 @@ void Profiler::stop(size_t const hash) void Profiler::write() { + // Find current MPI rank + int current_rank; + MPI_Comm comm = MPI_COMM_WORLD; + MPI_Comm_rank(comm, ¤t_rank); + // Pickup environment variable filename if it exists, if not use the default - // name of "profiler-output.txt" - const char* filename = getenv("ProfOut"); - if (filename != NULL) + // name of "profiler-output.txt". In either case, include the MPI rank in the + // name of the file. + const char* env_variable = std::getenv("PROFILER_OUTFILE"); + if (env_variable != NULL) { - output_stream.open(filename); + const char* user_filename = (env_variable + ("-" + std::to_string(current_rank))).c_str(); + output_stream.open(user_filename); } else { - output_stream.open("profiler-output.txt"); - delete filename; + delete env_variable; + std::string default_filename = "profiler-output-" + std::to_string(current_rank); + output_stream.open(default_filename); } // Write to file diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 813bf360..24b0d228 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -22,6 +22,7 @@ #include #include "omp.h" +#include "mpi.h" #include "hashtable.h" From 4a5f354e55b0c019c19261caf4f2c4edd87d0afa Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 13 Oct 2022 11:09:35 +0100 Subject: [PATCH 009/128] Tidying up profiler's write function (#52) --- src/c++/profiler.cpp | 13 +++++++++---- src/c++/profiler.h | 1 - 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 23851429..2a4bc9fc 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -126,21 +126,26 @@ void Profiler::write() MPI_Comm comm = MPI_COMM_WORLD; MPI_Comm_rank(comm, ¤t_rank); + // Filename "tail" - will be different for each rank and appended onto the + // end of the output file name + std::string mpi_filename_tail = "-" + std::to_string(current_rank); + // Pickup environment variable filename if it exists, if not use the default // name of "profiler-output.txt". In either case, include the MPI rank in the // name of the file. + std::ofstream output_stream; const char* env_variable = std::getenv("PROFILER_OUTFILE"); + std::string out_filename; if (env_variable != NULL) { - const char* user_filename = (env_variable + ("-" + std::to_string(current_rank))).c_str(); - output_stream.open(user_filename); + out_filename = env_variable + mpi_filename_tail; } else { delete env_variable; - std::string default_filename = "profiler-output-" + std::to_string(current_rank); - output_stream.open(default_filename); + out_filename = "profiler-output" + mpi_filename_tail; } + output_stream.open(out_filename); // Write to file for (auto& it : thread_hashtables_) diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 24b0d228..8a3ed19b 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -42,7 +42,6 @@ class Profiler int max_threads_; std::vector thread_hashtables_; std::vector>> thread_traceback_; - std::ofstream output_stream; // Type definitions for vector array indexing. typedef std::vector::size_type hashtable_iterator_t_; From b916d29d3cd862bffdde4f7d9f1a56bad648ab30 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 14:47:08 +0100 Subject: [PATCH 010/128] Initial workflow [checking gcov works before messing with lcov] (#45) --- .github/workflows/gcov.yml | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/gcov.yml diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml new file mode 100644 index 00000000..7f7fbe35 --- /dev/null +++ b/.github/workflows/gcov.yml @@ -0,0 +1,48 @@ +name: Check unit test coverage + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + run: + runs-on: ubuntu-latest + + steps: + + - name: Checkout + - uses: actions/checkout@v2 + + - name: Configure CMake + run: > + cmake -B ${{github.workspace}}/build + -DCMAKE_CXX_FLAGS="--coverage" + -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON + + - name: Build + run: cmake --build ${{github.workspace}}/build + + - name: Make + working-directory: ${{github.workspace}}/build + run: make + + - name: Run tests + working-directory: ${{github.workspace}}/build + run: ctest + + - name: Run Gcov + working-directory: ${{github.workspace}}/build + run: gcov -H -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno + + - name: Run Lcov + working-directory: ${{github.workspace}}/build + run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info + genhtml coverage.info --output-directory out + + - name: Cleanup + working-directory: ${{github.workspace}} + run: rm *.gcov + \ No newline at end of file From e61735da6707c6273b84ddd7d85d1e34e9c3bcef Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 14:51:47 +0100 Subject: [PATCH 011/128] Cosmetic changes to yml file --- .github/workflows/gcov.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 7f7fbe35..8c9aa1c3 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -8,7 +8,7 @@ on: branches: [ main ] jobs: - run: + build: runs-on: ubuntu-latest steps: @@ -40,9 +40,9 @@ jobs: - name: Run Lcov working-directory: ${{github.workspace}}/build run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - genhtml coverage.info --output-directory out + + - name: Generate html + working-directory: ${{githutb.workspace}}/build + run: genhtml coverage.info --output-directory out - - name: Cleanup - working-directory: ${{github.workspace}} - run: rm *.gcov - \ No newline at end of file + \ No newline at end of file From db80d2433baa350a65b8fc4cc6a2d088ee714fd9 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 14:54:52 +0100 Subject: [PATCH 012/128] Fixing error messsage from actions tab --- .github/workflows/gcov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 8c9aa1c3..4eb5e668 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - - uses: actions/checkout@v2 + uses: actions/checkout@v2 - name: Configure CMake run: > From 5b7172caf5372afd3836ca02f2ce0bc4a4ea8e28 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 15:05:01 +0100 Subject: [PATCH 013/128] Typo in yml file --- .github/workflows/gcov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 4eb5e668..93faa46c 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -42,7 +42,7 @@ jobs: run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - name: Generate html - working-directory: ${{githutb.workspace}}/build + working-directory: ${{github.workspace}}/build run: genhtml coverage.info --output-directory out \ No newline at end of file From 2281558b29fc4844db8aeffb3f30d1f6f3c262e0 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 15:08:30 +0100 Subject: [PATCH 014/128] Disabling doxygen, explicitly declaring compilers using matrix method --- .github/workflows/gcov.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 93faa46c..1a3693c1 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -10,6 +10,13 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + compiler: [ + {c: gcc-10, cpp: g++-10, fortran: gfortran-10}, + {c: clang-12, cpp: clang++-12, fortran: gfortran-10} + ] steps: @@ -19,8 +26,12 @@ jobs: - name: Configure CMake run: > cmake -B ${{github.workspace}}/build + -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} + -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} + -DCMAKE_Fortran_COMPILER=${{ matrix.compiler.fortran }} -DCMAKE_CXX_FLAGS="--coverage" -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON + -DENABLE_DOXYGEN=OFF - name: Build run: cmake --build ${{github.workspace}}/build From 639cc261493cc60eef1484e947769c524dcf797c Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 15:11:37 +0100 Subject: [PATCH 015/128] Including GTest --- .github/workflows/gcov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 1a3693c1..56cbe482 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -29,6 +29,8 @@ jobs: -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_Fortran_COMPILER=${{ matrix.compiler.fortran }} + -DBUILD_TESTS=ON + -DINCLUDE_GTEST=ON -DCMAKE_CXX_FLAGS="--coverage" -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON -DENABLE_DOXYGEN=OFF From d1739a9618250c6c58115e60e0272c7d5f78c3c7 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 15:14:17 +0100 Subject: [PATCH 016/128] Disabling fortran tests in cmake config --- .github/workflows/gcov.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 56cbe482..ae43f1c0 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -14,8 +14,7 @@ jobs: fail-fast: false matrix: compiler: [ - {c: gcc-10, cpp: g++-10, fortran: gfortran-10}, - {c: clang-12, cpp: clang++-12, fortran: gfortran-10} + {c: gcc-10, cpp: g++-10, fortran: gfortran-10} ] steps: @@ -30,6 +29,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_Fortran_COMPILER=${{ matrix.compiler.fortran }} -DBUILD_TESTS=ON + -DBUILD_FORTRAN_TESTS=OFF -DINCLUDE_GTEST=ON -DCMAKE_CXX_FLAGS="--coverage" -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON From af8279a7c747c192f08ad6dbf00aa21c777848a6 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 15:17:48 +0100 Subject: [PATCH 017/128] Altering gcov flags --- .github/workflows/gcov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index ae43f1c0..40d2dec0 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -48,7 +48,7 @@ jobs: - name: Run Gcov working-directory: ${{github.workspace}}/build - run: gcov -H -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno + run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Run Lcov working-directory: ${{github.workspace}}/build From b8dc1434cf8b47957b397599e09f66722a6420ef Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 15:57:21 +0100 Subject: [PATCH 018/128] Attempting conda install of gcov and lcov --- .github/workflows/gcov.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 40d2dec0..1e3084d7 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -8,6 +8,37 @@ on: branches: [ main ] jobs: + conda: + runs-on: ubunutu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Create environment + uses: conda-incubator/setup-miniconda@v2 + with: + auto-activate-base: false + environment-file: myenv.yml + python-version: 3.9 + activate-environment: myenv + + - name: Package list + shell: bash -l {0} + run: | + conda info + conda list + + - name: Gcov + shell: bash -l {0} + run: | + conda install gcov + + - name: Lcov + shell: bash -l {0} + run: | + conda install lcov + build: runs-on: ubuntu-latest strategy: @@ -46,6 +77,11 @@ jobs: working-directory: ${{github.workspace}}/build run: ctest + run: + runs-on: ubuntu-latest + + steps: + - name: Run Gcov working-directory: ${{github.workspace}}/build run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno From 0a4bb78d364b2cb87ebdbc4d51fd9d888fc1881f Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:01:21 +0100 Subject: [PATCH 019/128] Merging into single job --- .github/workflows/gcov.yml | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 1e3084d7..d7a2bfaf 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -8,8 +8,14 @@ on: branches: [ main ] jobs: - conda: + build: runs-on: ubunutu-latest + strategy: + fail-fast: false + matrix: + compiler: [ + {c: gcc-10, cpp: g++-10, fortran: gfortran-10} + ] steps: - name: Checkout @@ -39,20 +45,6 @@ jobs: run: | conda install lcov - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - compiler: [ - {c: gcc-10, cpp: g++-10, fortran: gfortran-10} - ] - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - name: Configure CMake run: > cmake -B ${{github.workspace}}/build @@ -77,11 +69,6 @@ jobs: working-directory: ${{github.workspace}}/build run: ctest - run: - runs-on: ubuntu-latest - - steps: - - name: Run Gcov working-directory: ${{github.workspace}}/build run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno From f1b77587a833849a9b67d90f009387d3cab2877e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:23:51 +0100 Subject: [PATCH 020/128] Fixed typo --- .github/workflows/gcov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index d7a2bfaf..f44e6547 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: ubunutu-latest + runs-on: ubuntu-latest strategy: fail-fast: false matrix: From ea03f9d5361353e34486cb16cc1233f0fb309939 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:26:11 +0100 Subject: [PATCH 021/128] Removing environment file line from conda setup --- .github/workflows/gcov.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index f44e6547..a5c5f835 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -25,7 +25,6 @@ jobs: uses: conda-incubator/setup-miniconda@v2 with: auto-activate-base: false - environment-file: myenv.yml python-version: 3.9 activate-environment: myenv From 1e34eaa2786132c690c0ded5122be84f6c166eb1 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:28:39 +0100 Subject: [PATCH 022/128] Removing conda install of gcov --- .github/workflows/gcov.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index a5c5f835..d4b18d7e 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -34,11 +34,6 @@ jobs: conda info conda list - - name: Gcov - shell: bash -l {0} - run: | - conda install gcov - - name: Lcov shell: bash -l {0} run: | From ffeea84d366c82957f981c60cb56a1466c10e30e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:35:26 +0100 Subject: [PATCH 023/128] Including conda forge channel --- .github/workflows/gcov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index d4b18d7e..1be213cb 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -26,6 +26,7 @@ jobs: with: auto-activate-base: false python-version: 3.9 + channels: conda-forge, defaults activate-environment: myenv - name: Package list From db9f09afecf7ba0504f44a0f54f7aef6f7995b7e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:41:22 +0100 Subject: [PATCH 024/128] Attempting to fix lcov command not working by including shell -l {0} in workflow step --- .github/workflows/gcov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 1be213cb..452fc589 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -70,6 +70,7 @@ jobs: - name: Run Lcov working-directory: ${{github.workspace}}/build + shell: bash -l {0} run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - name: Generate html From df89b092627a10d5f160699accd07c0adc2cb71a Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 16:46:38 +0100 Subject: [PATCH 025/128] Also adding shell command to genhtml step, workflow should now run all the way through --- .github/workflows/gcov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 452fc589..cceda48f 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -75,6 +75,7 @@ jobs: - name: Generate html working-directory: ${{github.workspace}}/build + shell: bash -l {0} run: genhtml coverage.info --output-directory out \ No newline at end of file From 683aabac99f4117b3a8e7cc16577931c875ee23c Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 17:08:58 +0100 Subject: [PATCH 026/128] Trying default workspace option part-way through job --- .github/workflows/gcov.yml | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index cceda48f..82351dbf 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -18,10 +18,9 @@ jobs: ] steps: - - name: Checkout - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - name: Create environment + - name: Create conda environment uses: conda-incubator/setup-miniconda@v2 with: auto-activate-base: false @@ -29,13 +28,7 @@ jobs: channels: conda-forge, defaults activate-environment: myenv - - name: Package list - shell: bash -l {0} - run: | - conda info - conda list - - - name: Lcov + - name: Install Lcov shell: bash -l {0} run: | conda install lcov @@ -56,25 +49,26 @@ jobs: - name: Build run: cmake --build ${{github.workspace}}/build + defaults: + run: + working-directory: ${{github.workspace}}/build + + steps: + - name: Make - working-directory: ${{github.workspace}}/build run: make - name: Run tests - working-directory: ${{github.workspace}}/build run: ctest - name: Run Gcov - working-directory: ${{github.workspace}}/build run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Run Lcov - working-directory: ${{github.workspace}}/build shell: bash -l {0} run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - name: Generate html - working-directory: ${{github.workspace}}/build shell: bash -l {0} run: genhtml coverage.info --output-directory out From c5849a54bbacb8b6b5b9dbe4b10d3dc95eb4677a Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 17:10:01 +0100 Subject: [PATCH 027/128] Removing steps from line56 --- .github/workflows/gcov.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 82351dbf..cf20e543 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -52,8 +52,6 @@ jobs: defaults: run: working-directory: ${{github.workspace}}/build - - steps: - name: Make run: make From ee78b360a1e367827d8888271e46587c2b0190c8 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 17:12:16 +0100 Subject: [PATCH 028/128] Added default shell --- .github/workflows/gcov.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index cf20e543..7545a728 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -17,6 +17,11 @@ jobs: {c: gcc-10, cpp: g++-10, fortran: gfortran-10} ] + defaults: + run: + shell: bash + working-directory: ${{github.workspace}}/build + steps: - uses: actions/checkout@v2 @@ -29,9 +34,8 @@ jobs: activate-environment: myenv - name: Install Lcov - shell: bash -l {0} - run: | - conda install lcov + shell: -l {0} + run: conda install lcov - name: Configure CMake run: > @@ -49,10 +53,6 @@ jobs: - name: Build run: cmake --build ${{github.workspace}}/build - defaults: - run: - working-directory: ${{github.workspace}}/build - - name: Make run: make @@ -63,11 +63,11 @@ jobs: run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Run Lcov - shell: bash -l {0} + shell: -l {0} run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - name: Generate html - shell: bash -l {0} + shell: -l {0} run: genhtml coverage.info --output-directory out \ No newline at end of file From 39de6d143f7fc4a8c2497f2fe3505b81b1cc5bb4 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 27 Oct 2022 17:17:43 +0100 Subject: [PATCH 029/128] Removing defaults option... --- .github/workflows/gcov.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 7545a728..492e7079 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -17,13 +17,9 @@ jobs: {c: gcc-10, cpp: g++-10, fortran: gfortran-10} ] - defaults: - run: - shell: bash - working-directory: ${{github.workspace}}/build - steps: - - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v2 - name: Create conda environment uses: conda-incubator/setup-miniconda@v2 @@ -34,7 +30,7 @@ jobs: activate-environment: myenv - name: Install Lcov - shell: -l {0} + shell: bash -l {0} run: conda install lcov - name: Configure CMake @@ -54,20 +50,22 @@ jobs: run: cmake --build ${{github.workspace}}/build - name: Make + working-directory: ${{github.workspace}}/build run: make - name: Run tests + working-directory: ${{github.workspace}}/build run: ctest - name: Run Gcov run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Run Lcov - shell: -l {0} + shell: bash -l {0} run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - name: Generate html - shell: -l {0} + shell: bash -l {0} run: genhtml coverage.info --output-directory out \ No newline at end of file From 808be57b972543b81e33e48c3f0871836f7001d7 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 10:24:07 +0100 Subject: [PATCH 030/128] Added step that updates github pages --- .github/workflows/gcov.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 492e7079..50421250 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -68,4 +68,21 @@ jobs: shell: bash -l {0} run: genhtml coverage.info --output-directory out + - name: Update github pages + run: | + mkdir -p ${{github.workspace}}/docs/coverage + cp -rf ${{github.workspace}}/out ${{github.workspace}}/docs/coverage + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]' + git add --force docs + git stash + git remote update + git checkout gh-pages + git rm -rf docs/coverage + git stash pop + git reset + git add --force docs + git commit -m "Update coverage documentation" + git push -f origin gh-pages + \ No newline at end of file From 51d8c916184977b84bc17831b544c756fb8c5e2c Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 10:29:26 +0100 Subject: [PATCH 031/128] Resolving unknown branch issue --- .github/workflows/gcov.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 50421250..4c54681d 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -77,7 +77,8 @@ jobs: git add --force docs git stash git remote update - git checkout gh-pages + git branch gh-pages + git checkout -b gh-pages git rm -rf docs/coverage git stash pop git reset From 020d4a75cdca92399ee112b7ff07a1ddefe1119d Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 10:32:51 +0100 Subject: [PATCH 032/128] Typo in final step --- .github/workflows/gcov.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 4c54681d..50421250 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -77,8 +77,7 @@ jobs: git add --force docs git stash git remote update - git branch gh-pages - git checkout -b gh-pages + git checkout gh-pages git rm -rf docs/coverage git stash pop git reset From 88e6628d188ebaf1a23ace17c4afe1262a6b6238 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 10:35:53 +0100 Subject: [PATCH 033/128] Removing git remote update line --- .github/workflows/gcov.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 50421250..234bc00e 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -76,8 +76,7 @@ jobs: git config user.email 'github-actions[bot]' git add --force docs git stash - git remote update - git checkout gh-pages + git checkout -b gh-pages git rm -rf docs/coverage git stash pop git reset From 0673329c90e92cfd5687e782ae1484b65128dae4 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 11:11:42 +0100 Subject: [PATCH 034/128] Fixing error msg when github tries to remove folder that isnt there --- .github/workflows/gcov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 234bc00e..f1a5f83b 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -77,7 +77,7 @@ jobs: git add --force docs git stash git checkout -b gh-pages - git rm -rf docs/coverage + git rm -rf ${{github.workspace}}/docs/coverage git stash pop git reset git add --force docs From ba54fbe322332fa69a85acbb6fda8ce62c7b654b Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 11:20:16 +0100 Subject: [PATCH 035/128] Removing git prefix from rm -rf --- .github/workflows/gcov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index f1a5f83b..157828a0 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -77,7 +77,7 @@ jobs: git add --force docs git stash git checkout -b gh-pages - git rm -rf ${{github.workspace}}/docs/coverage + rm -rf ${{github.workspace}}/docs/coverage git stash pop git reset git add --force docs From 29392722f6bda86a5cc3022e3a984598cc043f7a Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 11:38:14 +0100 Subject: [PATCH 036/128] Altering docs folder --- .github/workflows/gcov.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 157828a0..ff6a0cf7 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -70,14 +70,14 @@ jobs: - name: Update github pages run: | - mkdir -p ${{github.workspace}}/docs/coverage - cp -rf ${{github.workspace}}/out ${{github.workspace}}/docs/coverage + mkdir -p ${{github.workspace}}/docs + cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' git add --force docs git stash git checkout -b gh-pages - rm -rf ${{github.workspace}}/docs/coverage + rm -rf ${{github.workspace}}/docs git stash pop git reset git add --force docs From 906910ee4b501f1fd33feaf5c1ece84a055e7cf0 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 14:12:39 +0100 Subject: [PATCH 037/128] Filtering out STL headers --- .github/workflows/gcov.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index ff6a0cf7..2abfa290 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -60,10 +60,14 @@ jobs: - name: Run Gcov run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - - name: Run Lcov + - name: Inital Lcov Run shell: bash -l {0} - run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file coverage.info - + run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file basecoverage.info + + - name: Extract relevant info + shell: bash -l {0]} + run: lcov --extract basecoverage.info '${{github.workspace}}/src/*' --output-file coverage.info + - name: Generate html shell: bash -l {0} run: genhtml coverage.info --output-directory out From 4eb1ce455467294cc94cdc1d78d61b87164d9e25 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 14:15:50 +0100 Subject: [PATCH 038/128] Fixed typo in shell option --- .github/workflows/gcov.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 2abfa290..784cc04f 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -42,7 +42,7 @@ jobs: -DBUILD_TESTS=ON -DBUILD_FORTRAN_TESTS=OFF -DINCLUDE_GTEST=ON - -DCMAKE_CXX_FLAGS="--coverage" + -DCMAKE_CXX_FLAGS="-g --coverage" -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON -DENABLE_DOXYGEN=OFF @@ -65,7 +65,7 @@ jobs: run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file basecoverage.info - name: Extract relevant info - shell: bash -l {0]} + shell: bash -l {0} run: lcov --extract basecoverage.info '${{github.workspace}}/src/*' --output-file coverage.info - name: Generate html From 336dc18e56577324217998c84aae6ea8b5868c8c Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 14:25:03 +0100 Subject: [PATCH 039/128] Attempt to include fortran tests --- .github/workflows/gcov.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index 784cc04f..afba5813 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -40,7 +40,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_Fortran_COMPILER=${{ matrix.compiler.fortran }} -DBUILD_TESTS=ON - -DBUILD_FORTRAN_TESTS=OFF + -DBUILD_FORTRAN_TESTS=ON -DINCLUDE_GTEST=ON -DCMAKE_CXX_FLAGS="-g --coverage" -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON @@ -58,7 +58,9 @@ jobs: run: ctest - name: Run Gcov - run: gcov -j -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno + run: | + gcov --help + gcov -m -j ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Inital Lcov Run shell: bash -l {0} From 37ac655e2be46b9a4d1cda9fcc486683de021c71 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 14:28:04 +0100 Subject: [PATCH 040/128] Scrapping fortran tests --- .github/workflows/gcov.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gcov.yml b/.github/workflows/gcov.yml index afba5813..0f719df4 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/gcov.yml @@ -40,7 +40,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_Fortran_COMPILER=${{ matrix.compiler.fortran }} -DBUILD_TESTS=ON - -DBUILD_FORTRAN_TESTS=ON + -DBUILD_FORTRAN_TESTS=OFF -DINCLUDE_GTEST=ON -DCMAKE_CXX_FLAGS="-g --coverage" -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON @@ -59,8 +59,7 @@ jobs: - name: Run Gcov run: | - gcov --help - gcov -m -j ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno + gcov -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Inital Lcov Run shell: bash -l {0} From afd98f4887705c2cdbaae274fee5736bde703396 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 14:43:59 +0100 Subject: [PATCH 041/128] Getting Doxygen AND coverage docs to appear --- .github/workflows/{gcov.yml => coverage.yml} | 27 ++++++++++++-------- .github/workflows/documentation.yml | 10 ++++---- 2 files changed, 22 insertions(+), 15 deletions(-) rename .github/workflows/{gcov.yml => coverage.yml} (72%) diff --git a/.github/workflows/gcov.yml b/.github/workflows/coverage.yml similarity index 72% rename from .github/workflows/gcov.yml rename to .github/workflows/coverage.yml index 0f719df4..5993c105 100644 --- a/.github/workflows/gcov.yml +++ b/.github/workflows/coverage.yml @@ -1,4 +1,4 @@ -name: Check unit test coverage +name: Unit test coverage on: workflow_dispatch: @@ -22,6 +22,7 @@ jobs: uses: actions/checkout@v2 - name: Create conda environment + # Setup a virtual conda environment uses: conda-incubator/setup-miniconda@v2 with: auto-activate-base: false @@ -30,10 +31,14 @@ jobs: activate-environment: myenv - name: Install Lcov + # Install lcov from conda (graphical front-end for gcov reports) shell: bash -l {0} run: conda install lcov - name: Configure CMake + # Configure CMake in a 'build' subdirectory + # Key thing here is the "--coverage" CXX flag which generates + # .gcno files for gcov. run: > cmake -B ${{github.workspace}}/build -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} @@ -47,25 +52,27 @@ jobs: -DENABLE_DOXYGEN=OFF - name: Build + # Build with the given configuration run: cmake --build ${{github.workspace}}/build - - name: Make + - name: Make working-directory: ${{github.workspace}}/build run: make - name: Run tests + # Run all the unit test executables working-directory: ${{github.workspace}}/build run: ctest - name: Run Gcov - run: | - gcov -m ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno + run: gcov --demangled-names --human-readable ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - name: Inital Lcov Run shell: bash -l {0} run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file basecoverage.info - name: Extract relevant info + # Ignore all the STL header files from the initial lcov .info file shell: bash -l {0} run: lcov --extract basecoverage.info '${{github.workspace}}/src/*' --output-file coverage.info @@ -75,18 +82,18 @@ jobs: - name: Update github pages run: | - mkdir -p ${{github.workspace}}/docs - cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/. + mkdir -p ${{github.workspace}}/docs/coverage + cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage/. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' - git add --force docs + git add --force docs/coverage git stash git checkout -b gh-pages - rm -rf ${{github.workspace}}/docs + rm -rf ${{github.workspace}}/docs/coverage git stash pop git reset - git add --force docs - git commit -m "Update coverage documentation" + git add --force docs/coverage + git commit --allow-empty -m "Update coverage documentation" git push -f origin gh-pages \ No newline at end of file diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 836a708a..35235728 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -42,17 +42,17 @@ jobs: - name: Update Doxygen Pages run: | - mkdir -p ${{github.workspace}}/docs - cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/. + mkdir -p ${{github.workspace}}/docs/documentation + cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/documentation/. git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add --force docs + git add --force docs/documentation git stash git remote update git checkout gh-pages - git rm -rf docs + git rm -rf docs/documentation git stash pop git reset - git add --force docs + git add --force docs/documentation git commit --allow-empty -m "Update Doxygen documentation" git push From 9f9de39afa3df159cd8a07e832f0975ba83ac528 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 14:55:55 +0100 Subject: [PATCH 042/128] Fixed error in documentation.yml --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 35235728..eeb7c545 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -50,7 +50,7 @@ jobs: git stash git remote update git checkout gh-pages - git rm -rf docs/documentation + rm -rf ${{github.workspace}}/docs/documentation git stash pop git reset git add --force docs/documentation From 3807f796950ab586dbae0c0d4dfb004ca6dcab8b Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 15:07:48 +0100 Subject: [PATCH 043/128] Changing where doxygen documentation is put --- .github/workflows/documentation.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index eeb7c545..13a418ed 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -42,17 +42,17 @@ jobs: - name: Update Doxygen Pages run: | - mkdir -p ${{github.workspace}}/docs/documentation - cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/documentation/. + mkdir -p ${{github.workspace}}/docs + cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/. git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add --force docs/documentation + git add --force docs git stash git remote update git checkout gh-pages - rm -rf ${{github.workspace}}/docs/documentation + rm -rf ${{github.workspace}}/docs git stash pop git reset - git add --force docs/documentation + git add --force docs git commit --allow-empty -m "Update Doxygen documentation" git push From 62b4cd883aa001fca18dc90c34599fa87de53acd Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 15:16:19 +0100 Subject: [PATCH 044/128] Testing putting both workflows straight into docs --- .github/workflows/coverage.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5993c105..87716dd6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -82,17 +82,17 @@ jobs: - name: Update github pages run: | - mkdir -p ${{github.workspace}}/docs/coverage - cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage/. + mkdir -p ${{github.workspace}}/docs + cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' - git add --force docs/coverage + git add --force docs git stash git checkout -b gh-pages - rm -rf ${{github.workspace}}/docs/coverage + rm -rf ${{github.workspace}}/docs git stash pop git reset - git add --force docs/coverage + git add --force docs git commit --allow-empty -m "Update coverage documentation" git push -f origin gh-pages From eb12dfbe8470e865cb2e18899d70b8001f881314 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 15:21:31 +0100 Subject: [PATCH 045/128] Separating them into documentation and coverage files --- .github/workflows/coverage.yml | 17 ++++++++++------- .github/workflows/documentation.yml | 10 +++++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 87716dd6..829847db 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -4,8 +4,11 @@ on: workflow_dispatch: push: branches: [ main ] - pull_request: - branches: [ main ] + paths: + - 'src/**.h' + - 'src/**.cpp' + - 'tests/**' + - '.github/workflows/coverage.yml' jobs: build: @@ -82,17 +85,17 @@ jobs: - name: Update github pages run: | - mkdir -p ${{github.workspace}}/docs - cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/. + mkdir -p ${{github.workspace}}//coverage + cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' - git add --force docs + git add --force docs/coverage git stash git checkout -b gh-pages - rm -rf ${{github.workspace}}/docs + rm -rf ${{github.workspace}}/docs/coverage git stash pop git reset - git add --force docs + git add --force docs/coverage git commit --allow-empty -m "Update coverage documentation" git push -f origin gh-pages diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 13a418ed..eeb7c545 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -42,17 +42,17 @@ jobs: - name: Update Doxygen Pages run: | - mkdir -p ${{github.workspace}}/docs - cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/. + mkdir -p ${{github.workspace}}/docs/documentation + cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/documentation/. git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add --force docs + git add --force docs/documentation git stash git remote update git checkout gh-pages - rm -rf ${{github.workspace}}/docs + rm -rf ${{github.workspace}}/docs/documentation git stash pop git reset - git add --force docs + git add --force docs/documentation git commit --allow-empty -m "Update Doxygen documentation" git push From cbb643ad00a51b6d5ce4b655be46924a2702f2c1 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 15:24:39 +0100 Subject: [PATCH 046/128] Typo in coverage.yml --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 829847db..8d7a1b19 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -86,7 +86,7 @@ jobs: - name: Update github pages run: | mkdir -p ${{github.workspace}}//coverage - cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage. + cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage/. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' git add --force docs/coverage From dc2f0b4a7f201e257b1190dcbf382795785e70aa Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 15:28:26 +0100 Subject: [PATCH 047/128] Another typo in coverage.yml --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8d7a1b19..0e446df7 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -85,7 +85,7 @@ jobs: - name: Update github pages run: | - mkdir -p ${{github.workspace}}//coverage + mkdir -p ${{github.workspace}}/coverage cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage/. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' From 1590af52cddce4c992d92d1ca880b462cf1e281b Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 28 Oct 2022 15:33:58 +0100 Subject: [PATCH 048/128] Hopefully final error with coverage.yml --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0e446df7..256e620b 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -85,7 +85,7 @@ jobs: - name: Update github pages run: | - mkdir -p ${{github.workspace}}/coverage + mkdir -p ${{github.workspace}}/docs/coverage cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage/. git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]' From 62d45857413efe4970589a43563ee7ff9c8b0d74 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 3 Nov 2022 12:35:51 +0000 Subject: [PATCH 049/128] Overhead times reported from all threads. --- src/c++/hashtable.cpp | 77 ++++++++++++++++++++++++++++++++----------- src/c++/hashtable.h | 6 ++-- src/c++/profiler.cpp | 20 ++++++----- 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index a78916d6..9469833e 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -88,35 +88,72 @@ void HashTable::update(size_t hash, time_duration_t time_delta) } /** - * @brief Add child region time to parent. + * @brief Add child region and overhead times to parent. * @param [in] hash The hash of the child region to update. * @param [in] time_delta The time spent in the child region. */ -void HashTable::add_child_time(size_t const hash, time_duration_t time_delta) +void HashTable::add_child_time(size_t const hash, + time_duration_t const time_delta) { // Assertions assert (table_.size() > 0); assert (table_.count(hash) > 0); - // Increment the walltime for this hash entry auto& entry = table_.at(hash); + + // Increment the child time for this entry entry.child_walltime_ += time_delta; } /** - * @brief Add profiling overhead time, incurred when calling a child, to the - * parent region. - * @param [in] hash The hash of the child region to update. - * @param [in] calliper_time The profiling overhead time. + * @brief Add child region and overhead times to parent. + * @param [in] hash The hash of the child region to update. + * @param [in] time_delta The time spent in the child region. */ -void HashTable::add_overhead_time(size_t const hash, time_duration_t calliper_time) +void HashTable::add_overhead_time(size_t const hash, + time_duration_t const calliper_time) { + // Assertions + assert (table_.size() > 0); + assert (table_.count(hash) > 0); + auto& entry = table_.at(hash); + + // Increment the overhead time for this entry entry.overhead_walltime_ += calliper_time; } +/** + * @brief Add child region and overhead times to parent. + * @param [in] hash The hash of the child region to update. + * @param [in] time_delta The time spent in the child region. + */ + +void HashTable::add_subtimes(size_t const hash, + time_duration_t const time_delta, + time_duration_t const calliper_time) +{ + // Assertions + assert (table_.size() > 0); + assert (table_.count(hash) > 0); + + auto& entry = table_.at(hash); + + // Increment the child time for this entry + entry.child_walltime_ += time_delta; + + // Increment the overhead time for this entry + entry.overhead_walltime_ += calliper_time; +} + +void HashTable::add_total_overhead_time(time_duration_t const calliper_time) +{ + auto& entry = table_.at(profiler_hash_); + entry.total_walltime_ += calliper_time; +} + /** * @brief Writes all entries in the hashtable, sorted according to self times. * @@ -205,21 +242,23 @@ void HashTable::prepare_computed_times(size_t const hash) // corresponding values are zero thus far. for (auto& [hash, entry] : table_) { prepare_computed_times(hash); - total_overhead_time += entry.overhead_walltime_; } - // Check that the special profiler hash entries are all zero, even after the - // above loop. - assert(table_.at(profiler_hash_).self_walltime_ == time_duration_t::zero()); - assert(table_.at(profiler_hash_).child_walltime_ == time_duration_t::zero()); - assert(table_.at(profiler_hash_).total_walltime_ == time_duration_t::zero()); - assert(table_.at(profiler_hash_).total_raw_walltime_ == time_duration_t::zero()); + /// // Check that the special profiler hash entries are all zero, even after the + /// // above loop. + /// assert(table_.at(profiler_hash_).self_walltime_ == time_duration_t::zero()); + /// assert(table_.at(profiler_hash_).child_walltime_ == time_duration_t::zero()); + /// assert(table_.at(profiler_hash_).total_walltime_ == time_duration_t::zero()); + /// assert(table_.at(profiler_hash_).total_raw_walltime_ == time_duration_t::zero()); // Set values for the profiler entry specifically in the hashtable. - table_.at(profiler_hash_).self_walltime_ = total_overhead_time; - table_.at(profiler_hash_).child_walltime_ = time_duration_t::zero(); - table_.at(profiler_hash_).total_walltime_ = total_overhead_time; - table_.at(profiler_hash_).total_raw_walltime_ = total_overhead_time; + // table_.at(profiler_hash_).self_walltime_ = total_overhead_time_; + // table_.at(profiler_hash_).child_walltime_ = time_duration_t::zero(); + // table_.at(profiler_hash_).total_walltime_ = total_overhead_time_; + // table_.at(profiler_hash_).total_raw_walltime_ = total_overhead_time_; + + // std::cout << "My thread ID: " << omp_get_thread_num() << std::endl; + // std::cout << "Table thread ID: " << tid_ << std::endl; } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 0d937f4a..60a4392e 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -93,8 +93,10 @@ class HashTable{ // Member functions std::vector list_keys(); - void add_child_time (size_t const, time_duration_t); - void add_overhead_time(size_t const, time_duration_t); + void add_subtimes (size_t const, time_duration_t const, time_duration_t const); + void add_child_time (size_t const, time_duration_t const); + void add_overhead_time (size_t const, time_duration_t const); + void add_total_overhead_time(time_duration_t const); // Getters double get_total_walltime(size_t const hash) const; diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index e71fad07..ad78636a 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -135,7 +135,7 @@ void Profiler::stop(size_t const hash) // Precompute times as far as possible. We just need the calliper stop time // later. - // (t4-t1) = calliper time + region time + // (t4-t1) = calliper time + region duration // (t3-t2) = region_duration // calliper_time = (t4-t1) - (t3-t2) = t4 - ( t3-t2 + t1) auto temp_sum = start_calliper_times.calliper_start_time_ + region_duration; @@ -143,16 +143,20 @@ void Profiler::stop(size_t const hash) // Remove from the end of the list. thread_traceback_[tid].pop_back(); - // Prepare to add timings to parent + // Account for time spent in the profiler itself. + auto calliper_stop_time = prof_gettime(); + auto calliper_time = calliper_stop_time - temp_sum; + + // Add child and overhead times to parent. if (! thread_traceback_[tid].empty()) { size_t parent_hash = thread_traceback_[tid].back().first; - thread_hashtables_[tid].add_child_time(parent_hash, region_duration); - - // Account for time spent in the profiler itself. - auto calliper_stop_time = prof_gettime(); - auto calliper_time = calliper_stop_time - temp_sum; - thread_hashtables_[tid].add_overhead_time(parent_hash, calliper_time); + //thread_hashtables_[tid].add_child_time (parent_hash, region_duration); + //thread_hashtables_[tid].add_overhead_time(parent_hash, calliper_time); + thread_hashtables_[tid].add_subtimes(parent_hash, region_duration, calliper_time); } + + // Add overhead time to the separate profiler entry. + thread_hashtables_[tid].add_total_overhead_time(calliper_time); } /** From cbda1865f5f84b106a897a528fdd0d473cb13da4 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 3 Nov 2022 16:04:11 +0000 Subject: [PATCH 050/128] Draft of new IO structure contained within the two new files (#52) --- .gitignore | 1 + src/c++/CMakeLists.txt | 1 + src/c++/hashtable.cpp | 2 +- src/c++/hashtable.h | 2 +- src/c++/profiler.cpp | 42 ++------- src/c++/profiler.h | 1 - src/c++/writer.cpp | 110 ++++++++++++++++++++++++ src/c++/writer.h | 102 ++++++++++++++++++++++ tests/unit_tests/c++/test_profiler.cpp | 4 - tests/unit_tests/f/test_profiler_mod.pf | 3 - 10 files changed, 222 insertions(+), 46 deletions(-) create mode 100644 src/c++/writer.cpp create mode 100644 src/c++/writer.h diff --git a/.gitignore b/.gitignore index 3ee4c074..48d5cd05 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ flycheck_*.el ### Visual Studio Code template +.vscode* .vscode/* !.vscode/settings.json !.vscode/tasks.json diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index 4324398b..1495090e 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -8,6 +8,7 @@ include(CMakePackageConfigHelpers) # Add files source files to library. add_library(${CMAKE_PROJECT_NAME} SHARED profiler.h profiler.cpp + writer.h writer.cpp hashtable.h hashtable.cpp ) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 29ed9fdc..3103fb2f 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -91,7 +91,7 @@ void HashTable::add_child_time(size_t hash, time_duration_t time_delta) * */ -void HashTable::write(std::ofstream& outstream) +void HashTable::print(std::ostream& outstream) { this->compute_self_times(); diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 7b18a330..a4c45065 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -84,7 +84,7 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; void update(size_t, time_duration_t); - void write(std::ofstream&); + void print(std::ostream&); // Member functions std::vector list_keys(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 2a4bc9fc..548c88ec 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -7,6 +7,8 @@ #include "profiler.h" +#include "writer.h" + #include #include #include @@ -114,47 +116,15 @@ void Profiler::stop(size_t const hash) * @brief Write profile information to file. * * @note The default file that the profiler will spit information into is - * called "profiler-output.txt". There also exists the option to set a - * custom name via the environment variable "ProfOut". + * called "profiler-output". There also exists the option to set a + * custom name via an environment variable. * */ void Profiler::write() { - // Find current MPI rank - int current_rank; - MPI_Comm comm = MPI_COMM_WORLD; - MPI_Comm_rank(comm, ¤t_rank); - - // Filename "tail" - will be different for each rank and appended onto the - // end of the output file name - std::string mpi_filename_tail = "-" + std::to_string(current_rank); - - // Pickup environment variable filename if it exists, if not use the default - // name of "profiler-output.txt". In either case, include the MPI rank in the - // name of the file. - std::ofstream output_stream; - const char* env_variable = std::getenv("PROFILER_OUTFILE"); - std::string out_filename; - if (env_variable != NULL) - { - out_filename = env_variable + mpi_filename_tail; - } - else - { - delete env_variable; - out_filename = "profiler-output" + mpi_filename_tail; - } - output_stream.open(out_filename); - - // Write to file - for (auto& it : thread_hashtables_) - { - it.write(output_stream); - } - - output_stream.flush(); - output_stream.close(); + Writer scribe; + scribe.write(thread_hashtables_); } /** diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 8a3ed19b..a09544b9 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -22,7 +22,6 @@ #include #include "omp.h" -#include "mpi.h" #include "hashtable.h" diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp new file mode 100644 index 00000000..b190a0b3 --- /dev/null +++ b/src/c++/writer.cpp @@ -0,0 +1,110 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +#include "writer.h" +#include + +/** + * @brief Writes out all entires in the hashtable into a separate file for + * each mpi rank. + * + * @param[in] htvec A vector of hashtables. + * + */ + +void MultipleFiles::write(std::vector htvec) +{ + // Find current MPI rank + int current_rank; + MPI_Comm prof_comm_ = MPI_COMM_WORLD; + MPI_Comm_rank(prof_comm_, ¤t_rank); + + // Filename "tail" - will be different for each rank and appended onto the + // end of the output file name + std::string mpi_filename_tail = "-" + std::to_string(current_rank); + + // Pickup environment variable filename if it exists, if not use the + // default name of "profiler-output". In either case, include the MPI rank + // in the name of the file. + const char* env_variable = std::getenv("PROF_OUTFILE"); + std::string out_filename; + if (env_variable != NULL) + { + out_filename = env_variable + mpi_filename_tail; + } + else + { + delete env_variable; + out_filename = "profiler-output" + mpi_filename_tail; + } + output_stream.open(out_filename); + + // Write to file then close it + for (auto& it : htvec) + { + it.print(output_stream); + } + output_stream.flush(); + output_stream.close(); +} + +/** + * @brief Context class constructor. + * + * @param[in] temp_ptr Pointer to IO Interface, which will be empty after + * ownership is transferred to io_strategy_ + * + */ + +IO_StrategyContext::IO_StrategyContext(std::unique_ptr temp_ptr) + : io_strategy_(std::move(temp_ptr)) + {} + +/** + * @brief Allows the strategy to be switched. + * + * @param[in] temp_ptr Pointer to IO Interface, which will be empty after + * ownership is transferred to io_strategy_ + * + */ + +void IO_StrategyContext::setStrategy(std::unique_ptr temp_ptr) +{ + io_strategy_ = std::move(temp_ptr); +} + +/** + * @brief Calls the write method linked to whichever derived class the + * strategy is referencing. + * + * @param[in] htvec A vector of hashtables. + * + */ + +void IO_StrategyContext::executeStrategy(std::vector htvec) +{ + io_strategy_->write(htvec); +} + +/** + * @brief Strategy decision is made and context class object created in order + * to execute the appropriate write method. + * + * @param[in] htvec A vector of hashtables. + * + */ + +void Writer::write(std::vector htvec) +{ + const char* user_strat = std::getenv("PROF_IO_MODE"); + if ( user_strat == NULL || static_cast(user_strat) == "MultipleFiles") + { + IO_StrategyContext contextObj(std::move(std::make_unique())); + contextObj.executeStrategy(htvec); + } + else throw std::runtime_error("Invalid PROF_IO_MODE value"); +} \ No newline at end of file diff --git a/src/c++/writer.h b/src/c++/writer.h new file mode 100644 index 00000000..04905679 --- /dev/null +++ b/src/c++/writer.h @@ -0,0 +1,102 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +/** + * @file writer.h + * @brief Contains writer class and handles IO. + * + * Contains a strategy method made up of a base IO class + derived classes, + * aswell as context and writer classes which handle tasks such as creation + * of pointers and calling the correct methods. + * + */ + +#include +#include +#include +#include "hashtable.h" + +/** + * @brief IO interface class. + * + * An interface-style base class which contains a pure virtual write method, + * which can then be overloaded by derived classes. + * + */ + +class IO_Interface { + + protected: + + std::ofstream output_stream; + + public: + + // Virtual constructor + virtual ~IO_Interface() = default; + + // Pure virtual "write" method + virtual void write(std::vector) = 0; + +}; + +/** + * @brief Derived class for multiple-file output. + * + * Overloads the pure virtual write method in IO_Interface. + * + */ + +class MultipleFiles : public IO_Interface { + + public: + + void write(std::vector ht) override; + +}; + +/** + * @brief Context class. + * + * A key part of any strategy method which is responsible for maintaining + * a reference to one of the interface's derived classes. It has an execution + * method which calls the appropriate derived class write method. + * + */ + +class IO_StrategyContext { + + private: + + std::unique_ptr io_strategy_; + + public: + + // Constructor + explicit IO_StrategyContext(std::unique_ptr temp_ptr); + + // Methods + void setStrategy(std::unique_ptr temp_ptr); + void executeStrategy(std::vector ht); + +}; + +/** + * @brief Writer class. + * + * A class that picks a strategy and creates a context class object. This + * class serves as something for the profiler to interface with. + * + */ + +class Writer { + + public: + + void write(std::vector ht); + +}; \ No newline at end of file diff --git a/tests/unit_tests/c++/test_profiler.cpp b/tests/unit_tests/c++/test_profiler.cpp index 66749977..60df4531 100644 --- a/tests/unit_tests/c++/test_profiler.cpp +++ b/tests/unit_tests/c++/test_profiler.cpp @@ -13,7 +13,6 @@ TEST(SystemTests, TimingTest) { - // Start timing: noddy way, and using Profiler. auto prof_main = prof.start("MAIN"); double t1 = omp_get_wtime(); @@ -47,9 +46,6 @@ TEST(SystemTests, TimingTest) double t2 = omp_get_wtime(); prof.stop(prof_main); - // Write the profile - prof.write(); - // Check that the total time measured by the profiler is within some tolerance // of the actual time measured by simple t2-t1. This only tests the top-level // timing, not individual subroutine timings. diff --git a/tests/unit_tests/f/test_profiler_mod.pf b/tests/unit_tests/f/test_profiler_mod.pf index f7b366a8..baacd7eb 100644 --- a/tests/unit_tests/f/test_profiler_mod.pf +++ b/tests/unit_tests/f/test_profiler_mod.pf @@ -66,9 +66,6 @@ contains t2 = omp_get_wtime(); call profiler_stop(prof_main) - ! Write the profile - call profiler_write() - actual_time = t2 - t1 profiler_wallclock_time = profiler_get_thread0_walltime(prof_main) From c3a40d27a165fd616425f63d162b2069f78cde2b Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 3 Nov 2022 16:37:43 +0000 Subject: [PATCH 051/128] Formatting mostly there but not working with multiple threads (#70) --- src/c++/hashtable.cpp | 95 +++++++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 154373de..f9f582b9 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -95,40 +95,93 @@ void HashTable::write() this->compute_self_times(); - std::string routine_at_thread = "Thread: " + std::to_string(tid_); + // Headers + std::string column = " "; - // Write headings std::cout << "\n"; std::cout - << std::setw(40) << std::left << routine_at_thread << " " - << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (s)" << " " - << std::setw(10) << std::right << "Calls" << "\n"; - - std::cout << std::setfill('-'); - std::cout - << std::setw(40) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(10) << "-" << "\n"; - std::cout << std::setfill(' '); + << std::setw(3) << std::left << "#" + << std::setw(8) << std::left << "% Time" << column + << std::setw(8) << std::right << "Cumul" << column + << std::setw(8) << std::right << "Self" << column + << std::setw(8) << std::right << "Total" << column + << std::setw(5) << std::right << "calls" << column + << std::setw(8) << std::right << "Self" << column + << std::setw(8) << std::right << "Total" << column + << std::setw(8) << std::right << "Routine@" << "\n"; + std::cout + << std::setw(75) << " " + << std::setw(45) << std::left << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; + + // Subheaders + std::cout + << std::setw(3) << std::left << "" + << std::setw(8) << std::left << "(self)" << column + << std::setw(8) << std::right << "(sec)" << column + << std::setw(8) << std::right << "(sec)" << column + << std::setw(8) << std::right << "(sec)" << column + << std::setw(5) << std::right << "" << column + << std::setw(8) << std::right << "ms/call" << column + << std::setw(8) << std::right << "ms/call" << column + << std::setw(8) << std::right << "" << "\n\n"; // Create a vector from the hashtable and sort the entries according to self // walltime. If optimisation of this is needed, it ought to be possible to // acquire a vector of hash-selftime pairs in the correct order, then use the // hashes to look up other information directly from the hashtable. - hashvec = std::vector>(table_.cbegin(), table_.cend()); + auto hashvec = std::vector>(table_.cbegin(), table_.cend()); std::sort(begin(hashvec), end(hashvec), - [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_;}); + [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_; }); + + // Create a string to append onto the end of the region names + std::string region_name_tail = "@" + std::to_string(tid_); + + // Find the highest walltime in table_, which should be the total runtime of + // the program. This is used later when calculating '% Time'. + double top_walltime = std::max_element + ( + std::begin(hashvec), std::end(hashvec), + [] (auto a, auto b) { + return a.second.total_walltime_ < b.second.total_walltime_; + } + )->second.total_walltime_.count(); + + // Declare any variables external to HashEntry + int region_number = 0; + double percent_time; + time_duration_t cumul_walltime = time_duration_t::zero(); + double self_per_call; + double total_per_call; + + // + // Write data to file + // + + std::cout << std::fixed << std::showpoint << std::setprecision(3); - // Data entries for (auto& [hash, entry] : hashvec) { + + // Calculate non-HashEntry data + region_number++; + percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); + cumul_walltime += entry.self_walltime_; + self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); + total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); + + // Write everything out std::cout - << std::setw(40) << std::left << entry.region_name_ << " " - << std::setw(15) << std::right << entry.self_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_walltime_.count() << " " - << std::setw(10) << std::right << entry.call_count_ << "\n"; + << std::setw(3) << std::left << region_number + << std::setw(8) << std::left << percent_time << column + << std::setw(8) << std::right << cumul_walltime.count() << column + << std::setw(8) << std::right << entry.self_walltime_.count() << column + << std::setw(8) << std::right << entry.total_walltime_.count() << column + << std::setw(5) << std::right << entry.call_count_ << column + << std::setw(8) << std::right << self_per_call << column + << std::setw(8) << std::right << total_per_call << column + << std::setw(8) << std::right << entry.region_name_ + region_name_tail << "\n"; + } + } /** From 0c6c8b076ba4244e089e119de736c32994e9d9a8 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 4 Nov 2022 13:27:00 +0000 Subject: [PATCH 052/128] Draft solution for combining all hashtables together (#70) --- src/c++/hashtable.cpp | 16 ++++++++++++---- src/c++/hashtable.h | 2 +- src/c++/profiler.cpp | 17 ++++++++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index f9f582b9..1f093464 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -133,9 +133,6 @@ void HashTable::write() std::sort(begin(hashvec), end(hashvec), [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_; }); - // Create a string to append onto the end of the region names - std::string region_name_tail = "@" + std::to_string(tid_); - // Find the highest walltime in table_, which should be the total runtime of // the program. This is used later when calculating '% Time'. double top_walltime = std::max_element @@ -178,12 +175,23 @@ void HashTable::write() << std::setw(5) << std::right << entry.call_count_ << column << std::setw(8) << std::right << self_per_call << column << std::setw(8) << std::right << total_per_call << column - << std::setw(8) << std::right << entry.region_name_ + region_name_tail << "\n"; + << std::setw(8) << std::right << entry.region_name_ << "\n"; } } +/** + * @brief Combines the table_ of two HashTable's together. + * + * @param[in] ht The input HashTable + */ + +void HashTable::combine(const HashTable& ht) +{ + table_.insert(ht.table_.begin(), ht.table_.end()); +} + /** * @brief Computes self times from total times. * diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 04c6e464..eabe39b6 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -73,7 +73,6 @@ class HashTable{ int tid_; std::unordered_map table_; std::hash hash_function_; - std::vector> hashvec; public: @@ -85,6 +84,7 @@ class HashTable{ size_t query_insert(std::string_view) noexcept; void update(size_t, time_duration_t); void write(); + void combine(const HashTable& ht); // Member functions std::vector list_keys(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index f9031633..e9bb0356 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -57,7 +57,8 @@ size_t Profiler::start(std::string_view region_name) assert (tid <= thread_traceback_.size()); // Insert this region into the thread's hash table. - size_t const hash = thread_hashtables_[tid].query_insert(region_name); + std::string new_region_name = std::string(region_name) + "@" + std::to_string(tid); + size_t const hash = thread_hashtables_[tid].query_insert(new_region_name); // Add routine to the traceback. auto start_time = std::chrono::steady_clock::now(); @@ -116,11 +117,21 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - // Write each one + // Remove any empty entries + thread_hashtables_.shrink_to_fit(); + + // Create a new HashTable object that will become a combination of every + // HashTable in thread_hashtables_ + HashTable one_table_to_rule_them_all(999); + + // Fuse together all hashtables for (auto& it : thread_hashtables_) { - it.write(); + one_table_to_rule_them_all.combine(it); } + + // Write out the big one + one_table_to_rule_them_all.write(); } /** From 46a6d11674c4fa6561fb535a630a3e04336b1adb Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 8 Nov 2022 14:43:58 +0000 Subject: [PATCH 053/128] Added HashVec class that collects all hashtables, then sorts & writes entries (#70) --- .vscode/settings.json | 42 ++++++++++++++ src/c++/CMakeLists.txt | 1 + src/c++/hashtable.cpp | 124 +++++------------------------------------ src/c++/hashtable.h | 2 +- src/c++/hashvec.cpp | 104 ++++++++++++++++++++++++++++++++++ src/c++/hashvec.h | 23 ++++++++ src/c++/profiler.cpp | 16 +++--- 7 files changed, 191 insertions(+), 121 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/c++/hashvec.cpp create mode 100644 src/c++/hashvec.h diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..56849390 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,42 @@ +{ + "files.associations": { + "array": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "fstream": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "numeric": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "typeinfo": "cpp", + "string": "cpp" + } +} \ No newline at end of file diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index bd3aa368..3e7a0440 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -13,6 +13,7 @@ include(CMakePackageConfigHelpers) # Add files source files to library. add_library(${CMAKE_PROJECT_NAME} SHARED profiler.h profiler.cpp + hashvec.h hashvec.cpp hashtable.h hashtable.cpp ) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 1f093464..f862d08e 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -4,12 +4,10 @@ under which the code may be used. \*----------------------------------------------------------------------------*/ -#include "hashtable.h" -#include +#include "hashvec.h" #include -#include #include -#include + /** * @brief Constructs a new entry in the hash table. @@ -85,113 +83,6 @@ void HashTable::add_child_time(size_t hash, time_duration_t time_delta) entry.child_walltime_ += time_delta; } -/** - * @brief Writes all entries in the hashtable, sorted according to self times. - * - */ - -void HashTable::write() -{ - - this->compute_self_times(); - - // Headers - std::string column = " "; - - std::cout << "\n"; - std::cout - << std::setw(3) << std::left << "#" - << std::setw(8) << std::left << "% Time" << column - << std::setw(8) << std::right << "Cumul" << column - << std::setw(8) << std::right << "Self" << column - << std::setw(8) << std::right << "Total" << column - << std::setw(5) << std::right << "calls" << column - << std::setw(8) << std::right << "Self" << column - << std::setw(8) << std::right << "Total" << column - << std::setw(8) << std::right << "Routine@" << "\n"; - std::cout - << std::setw(75) << " " - << std::setw(45) << std::left << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; - - // Subheaders - std::cout - << std::setw(3) << std::left << "" - << std::setw(8) << std::left << "(self)" << column - << std::setw(8) << std::right << "(sec)" << column - << std::setw(8) << std::right << "(sec)" << column - << std::setw(8) << std::right << "(sec)" << column - << std::setw(5) << std::right << "" << column - << std::setw(8) << std::right << "ms/call" << column - << std::setw(8) << std::right << "ms/call" << column - << std::setw(8) << std::right << "" << "\n\n"; - - // Create a vector from the hashtable and sort the entries according to self - // walltime. If optimisation of this is needed, it ought to be possible to - // acquire a vector of hash-selftime pairs in the correct order, then use the - // hashes to look up other information directly from the hashtable. - auto hashvec = std::vector>(table_.cbegin(), table_.cend()); - std::sort(begin(hashvec), end(hashvec), - [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_; }); - - // Find the highest walltime in table_, which should be the total runtime of - // the program. This is used later when calculating '% Time'. - double top_walltime = std::max_element - ( - std::begin(hashvec), std::end(hashvec), - [] (auto a, auto b) { - return a.second.total_walltime_ < b.second.total_walltime_; - } - )->second.total_walltime_.count(); - - // Declare any variables external to HashEntry - int region_number = 0; - double percent_time; - time_duration_t cumul_walltime = time_duration_t::zero(); - double self_per_call; - double total_per_call; - - // - // Write data to file - // - - std::cout << std::fixed << std::showpoint << std::setprecision(3); - - for (auto& [hash, entry] : hashvec) { - - // Calculate non-HashEntry data - region_number++; - percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); - cumul_walltime += entry.self_walltime_; - self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); - total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); - - // Write everything out - std::cout - << std::setw(3) << std::left << region_number - << std::setw(8) << std::left << percent_time << column - << std::setw(8) << std::right << cumul_walltime.count() << column - << std::setw(8) << std::right << entry.self_walltime_.count() << column - << std::setw(8) << std::right << entry.total_walltime_.count() << column - << std::setw(5) << std::right << entry.call_count_ << column - << std::setw(8) << std::right << self_per_call << column - << std::setw(8) << std::right << total_per_call << column - << std::setw(8) << std::right << entry.region_name_ << "\n"; - - } - -} - -/** - * @brief Combines the table_ of two HashTable's together. - * - * @param[in] ht The input HashTable - */ - -void HashTable::combine(const HashTable& ht) -{ - table_.insert(ht.table_.begin(), ht.table_.end()); -} - /** * @brief Computes self times from total times. * @@ -219,6 +110,17 @@ std::vector HashTable::list_keys() return keys; } +/** + * @brief Appends table_ onto the end of an input HashVec + * + */ + +void HashTable::append_to(std::vector>* hashvec_ptr) +{ + compute_self_times(); + hashvec_ptr->insert(hashvec_ptr->end(), table_.begin(), table_.end()); +} + /** * @brief Get the total (inclusive) time corresponding to the input hash. * diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index eabe39b6..4d0877cb 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -84,12 +84,12 @@ class HashTable{ size_t query_insert(std::string_view) noexcept; void update(size_t, time_duration_t); void write(); - void combine(const HashTable& ht); // Member functions std::vector list_keys(); void add_child_time(size_t, time_duration_t); void compute_self_times(); + void append_to(std::vector>* hashvec_ptr); // Getters double get_total_walltime(size_t const hash) const; diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp new file mode 100644 index 00000000..4815176c --- /dev/null +++ b/src/c++/hashvec.cpp @@ -0,0 +1,104 @@ + +#include +#include +#include + +#include "hashvec.h" + +std::vector>* HashVec::return_mem_address() +{ + return &hashvec_; +} + +void HashVec::sort() +{ + // Sort from high to low self_walltime_ + std::sort + ( + begin(hashvec_), end(hashvec_), + [] (auto a, auto b) { + return a.second.self_walltime_ > b.second.self_walltime_; + } + ); +} + +void HashVec::write() const +{ + + // Headers + std::string column = " "; + + std::cout << "\n"; + std::cout + << std::setw(3) << std::left << "#" + << std::setw(8) << std::left << "% Time" << column + << std::setw(8) << std::right << "Cumul" << column + << std::setw(8) << std::right << "Self" << column + << std::setw(8) << std::right << "Total" << column + << std::setw(5) << std::right << "calls" << column + << std::setw(8) << std::right << "Self" << column + << std::setw(8) << std::right << "Total" << column + << std::setw(8) << std::right << "Routine@" << "\n"; + std::cout + << std::setw(75) << " " + << std::setw(45) << std::left << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; + + // Subheaders + std::cout + << std::setw(3) << std::left << "" + << std::setw(8) << std::left << "(self)" << column + << std::setw(8) << std::right << "(sec)" << column + << std::setw(8) << std::right << "(sec)" << column + << std::setw(8) << std::right << "(sec)" << column + << std::setw(5) << std::right << "" << column + << std::setw(8) << std::right << "ms/call" << column + << std::setw(8) << std::right << "ms/call" << column + << std::setw(8) << std::right << "" << "\n\n"; + + // Find the highest walltime in table_, which should be the total runtime of + // the program. This is used later when calculating '% Time'. + double top_walltime = std::max_element + ( + std::begin(hashvec_), std::end(hashvec_), + [] (auto a, auto b) { + return a.second.total_walltime_ < b.second.total_walltime_; + } + )->second.total_walltime_.count(); + + // Declare any variables external to HashEntry + int region_number = 0; + double percent_time; + time_duration_t cumul_walltime = time_duration_t::zero(); + double self_per_call; + double total_per_call; + + // + // Write data to file + // + + std::cout << std::fixed << std::showpoint << std::setprecision(3); + + for (auto& [hash, entry] : hashvec_) { + + // Calculate non-HashEntry data + region_number++; + percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); + cumul_walltime += entry.self_walltime_; + self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); + total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); + + // Write everything out + std::cout + << std::setw(3) << std::left << region_number + << std::setw(8) << std::left << percent_time << column + << std::setw(8) << std::right << cumul_walltime.count() << column + << std::setw(8) << std::right << entry.self_walltime_.count() << column + << std::setw(8) << std::right << entry.total_walltime_.count() << column + << std::setw(5) << std::right << entry.call_count_ << column + << std::setw(8) << std::right << self_per_call << column + << std::setw(8) << std::right << total_per_call << column + << std::setw(8) << std::right << entry.region_name_ << "\n"; + + } + +} \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h new file mode 100644 index 00000000..215be566 --- /dev/null +++ b/src/c++/hashvec.h @@ -0,0 +1,23 @@ +#ifndef HASHVEC_H +#define HASHVEC_H + +#include "hashtable.h" + +class HashVec { + + private: + + // The hashvec - a vector of pairs, since std::unordered_maps' cannot + // be sorted via std::sort + std::vector> hashvec_; + + public: + + // Member functions + std::vector>* return_mem_address(); + void sort(); + void write() const; + +}; + +#endif diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index e9bb0356..cd1f6c5f 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -5,6 +5,7 @@ \*----------------------------------------------------------------------------*/ #include "profiler.h" +#include "hashvec.h" #include #include @@ -117,23 +118,20 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - // Remove any empty entries - thread_hashtables_.shrink_to_fit(); - // Create a new HashTable object that will become a combination of every - // HashTable in thread_hashtables_ - HashTable one_table_to_rule_them_all(999); + HashVec new_hashvec; - // Fuse together all hashtables for (auto& it : thread_hashtables_) { - one_table_to_rule_them_all.combine(it); + it.append_to(new_hashvec.return_mem_address()); } - // Write out the big one - one_table_to_rule_them_all.write(); + new_hashvec.sort(); + new_hashvec.write(); + } + /** * @brief Get the total (inclusive) time of everything below the specified hash. * From 9d56df87437db0bf22ec4a908740d02e786822f5 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 8 Nov 2022 14:53:10 +0000 Subject: [PATCH 054/128] Adding banners and fixing includes (#70) --- src/c++/hashtable.cpp | 2 +- src/c++/hashvec.cpp | 5 +++++ src/c++/hashvec.h | 6 ++++++ src/c++/profiler.cpp | 1 - 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index f862d08e..16ca0abc 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -4,7 +4,7 @@ under which the code may be used. \*----------------------------------------------------------------------------*/ -#include "hashvec.h" +#include "hashtable.h" #include #include diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 4815176c..f5be8476 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -1,3 +1,8 @@ +/*----------------------------------------------------------------------------*\ + (c) Crown copyright 2022 Met Office. All rights reserved. + The file LICENCE, distributed with this code, contains details of the terms + under which the code may be used. +\*----------------------------------------------------------------------------*/ #include #include diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 215be566..e43df433 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -1,3 +1,9 @@ +/*----------------------------------------------------------------------------*\ + (c) Crown copyright 2022 Met Office. All rights reserved. + The file LICENCE, distributed with this code, contains details of the terms + under which the code may be used. +\*----------------------------------------------------------------------------*/ + #ifndef HASHVEC_H #define HASHVEC_H diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index cd1f6c5f..7179567b 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -7,7 +7,6 @@ #include "profiler.h" #include "hashvec.h" -#include #include #include From 2df62144b25e47de1daf9f68844000d6333d6f2e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 8 Nov 2022 15:02:51 +0000 Subject: [PATCH 055/128] Switching to getter that returns a reference (instead of using pointers) (#70) --- src/c++/hashtable.cpp | 4 ++-- src/c++/hashtable.h | 2 +- src/c++/hashvec.cpp | 4 ++-- src/c++/hashvec.h | 2 +- src/c++/profiler.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 16ca0abc..b758047e 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -115,10 +115,10 @@ std::vector HashTable::list_keys() * */ -void HashTable::append_to(std::vector>* hashvec_ptr) +void HashTable::append_to(std::vector>& hashvec) { compute_self_times(); - hashvec_ptr->insert(hashvec_ptr->end(), table_.begin(), table_.end()); + hashvec.insert(hashvec.end(), table_.begin(), table_.end()); } /** diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 4d0877cb..020987fd 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -89,7 +89,7 @@ class HashTable{ std::vector list_keys(); void add_child_time(size_t, time_duration_t); void compute_self_times(); - void append_to(std::vector>* hashvec_ptr); + void append_to(std::vector>& hashvec); // Getters double get_total_walltime(size_t const hash) const; diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index f5be8476..08d89825 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -10,9 +10,9 @@ #include "hashvec.h" -std::vector>* HashVec::return_mem_address() +std::vector>* HashVec::get() { - return &hashvec_; + return hashvec_; } void HashVec::sort() diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index e43df433..c9d399cc 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -20,7 +20,7 @@ class HashVec { public: // Member functions - std::vector>* return_mem_address(); + std::vector>& get(); void sort(); void write() const; diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 7179567b..c13659fa 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -122,7 +122,7 @@ void Profiler::write() for (auto& it : thread_hashtables_) { - it.append_to(new_hashvec.return_mem_address()); + it.append_to(new_hashvec.get()); } new_hashvec.sort(); From 7c6fb2fdf6ae104581a422866cec6117a7682ddf Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 8 Nov 2022 15:13:07 +0000 Subject: [PATCH 056/128] Improved comments (#70) --- src/c++/hashvec.cpp | 20 ++++++++++++++++++-- src/c++/hashvec.h | 9 +++++++++ src/c++/profiler.cpp | 1 + 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 08d89825..7657828b 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -10,14 +10,25 @@ #include "hashvec.h" -std::vector>* HashVec::get() +/** + * @brief Getter that returns a reference to the private hashvec. + * + * @param[out] hashvec_ The hashvec itself, a vector of key-value pairs. + * + */ + +std::vector>& HashVec::get() { return hashvec_; } +/** + * @brief CFunction that sorts all entries from high to low self walltime. + * + */ + void HashVec::sort() { - // Sort from high to low self_walltime_ std::sort ( begin(hashvec_), end(hashvec_), @@ -27,6 +38,11 @@ void HashVec::sort() ); } +/** + * @brief Write all data to file. + * + */ + void HashVec::write() const { diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index c9d399cc..53906f10 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -9,6 +9,15 @@ #include "hashtable.h" +/** + * @brief Class containing the "hashvec", a STL vector of key-value pairs. + * + * The hashvec is used to sort from high to low self walltimes, since this + * can't be done on a traditional C++ hashtable. It also serves as a singular + * container for each thread's hashtable to go into. + * + */ + class HashVec { private: diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index c13659fa..00d46b6c 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -9,6 +9,7 @@ #include #include +#include /** * @brief Constructor From 3b378b80c4a1b90e84e56da9073ceaa2392f639c Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Tue, 8 Nov 2022 17:15:03 +0000 Subject: [PATCH 057/128] Capture Fortran time (appending null character). --- src/c++/hashtable.cpp | 2 +- src/c++/hashtable.h | 2 +- src/c++/profiler.cpp | 37 ++++++++++++++++++++++++++++++++++--- src/c++/profiler.h | 2 ++ src/c/profiler_c.cpp | 16 +++++++++++++--- src/f/profiler_mod.F90 | 14 ++++++++++---- 6 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 9469833e..6309d823 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -69,7 +69,7 @@ size_t HashTable::query_insert(std::string_view region_name) noexcept * @param [in] time_delta The time increment to add. */ -void HashTable::update(size_t hash, time_duration_t time_delta) +void HashTable::update(size_t const hash, time_duration_t time_delta) { // Assertions assert (table_.size() > 0); diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 60a4392e..92bd8fe7 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -88,7 +88,7 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; - void update(size_t, time_duration_t); + void update(size_t const, time_duration_t); void write(); // Member functions diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index ad78636a..68002e0f 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -11,6 +11,11 @@ #include #include +// Work around a GNU compiler bug. +extern time_point_t logged_calliper_start_time; +#pragma omp threadprivate(logged_calliper_start_time) +time_point_t logged_calliper_start_time; + /** * @brief Constructor for StartCalliperValues struct. * @@ -59,6 +64,7 @@ Profiler::Profiler() /** * @brief Start timing a profiled code region. + * @detail Calls two other start routines. * @param [in] region_name The code region name. * @returns Unique hash for the code region being started. * @todo Revisit profiling overhead measurement. (#64) @@ -66,9 +72,33 @@ Profiler::Profiler() size_t Profiler::start(std::string_view region_name) { + start1(); + auto hash = start2(region_name); + return hash; +} + +/** + * @brief Start timing a profiled code region, part 1: make a + * threadprivate note of the time. + * @param [in] region_name The code region name. + * @returns Unique hash for the code region being started. + * @todo Revisit profiling overhead measurement. (#64) + */ - // Note the time on entry to the profiler call. - time_point_t calliper_start_time = prof_gettime(); +void Profiler::start1() +{ + logged_calliper_start_time = prof_gettime(); +} + +/** + * @brief Start timing a profiled code region, part 2. + * @param [in] region_name The code region name. + * @returns Unique hash for the code region being started. + * @todo Revisit profiling overhead measurement. (#64) + */ + +size_t Profiler::start2(std::string_view region_name) +{ // Determine the thread number auto tid = static_cast(0); @@ -84,7 +114,8 @@ size_t Profiler::start(std::string_view region_name) // Store the calliper and region start times. auto region_start_time = prof_gettime(); - StartCalliperValues new_times = StartCalliperValues(region_start_time, calliper_start_time); + StartCalliperValues new_times = StartCalliperValues( + region_start_time, logged_calliper_start_time); thread_traceback_[tid].push_back(std::make_pair(hash, new_times)); return hash; diff --git a/src/c++/profiler.h b/src/c++/profiler.h index a2e5b80f..67942bac 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -69,6 +69,8 @@ class Profiler // Member functions size_t start(std::string_view); + void start1(); + size_t start2(std::string_view); void stop (size_t const); void write(); diff --git a/src/c/profiler_c.cpp b/src/c/profiler_c.cpp index da17f223..269f3171 100644 --- a/src/c/profiler_c.cpp +++ b/src/c/profiler_c.cpp @@ -21,7 +21,8 @@ #include extern "C" { - void c_profiler_start(long int&, char const*); + void c_profiler_start1(); + void c_profiler_start2(long int&, char const*); void c_profiler_stop (long int const&); void c_profiler_write(); double c_get_total_walltime(long int const&, int const&); @@ -31,9 +32,18 @@ extern "C" { * @brief Start timing a named region and return a unique handle. */ -void c_profiler_start(long int& hash_out, char const* name) +void c_profiler_start1() { - size_t hash = prof.start( name ); + prof.start1(); +} + +/** + * @brief Start timing a named region and return a unique handle. + */ + +void c_profiler_start2(long int& hash_out, char const* name) +{ + size_t hash = prof.start2( name ); // Ensure that the source and destination have the same size. static_assert(sizeof(hash) == sizeof(hash_out), "Hash/Out size mismatch."); diff --git a/src/f/profiler_mod.F90 b/src/f/profiler_mod.F90 index 769860c7..242898d4 100644 --- a/src/f/profiler_mod.F90 +++ b/src/f/profiler_mod.F90 @@ -37,12 +37,16 @@ module profiler_mod interface - subroutine interface_profiler_start(hash_out, region_name) & - bind(C, name='c_profiler_start') + subroutine interface_profiler_start1() bind(C, name='c_profiler_start1') + !No arguments to handle + end subroutine interface_profiler_start1 + + subroutine interface_profiler_start2(hash_out, region_name) & + bind(C, name='c_profiler_start2') import :: c_char, pik character(kind=c_char, len=1), intent(in) :: region_name(*) integer(kind=pik), intent(out) :: hash_out - end subroutine interface_profiler_start + end subroutine interface_profiler_start2 subroutine profiler_stop(hash_in) bind(C, name='c_profiler_stop') import :: pik @@ -83,9 +87,11 @@ subroutine profiler_start(hash_out, region_name) !Local variables character(len=len_trim(region_name)+1) :: local_region_name + call interface_profiler_start1() + call append_null_char(region_name, local_region_name, len_trim(region_name)) - call interface_profiler_start(hash_out, local_region_name) + call interface_profiler_start2(hash_out, local_region_name) end subroutine profiler_start From 427b68a703841c7e4a5bb661f27fef81c84cd14d Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 9 Nov 2022 11:00:28 +0000 Subject: [PATCH 058/128] Adding in PLACEHOLDER block of info above table (#70) --- src/c++/hashvec.cpp | 94 ++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 7657828b..cc46b644 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -29,13 +29,13 @@ std::vector>& HashVec::get() void HashVec::sort() { - std::sort - ( - begin(hashvec_), end(hashvec_), - [] (auto a, auto b) { - return a.second.self_walltime_ > b.second.self_walltime_; - } - ); + std::sort + ( + begin(hashvec_), end(hashvec_), + [] (auto a, auto b) { + return a.second.self_walltime_ > b.second.self_walltime_; + } + ); } /** @@ -45,36 +45,43 @@ void HashVec::sort() void HashVec::write() const { - - // Headers - std::string column = " "; - - std::cout << "\n"; - std::cout - << std::setw(3) << std::left << "#" - << std::setw(8) << std::left << "% Time" << column - << std::setw(8) << std::right << "Cumul" << column - << std::setw(8) << std::right << "Self" << column - << std::setw(8) << std::right << "Total" << column - << std::setw(5) << std::right << "calls" << column - << std::setw(8) << std::right << "Self" << column - << std::setw(8) << std::right << "Total" << column - << std::setw(8) << std::right << "Routine@" << "\n"; + // Preliminary info + std::cout << " " << "No. of instrumented routines called : 9\n"; + std::cout << " " << "Instrumentation started : 20100521 171238\n"; + std::cout << " " << "Instrumentation ended : 20100521 172033\n"; + std::cout << " " << "Instrumentation overhead: 0.90%\n"; + std::cout << " " << "Memory usage : Memory usage : 1346 MBytes (heap), 1315 MBytes (rss), 796 MBytes (stack), 1116 (paging)\n"; + std::cout << " " << "Wall-time is 472.28 sec on proc#1 (192 procs, 1 threads)\n"; + std::cout << " " << "Thread#1: 472.28 sec (100.00%)\n" << std::endl; + + // Table Headers std::cout - << std::setw(75) << " " - << std::setw(45) << std::left << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; + << " " + << std::setw(3) << std::left << "#" + << std::setw(7) << std::left << "% Time" + << std::setw(13) << std::right << "Cumul" + << std::setw(13) << std::right << "Self" + << std::setw(13) << std::right << "Total" + << std::setw(15) << std::right << "# of calls" + << std::setw(12) << std::right << "Self" + << std::setw(12) << std::right << "Total" << " " + << "Routine@" << "\n"; + std::cout << " " + << std::setw(73) << "" + << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; // Subheaders std::cout - << std::setw(3) << std::left << "" - << std::setw(8) << std::left << "(self)" << column - << std::setw(8) << std::right << "(sec)" << column - << std::setw(8) << std::right << "(sec)" << column - << std::setw(8) << std::right << "(sec)" << column - << std::setw(5) << std::right << "" << column - << std::setw(8) << std::right << "ms/call" << column - << std::setw(8) << std::right << "ms/call" << column - << std::setw(8) << std::right << "" << "\n\n"; + << " " + << std::setw(3) << std::left << "" + << std::setw(7) << std::right << "(self)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(15) << std::right << "" + << std::setw(12) << std::right << "ms/call" + << std::setw(12) << std::right << "ms/call" + << "\n\n"; // Find the highest walltime in table_, which should be the total runtime of // the program. This is used later when calculating '% Time'. @@ -109,16 +116,17 @@ void HashVec::write() const total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); // Write everything out - std::cout - << std::setw(3) << std::left << region_number - << std::setw(8) << std::left << percent_time << column - << std::setw(8) << std::right << cumul_walltime.count() << column - << std::setw(8) << std::right << entry.self_walltime_.count() << column - << std::setw(8) << std::right << entry.total_walltime_.count() << column - << std::setw(5) << std::right << entry.call_count_ << column - << std::setw(8) << std::right << self_per_call << column - << std::setw(8) << std::right << total_per_call << column - << std::setw(8) << std::right << entry.region_name_ << "\n"; + std::cout + << " " + << std::setw(3) << std::left << region_number + << std::setw(7) << std::right << percent_time + << std::setw(13) << std::right << cumul_walltime.count() + << std::setw(13) << std::right << entry.self_walltime_.count() + << std::setw(13) << std::right << entry.total_walltime_.count() + << std::setw(15) << std::right << entry.call_count_ + << std::setw(12) << std::right << self_per_call + << std::setw(12) << std::right << total_per_call << " " + << entry.region_name_ << "\n"; } From b16942103fe85f2481aecfa950d5899a4d7ef548 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 9 Nov 2022 16:28:54 +0000 Subject: [PATCH 059/128] Switching to functions rather than single-method objects (#52) --- src/c++/profiler.cpp | 12 +++++- src/c++/writer.cpp | 94 ++++++++++++++----------------------------- src/c++/writer.h | 96 +++++++++++--------------------------------- 3 files changed, 65 insertions(+), 137 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 548c88ec..0a204e5d 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -123,8 +123,16 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - Writer scribe; - scribe.write(thread_hashtables_); + + const char* user_strat = std::getenv("PROF_IO_MODE"); + + if ( user_strat == NULL || static_cast(user_strat) == "MultipleFiles" ) + { + Writer scribe(Writer::MultiFile); + scribe.executeStrategy(thread_hashtables_); + } + else throw std::runtime_error("Invalid PROF_IO_MODE choice"); + } /** diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index b190a0b3..69d81f52 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -7,16 +7,37 @@ #include "writer.h" #include +#include /** - * @brief Writes out all entires in the hashtable into a separate file for - * each mpi rank. + * @brief Writer constructor. + * + */ + +Writer::Writer(std::function)> in) + : strategy_(std::move(in)) + {} + +/** + * @brief Method that executes whatever function is passed to strategy_ via the + * constructor. + * + */ + +void Writer::executeStrategy(std::vector htv) +{ + strategy_(htv); +} + +/** + * @brief The multiple-output-files strategy. * - * @param[in] htvec A vector of hashtables. + * Writes out data from the vector of HashTables given to it into one file per + * mpi rank. * */ -void MultipleFiles::write(std::vector htvec) +void Writer::MultiFile(std::vector htv) { // Find current MPI rank int current_rank; @@ -41,70 +62,17 @@ void MultipleFiles::write(std::vector htvec) delete env_variable; out_filename = "profiler-output" + mpi_filename_tail; } + std::ofstream output_stream; output_stream.open(out_filename); - // Write to file then close it - for (auto& it : htvec) + // + // Write to file + // + for (auto& it : htv) { it.print(output_stream); } + output_stream.flush(); output_stream.close(); -} - -/** - * @brief Context class constructor. - * - * @param[in] temp_ptr Pointer to IO Interface, which will be empty after - * ownership is transferred to io_strategy_ - * - */ - -IO_StrategyContext::IO_StrategyContext(std::unique_ptr temp_ptr) - : io_strategy_(std::move(temp_ptr)) - {} - -/** - * @brief Allows the strategy to be switched. - * - * @param[in] temp_ptr Pointer to IO Interface, which will be empty after - * ownership is transferred to io_strategy_ - * - */ - -void IO_StrategyContext::setStrategy(std::unique_ptr temp_ptr) -{ - io_strategy_ = std::move(temp_ptr); -} - -/** - * @brief Calls the write method linked to whichever derived class the - * strategy is referencing. - * - * @param[in] htvec A vector of hashtables. - * - */ - -void IO_StrategyContext::executeStrategy(std::vector htvec) -{ - io_strategy_->write(htvec); -} - -/** - * @brief Strategy decision is made and context class object created in order - * to execute the appropriate write method. - * - * @param[in] htvec A vector of hashtables. - * - */ - -void Writer::write(std::vector htvec) -{ - const char* user_strat = std::getenv("PROF_IO_MODE"); - if ( user_strat == NULL || static_cast(user_strat) == "MultipleFiles") - { - IO_StrategyContext contextObj(std::move(std::make_unique())); - contextObj.executeStrategy(htvec); - } - else throw std::runtime_error("Invalid PROF_IO_MODE value"); } \ No newline at end of file diff --git a/src/c++/writer.h b/src/c++/writer.h index 04905679..a161a5e5 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -7,96 +7,48 @@ /** * @file writer.h - * @brief Contains writer class and handles IO. + * @brief Contains the writer class, which handles IO. * - * Contains a strategy method made up of a base IO class + derived classes, - * aswell as context and writer classes which handle tasks such as creation - * of pointers and calling the correct methods. + * Implements a strategy pattern that favours using functions over + * single-method objects, hence reducing the number of classes required. * */ +#ifndef WRITER_H +#define WRITER_H + #include -#include -#include +#include #include "hashtable.h" /** - * @brief IO interface class. + * @brief Writer class, which sits above hashtable and is constructed + * within the profiler. * - * An interface-style base class which contains a pure virtual write method, - * which can then be overloaded by derived classes. + * Any callable function can be passed into the writer class constructor and + * subsequently executed using the execute method. Different strategies can be + * included as static member functions, such as MultiFile. * */ -class IO_Interface { - - protected: +class Writer { - std::ofstream output_stream; + private: - public: + // Strategy + std::function)> strategy_; - // Virtual constructor - virtual ~IO_Interface() = default; + public: - // Pure virtual "write" method - virtual void write(std::vector) = 0; - -}; + // Constructor + explicit Writer(std::function)>); -/** - * @brief Derived class for multiple-file output. - * - * Overloads the pure virtual write method in IO_Interface. - * - */ + // Execution method + void executeStrategy(std::vector); -class MultipleFiles : public IO_Interface { + // Strategies + static void MultiFile(std::vector); - public: - - void write(std::vector ht) override; - }; -/** - * @brief Context class. - * - * A key part of any strategy method which is responsible for maintaining - * a reference to one of the interface's derived classes. It has an execution - * method which calls the appropriate derived class write method. - * - */ - -class IO_StrategyContext { - - private: - - std::unique_ptr io_strategy_; - - public: - - // Constructor - explicit IO_StrategyContext(std::unique_ptr temp_ptr); - - // Methods - void setStrategy(std::unique_ptr temp_ptr); - void executeStrategy(std::vector ht); - -}; - -/** - * @brief Writer class. - * - * A class that picks a strategy and creates a context class object. This - * class serves as something for the profiler to interface with. - * - */ - -class Writer { - - public: - - void write(std::vector ht); - -}; \ No newline at end of file +#endif From 91f430c70a98f59a3083ead04307801bad0c2e41 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 9 Nov 2022 16:50:20 +0000 Subject: [PATCH 060/128] Move post final gettime time updates to pointer / ref increments. --- src/c++/hashtable.cpp | 66 +++++++++---------------------------------- src/c++/hashtable.h | 6 ++-- src/c++/profiler.cpp | 28 +++++++++++------- 3 files changed, 34 insertions(+), 66 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 6309d823..6b5528f1 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -80,49 +80,34 @@ void HashTable::update(size_t const hash, time_duration_t time_delta) entry.total_walltime_ += time_delta; // Update the number of times this region has been called - entry.call_count_++; + ++entry.call_count_; - // Also increment the number of calliper-pairs called. - auto& profiler_entry = table_.at(profiler_hash_); - profiler_entry.call_count_++; } /** - * @brief Add child region and overhead times to parent. - * @param [in] hash The hash of the child region to update. - * @param [in] time_delta The time spent in the child region. + * @brief + * */ -void HashTable::add_child_time(size_t const hash, - time_duration_t const time_delta) +time_duration_t* HashTable::add_child_time( + size_t const hash, + time_duration_t child_walltime) { - // Assertions - assert (table_.size() > 0); - assert (table_.count(hash) > 0); - auto& entry = table_.at(hash); - - // Increment the child time for this entry - entry.child_walltime_ += time_delta; + entry.child_walltime_ += child_walltime; + return &entry.overhead_walltime_; } /** - * @brief Add child region and overhead times to parent. - * @param [in] hash The hash of the child region to update. - * @param [in] time_delta The time spent in the child region. + * @brief + * */ -void HashTable::add_overhead_time(size_t const hash, - time_duration_t const calliper_time) +time_duration_t& HashTable::increment_profiler_calls() { - // Assertions - assert (table_.size() > 0); - assert (table_.count(hash) > 0); - - auto& entry = table_.at(hash); - - // Increment the overhead time for this entry - entry.overhead_walltime_ += calliper_time; + auto& entry = table_.at(profiler_hash_); + ++entry.call_count_; + return entry.total_walltime_; } /** @@ -131,29 +116,6 @@ void HashTable::add_overhead_time(size_t const hash, * @param [in] time_delta The time spent in the child region. */ -void HashTable::add_subtimes(size_t const hash, - time_duration_t const time_delta, - time_duration_t const calliper_time) -{ - // Assertions - assert (table_.size() > 0); - assert (table_.count(hash) > 0); - - auto& entry = table_.at(hash); - - // Increment the child time for this entry - entry.child_walltime_ += time_delta; - - // Increment the overhead time for this entry - entry.overhead_walltime_ += calliper_time; -} - -void HashTable::add_total_overhead_time(time_duration_t const calliper_time) -{ - auto& entry = table_.at(profiler_hash_); - entry.total_walltime_ += calliper_time; -} - /** * @brief Writes all entries in the hashtable, sorted according to self times. * diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 92bd8fe7..768f6d60 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -93,10 +93,8 @@ class HashTable{ // Member functions std::vector list_keys(); - void add_subtimes (size_t const, time_duration_t const, time_duration_t const); - void add_child_time (size_t const, time_duration_t const); - void add_overhead_time (size_t const, time_duration_t const); - void add_total_overhead_time(time_duration_t const); + time_duration_t* add_child_time(size_t const, time_duration_t); + time_duration_t& increment_profiler_calls(); // Getters double get_total_walltime(size_t const hash) const; diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 68002e0f..d5c1d27b 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -174,20 +174,28 @@ void Profiler::stop(size_t const hash) // Remove from the end of the list. thread_traceback_[tid].pop_back(); + // The sequence of code that follows is aimed at leaving only minimal and + // simple operations after the call to prof_gettime(). + time_duration_t* parent_overhead_time_ptr = nullptr; + + // Acquire parent pointers + if (! thread_traceback_[tid].empty()){ + size_t parent_hash = thread_traceback_[tid].back().first; + parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( + parent_hash, region_duration); + } + + // Increment profiler calls, and get a reference to the total overhead time. + auto& total_overhead_time = thread_hashtables_[tid].increment_profiler_calls(); + // Account for time spent in the profiler itself. auto calliper_stop_time = prof_gettime(); auto calliper_time = calliper_stop_time - temp_sum; - // Add child and overhead times to parent. - if (! thread_traceback_[tid].empty()) { - size_t parent_hash = thread_traceback_[tid].back().first; - //thread_hashtables_[tid].add_child_time (parent_hash, region_duration); - //thread_hashtables_[tid].add_overhead_time(parent_hash, calliper_time); - thread_hashtables_[tid].add_subtimes(parent_hash, region_duration, calliper_time); - } - - // Add overhead time to the separate profiler entry. - thread_hashtables_[tid].add_total_overhead_time(calliper_time); + // Increment the overhead time both the parent and total overhead times, using + // previously-obtained pointers or references, as appropriate. + if(parent_overhead_time_ptr){ *parent_overhead_time_ptr += calliper_time; } + total_overhead_time += calliper_time; } /** From dbc5a8a8d66f1ef27f2b0d0e91118c08c93cae65 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 08:45:30 +0000 Subject: [PATCH 061/128] Attempting to debug mpi issue in workflow (#52) --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f92994c7..beea0640 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: {c: gcc-10, cpp: g++-10, fortran: gfortran-10}, {c: clang-12, cpp: clang++-12, fortran: gfortran-10} ] - + steps: - uses: actions/checkout@v2 @@ -33,6 +33,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libomp-12-dev + which mpirun - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From c7cb1331edce2f13198cb272fd36e2ab5be2a4ed Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 08:49:30 +0000 Subject: [PATCH 062/128] Installing mpich in workflow (#52) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index beea0640..2cb74c85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libomp-12-dev - which mpirun + sudo apt install mpich - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From 9cfa15381950e790115d33e26a940678c46006fd Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 08:53:27 +0000 Subject: [PATCH 063/128] Changing workflow step name to reflect mpich installation (#52) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2cb74c85..da7be79c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,12 +28,12 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Install libomp-devel + - name: Install libomp-devel and mpich # Ensure that the OpenMP development libraries are installed. run: | sudo apt-get update sudo apt-get install -y libomp-12-dev - sudo apt install mpich + sudo apt install -y mpich - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From b4e32fd2578afa16b37694b4d9d53c803149868c Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 09:05:19 +0000 Subject: [PATCH 064/128] Slight comment changes (#52) --- src/c++/profiler.cpp | 1 + src/c++/writer.cpp | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 7a8ffd9e..3109541a 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -169,6 +169,7 @@ void Profiler::stop(size_t const hash) void Profiler::write() { + // Find user-defined IO method (will return NULL if empty) const char* user_strat = std::getenv("PROF_IO_MODE"); if ( user_strat == NULL || static_cast(user_strat) == "MultipleFiles" ) diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index 69d81f52..f4722184 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -44,22 +44,20 @@ void Writer::MultiFile(std::vector htv) MPI_Comm prof_comm_ = MPI_COMM_WORLD; MPI_Comm_rank(prof_comm_, ¤t_rank); - // Filename "tail" - will be different for each rank and appended onto the - // end of the output file name + // For later appending onto the end of the output file name std::string mpi_filename_tail = "-" + std::to_string(current_rank); // Pickup environment variable filename if it exists, if not use the // default name of "profiler-output". In either case, include the MPI rank // in the name of the file. - const char* env_variable = std::getenv("PROF_OUTFILE"); std::string out_filename; + const char* env_variable = std::getenv("PROF_OUTFILE"); if (env_variable != NULL) { - out_filename = env_variable + mpi_filename_tail; + out_filename = static_cast(env_variable) + mpi_filename_tail; } else { - delete env_variable; out_filename = "profiler-output" + mpi_filename_tail; } std::ofstream output_stream; From 64365720a2c665f052787dcaac6ca37dcc0f9b7e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 13:02:57 +0000 Subject: [PATCH 065/128] Printing mpich version within workflow (#52) --- .github/workflows/build.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da7be79c..c2de288e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,10 +31,16 @@ jobs: - name: Install libomp-devel and mpich # Ensure that the OpenMP development libraries are installed. run: | - sudo apt-get update - sudo apt-get install -y libomp-12-dev + sudo apt update + sudo apt install -y libomp-12-dev sudo apt install -y mpich + - name: Check versions + # Print the version number of installed libraries + run: | + gcc --version + mpichversion + - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type From a426c3bfcdb1610db5eb53771e85e6c06fd90077 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 13:13:57 +0000 Subject: [PATCH 066/128] Trying to print OpenMP version (#52) --- .github/workflows/build.yml | 1 + README.md | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2de288e..d652640e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,7 @@ jobs: # Print the version number of installed libraries run: | gcc --version + $ echo |cpp -fopenmp -dM |grep -i open mpichversion - name: Configure CMake diff --git a/README.md b/README.md index 83cd14d3..baa70091 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ The code has been tested with the following compilers: - GCC versions: 9.3.0, 10.2.0 - Clang versions: 12.0 +### Libraries + +- OpenMP 5.0 (from GCC 9.4.0) +- MPICH 3.3.2 + ### Testing Framework and Documentation The testing framework is GoogleTest (1.11.0). From 244295f79b3be2976f6fa6492a65e1ab6358c8b8 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 10 Nov 2022 13:19:39 +0000 Subject: [PATCH 067/128] Updating readme with correct OpenMP version number (#52) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index baa70091..7edb48e9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The code has been tested with the following compilers: ### Libraries -- OpenMP 5.0 (from GCC 9.4.0) +- OpenMP 4.5 - MPICH 3.3.2 ### Testing Framework and Documentation From cd1256b6218a7fae03f6b6e7701e34d2d2670873 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 11 Nov 2022 09:37:13 +0000 Subject: [PATCH 068/128] Change the traceback from std::vector to std::array. --- src/c++/hashtable.cpp | 6 +++++- src/c++/hashtable.h | 2 +- src/c++/profiler.cpp | 39 +++++++++++++++++++++++++++------------ src/c++/profiler.h | 10 +++++++--- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 6b5528f1..f97b6e4e 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -69,7 +69,7 @@ size_t HashTable::query_insert(std::string_view region_name) noexcept * @param [in] time_delta The time increment to add. */ -void HashTable::update(size_t const hash, time_duration_t time_delta) +void HashTable::update(size_t const hash, time_duration_t const time_delta) { // Assertions assert (table_.size() > 0); @@ -93,6 +93,10 @@ time_duration_t* HashTable::add_child_time( size_t const hash, time_duration_t child_walltime) { + // Assertions + assert (table_.size() > 0); + assert (table_.count(hash) > 0); + auto& entry = table_.at(hash); entry.child_walltime_ += child_walltime; return &entry.overhead_walltime_; diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 768f6d60..cfdcc620 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -88,7 +88,7 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; - void update(size_t const, time_duration_t); + void update(size_t const, time_duration_t const); void write(); // Member functions diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index d5c1d27b..ace56ec4 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -16,11 +16,18 @@ extern time_point_t logged_calliper_start_time; #pragma omp threadprivate(logged_calliper_start_time) time_point_t logged_calliper_start_time; +extern int call_depth; +#pragma omp threadprivate(call_depth) +int call_depth; + /** * @brief Constructor for StartCalliperValues struct. * */ +Profiler::StartCalliperValues::StartCalliperValues() + {} + Profiler::StartCalliperValues::StartCalliperValues( time_point_t region_start_time, time_point_t calliper_start_time) @@ -51,11 +58,14 @@ Profiler::Profiler() thread_hashtables_.push_back(new_table); // Create a new list - std::vector> new_list; + std::array, PROF_MAX_TRACEBACK_SIZE> new_list; thread_traceback_.push_back(new_list); - } + // Initialise the call depth, which is threadprivate. Used as an array index, + // so the first element will be indexed by 0. + call_depth = -1; + // Assertions assert ( static_cast (thread_hashtables_.size()) == max_threads_); assert ( static_cast (thread_traceback_.size() ) == max_threads_); @@ -116,7 +126,11 @@ size_t Profiler::start2(std::string_view region_name) auto region_start_time = prof_gettime(); StartCalliperValues new_times = StartCalliperValues( region_start_time, logged_calliper_start_time); - thread_traceback_[tid].push_back(std::make_pair(hash, new_times)); + + + ++call_depth; + auto call_depth_it = static_cast(call_depth); + thread_traceback_[tid].at(call_depth_it) = std::make_pair(hash, std::move(new_times)); return hash; } @@ -135,8 +149,8 @@ size_t Profiler::start2(std::string_view region_name) void Profiler::stop(size_t const hash) { - // Log the region stop time. - auto region_stop_time = prof_gettime(); + // Log the region stop time. + auto region_stop_time = prof_gettime(); // Determine the thread number auto tid = static_cast(0); @@ -145,7 +159,8 @@ void Profiler::stop(size_t const hash) #endif // Checks - which hash is last on the traceback list? - size_t last_hash_on_list = thread_traceback_[tid].back().first; + auto call_depth_it = static_cast(call_depth); + size_t last_hash_on_list = thread_traceback_[tid].at(call_depth_it).first; // Check that the hash is the one we expect. If it isn't, there is an error in // the instrumentation. @@ -155,8 +170,7 @@ void Profiler::stop(size_t const hash) } // Get start calliper values needed for subsequent computation. - StartCalliperValues& start_calliper_times = - thread_traceback_[tid].back().second; + auto& start_calliper_times = thread_traceback_[tid].at(call_depth_it).second; // Compute the region time auto region_duration = region_stop_time - start_calliper_times.region_start_time_; @@ -171,16 +185,17 @@ void Profiler::stop(size_t const hash) // calliper_time = (t4-t1) - (t3-t2) = t4 - ( t3-t2 + t1) auto temp_sum = start_calliper_times.calliper_start_time_ + region_duration; - // Remove from the end of the list. - thread_traceback_[tid].pop_back(); + // Move the end of the list. + --call_depth; // The sequence of code that follows is aimed at leaving only minimal and // simple operations after the call to prof_gettime(). time_duration_t* parent_overhead_time_ptr = nullptr; // Acquire parent pointers - if (! thread_traceback_[tid].empty()){ - size_t parent_hash = thread_traceback_[tid].back().first; + if (call_depth > 0){ + call_depth_it = static_cast(call_depth); + size_t parent_hash = thread_traceback_[tid].at(call_depth_it).first; parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( parent_hash, region_duration); } diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 67942bac..b5984d3c 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -19,10 +19,12 @@ #include #include #include +#include #include #include "hashtable.h" +#define PROF_MAX_TRACEBACK_SIZE 1000 /** * @brief Top-level profiler class. @@ -45,6 +47,7 @@ class Profiler public: // Constructors + StartCalliperValues(); StartCalliperValues(time_point_t, time_point_t); // Data members @@ -55,12 +58,13 @@ class Profiler // Data members int max_threads_; - std::vector thread_hashtables_; - std::vector>> thread_traceback_; + std::vector thread_hashtables_; + std::vector,PROF_MAX_TRACEBACK_SIZE>> thread_traceback_; // Type definitions for vector array indexing. typedef std::vector::size_type hashtable_iterator_t_; - typedef std::vector>>::size_type pair_iterator_t_; + typedef std::vector,PROF_MAX_TRACEBACK_SIZE>> + ::size_type pair_iterator_t_; public: From f16bbc45a541d5fa0d10efc9c93dd5a5c9014b10 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 11 Nov 2022 10:26:05 +0000 Subject: [PATCH 069/128] Catch the case where a stop calliper is called before a start calliper. --- src/c++/profiler.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index ace56ec4..aca6438a 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -158,12 +158,17 @@ void Profiler::stop(size_t const hash) tid = static_cast(omp_get_thread_num()); #endif - // Checks - which hash is last on the traceback list? auto call_depth_it = static_cast(call_depth); - size_t last_hash_on_list = thread_traceback_[tid].at(call_depth_it).first; - // Check that the hash is the one we expect. If it isn't, there is an error in - // the instrumentation. + // Check that we have called a start calliper before the stop calliper. + // If not, then the call depth would be -1. + if (call_depth < 0) { + std::cerr << "EMERGENCY STOP: stop called before start calliper." << "\n"; + exit (101); + } + + // Check: which hash is last on the traceback list? + size_t last_hash_on_list = thread_traceback_[tid].at(call_depth_it).first; if (hash != last_hash_on_list){ std::cerr << "EMERGENCY STOP: hashes don't match." << "\n"; exit (100); From 28127da80977de1cef2fc7af32cd8acf33040053 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 11 Nov 2022 15:22:07 +0000 Subject: [PATCH 070/128] Double stdfunction strategy pattern that includes ouput format (#72) --- .gitignore | 1 + .vscode/settings.json | 42 ------ src/c++/CMakeLists.txt | 8 +- src/c++/formatter.cpp | 176 +++++++++++++++++++++++ src/c++/formatter.h | 62 ++++++++ src/c++/hashtable.cpp | 64 ++------- src/c++/hashtable.h | 6 +- src/c++/hashvec.cpp | 133 ----------------- src/c++/hashvec.h | 38 ----- src/c++/prof_gettime.h | 2 + src/c++/profiler.cpp | 13 +- src/c++/writer.cpp | 21 +-- src/c++/writer.h | 9 +- tests/unit_tests/c++/test_hashtable.cpp | 6 +- tests/unit_tests/c++/test_regionname.cpp | 4 +- 15 files changed, 285 insertions(+), 300 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 src/c++/formatter.cpp create mode 100644 src/c++/formatter.h delete mode 100644 src/c++/hashvec.cpp delete mode 100644 src/c++/hashvec.h diff --git a/.gitignore b/.gitignore index 48d5cd05..a4849c00 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ flycheck_*.el ### Visual Studio Code template .vscode* .vscode/* +.vscode/settings.json !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 56849390..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "files.associations": { - "array": "cpp", - "*.tcc": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "cstdarg": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "fstream": "cpp", - "functional": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "new": "cpp", - "ostream": "cpp", - "numeric": "cpp", - "ratio": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "typeinfo": "cpp", - "string": "cpp" - } -} \ No newline at end of file diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index c630e93f..7100360e 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -12,11 +12,11 @@ include(CMakePackageConfigHelpers) # Add files source files to library. add_library(${CMAKE_PROJECT_NAME} SHARED - profiler.h profiler.cpp + profiler.h profiler.cpp prof_gettime.h prof_gettime.cpp - writer.h writer.cpp - hashvec.h hashvec.cpp - hashtable.h hashtable.cpp + writer.h writer.cpp + formatter.h formatter.cpp + hashtable.h hashtable.cpp ) # Link library to and external libs (also use project warnings and options). diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp new file mode 100644 index 00000000..59c99719 --- /dev/null +++ b/src/c++/formatter.cpp @@ -0,0 +1,176 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +#include "formatter.h" +#include +#include + +/** + * @brief Formatter constructor. + * + */ + +Formatter::Formatter(std::function in) + : strategy_(std::move(in)) + {} + +// + +std::vector>& Formatter::get_hashvec() +{ + return hashvec_; +} + +// + +void Formatter::sort_hashvec() +{ + std::sort + ( + begin(hashvec_), end(hashvec_), + [] (auto a, auto b) { + return a.second.self_walltime_ > b.second.self_walltime_; + } + ); +} + +// + +void Formatter::executeStrategy(std::ofstream& output_stream, Formatter& formatter) +{ + strategy_(output_stream, formatter); +} + +// + +void Format::Standard(std::ofstream& output_stream, Formatter& formatter) +{ + + std::string routine_at_thread = "Thread: " /*+ std::to_string(tid_)*/; + + // Write headings + output_stream << "\n"; + output_stream + << std::setw(40) << std::left << routine_at_thread << " " + << std::setw(15) << std::right << "Self (s)" << " " + << std::setw(15) << std::right << "Total (raw) (s)" << " " + << std::setw(15) << std::right << "Total (s)" << " " + << std::setw(10) << std::right << "Calls" << "\n"; + + output_stream << std::setfill('-'); + output_stream + << std::setw(40) << "-" << " " + << std::setw(15) << "-" << " " + << std::setw(15) << "-" << " " + << std::setw(15) << "-" << " " + << std::setw(10) << "-" << "\n"; + output_stream << std::setfill(' '); + + // Data entries + for (auto& [hash, entry] : formatter.get_hashvec()) { + output_stream + << std::setw(40) << std::left << entry.region_name_ << " " + << std::setw(15) << std::right << entry.self_walltime_.count() << " " + << std::setw(15) << std::right << entry.total_raw_walltime_.count() << " " + << std::setw(15) << std::right << entry.total_walltime_.count() << " " + << std::setw(10) << std::right << entry.call_count_ << "\n"; + } + +} + +// + +void Format::DrHook(std::ofstream& output_stream, Formatter& formatter) +{ + // Preliminary info + output_stream << " " << "No. of instrumented routines called : 9\n"; + output_stream << " " << "Instrumentation started : 20100521 171238\n"; + output_stream << " " << "Instrumentation ended : 20100521 172033\n"; + output_stream << " " << "Instrumentation overhead: 0.90%\n"; + output_stream << " " << "Memory usage : Memory usage : 1346 MBytes (heap), 1315 MBytes (rss), 796 MBytes (stack), 1116 (paging)\n"; + output_stream << " " << "Wall-time is 472.28 sec on proc#1 (192 procs, 1 threads)\n"; + output_stream << " " << "Thread#1: 472.28 sec (100.00%)" << std::endl; + + // Table Headers + output_stream << "\n"; + output_stream + << " " + << std::setw(3) << std::left << "#" + << std::setw(7) << std::left << "% Time" + << std::setw(13) << std::right << "Cumul" + << std::setw(13) << std::right << "Self" + << std::setw(13) << std::right << "Total" + << std::setw(15) << std::right << "# of calls" + << std::setw(12) << std::right << "Self" + << std::setw(12) << std::right << "Total" << " " + << "Routine@" << "\n"; + output_stream + << " " + << std::setw(73) << "" + << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; + + // Subheaders + output_stream + << " " + << std::setw(3) << std::left << "" + << std::setw(7) << std::right << "(self)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(15) << std::right << "" + << std::setw(12) << std::right << "ms/call" + << std::setw(12) << std::right << "ms/call" + << "\n\n"; + + // Find the highest walltime in table_, which should be the total runtime of + // the program. This is used later when calculating '% Time'. + double top_walltime = std::max_element + ( + std::begin(formatter.get_hashvec()), std::end(formatter.get_hashvec()), + [] (auto a, auto b) { + return a.second.total_walltime_ < b.second.total_walltime_; + } + )->second.total_walltime_.count(); + + // Declare any variables external to HashEntry + int region_number = 0; + double percent_time; + time_duration_t cumul_walltime = time_duration_t::zero(); + double self_per_call; + double total_per_call; + + // + // Write data to file + // + + output_stream << std::fixed << std::showpoint << std::setprecision(3); + + for (auto& [hash, entry] : formatter.get_hashvec()) { + + // Calculate non-HashEntry data + region_number++; + percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); + cumul_walltime += entry.self_walltime_; + self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); + total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); + + // Write everything out + output_stream + << " " + << std::setw(3) << std::left << region_number + << std::setw(7) << std::right << percent_time + << std::setw(13) << std::right << cumul_walltime.count() + << std::setw(13) << std::right << entry.self_walltime_.count() + << std::setw(13) << std::right << entry.total_walltime_.count() + << std::setw(15) << std::right << entry.call_count_ + << std::setw(12) << std::right << self_per_call + << std::setw(12) << std::right << total_per_call << " " + << entry.region_name_ << "\n"; + + } + +} \ No newline at end of file diff --git a/src/c++/formatter.h b/src/c++/formatter.h new file mode 100644 index 00000000..4af9b96e --- /dev/null +++ b/src/c++/formatter.h @@ -0,0 +1,62 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +/** + * @file formatter.h + * @brief + + * + */ + +#ifndef FORMATTER_H +#define FORMATTER_H + +#include +#include +#include "hashtable.h" + +/** + * @brief + * + * + */ + +class Formatter { + + private: + + // Write strategy + std::function strategy_; + + // Hashvec + std::vector> hashvec_; + + public: + + // Constructor + explicit Formatter(std::function); + + // Member functions + std::vector>& get_hashvec(); + void sort_hashvec(); + + // Execution method + void executeStrategy(std::ofstream&, Formatter&); + +}; + +struct Format { + + public: + + // Strategies + static void Standard(std::ofstream&, Formatter&); + static void DrHook(std::ofstream&, Formatter&); + +}; + +#endif \ No newline at end of file diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 77cc046f..a05d108c 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -6,8 +6,6 @@ #include "hashtable.h" #include -#include - /** * @brief Constructs a new entry in the hash table. @@ -33,9 +31,8 @@ HashEntry::HashEntry(std::string_view region_name) HashTable::HashTable(int const tid) : tid_(tid) { - // Set the name and hash of the profiler entry. - std::string const profiler_name = "__profiler__"; + std::string const profiler_name = "__profiler__@" + std::to_string(tid); profiler_hash_ = hash_function_(profiler_name); // Insert special entry for the profiler overhead time. @@ -115,56 +112,6 @@ void HashTable::add_overhead_time(size_t const hash, time_duration_t calliper_ti entry.overhead_walltime_ += calliper_time; } -/** - * @brief Writes all entries in the hashtable, sorted according to self times. - * - */ - -void HashTable::print(std::ostream& outstream) -{ - - // Ensure all computed times are up-to-date. - prepare_computed_times_all(); - - std::string routine_at_thread = "Thread: " + std::to_string(tid_); - - // Write headings - outstream << "\n"; - outstream - << std::setw(40) << std::left << routine_at_thread << " " - << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (raw) (s)" << " " - << std::setw(15) << std::right << "Total (s)" << " " - << std::setw(10) << std::right << "Calls" << "\n"; - - outstream << std::setfill('-'); - outstream - << std::setw(40) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(10) << "-" << "\n"; - outstream << std::setfill(' '); - - // Create a vector from the hashtable and sort the entries according to self - // walltime. If optimisation of this is needed, it ought to be possible to - // acquire a vector of hash-selftime pairs in the correct order, then use the - // hashes to look up other information directly from the hashtable. - auto hashvec = std::vector>(table_.cbegin(), table_.cend()); - std::sort(begin(hashvec), end(hashvec), - [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_;}); - - // Data entries - for (auto& [hash, entry] : hashvec) { - outstream - << std::setw(40) << std::left << entry.region_name_ << " " - << std::setw(15) << std::right << entry.self_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_raw_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_walltime_.count() << " " - << std::setw(10) << std::right << entry.call_count_ << "\n"; - } -} - /** * @brief Evaluates times derived from other times measured, for a particular * code region. @@ -244,7 +191,14 @@ std::vector HashTable::list_keys() void HashTable::append_to(std::vector>& hashvec) { - compute_self_times(); + // Compute overhead and self times before appending + prepare_computed_times_all(); + + // Remove __profiler__ entries with 0 calls + auto it = table_.find(profiler_hash_); + if (it != NULL && it->second.call_count_ == 0) { table_.erase(it); } + + // Insert local table into hashvec hashvec.insert(hashvec.end(), table_.begin(), table_.end()); } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index c8b299a9..ceb8335c 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -27,8 +27,6 @@ #include #include #include -#include -#include #include "prof_gettime.h" @@ -88,13 +86,11 @@ class HashTable{ // Prototypes size_t query_insert(std::string_view) noexcept; void update(size_t, time_duration_t); - void print(std::ostream&); // Member functions std::vector list_keys(); - void add_child_time (size_t const, time_duration_t); + void add_child_time(size_t const, time_duration_t); void add_overhead_time(size_t const, time_duration_t); - void add_child_time(size_t, time_duration_t); void compute_self_times(); void append_to(std::vector>& hashvec); diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp deleted file mode 100644 index cc46b644..00000000 --- a/src/c++/hashvec.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/*----------------------------------------------------------------------------*\ - (c) Crown copyright 2022 Met Office. All rights reserved. - The file LICENCE, distributed with this code, contains details of the terms - under which the code may be used. -\*----------------------------------------------------------------------------*/ - -#include -#include -#include - -#include "hashvec.h" - -/** - * @brief Getter that returns a reference to the private hashvec. - * - * @param[out] hashvec_ The hashvec itself, a vector of key-value pairs. - * - */ - -std::vector>& HashVec::get() -{ - return hashvec_; -} - -/** - * @brief CFunction that sorts all entries from high to low self walltime. - * - */ - -void HashVec::sort() -{ - std::sort - ( - begin(hashvec_), end(hashvec_), - [] (auto a, auto b) { - return a.second.self_walltime_ > b.second.self_walltime_; - } - ); -} - -/** - * @brief Write all data to file. - * - */ - -void HashVec::write() const -{ - // Preliminary info - std::cout << " " << "No. of instrumented routines called : 9\n"; - std::cout << " " << "Instrumentation started : 20100521 171238\n"; - std::cout << " " << "Instrumentation ended : 20100521 172033\n"; - std::cout << " " << "Instrumentation overhead: 0.90%\n"; - std::cout << " " << "Memory usage : Memory usage : 1346 MBytes (heap), 1315 MBytes (rss), 796 MBytes (stack), 1116 (paging)\n"; - std::cout << " " << "Wall-time is 472.28 sec on proc#1 (192 procs, 1 threads)\n"; - std::cout << " " << "Thread#1: 472.28 sec (100.00%)\n" << std::endl; - - // Table Headers - std::cout - << " " - << std::setw(3) << std::left << "#" - << std::setw(7) << std::left << "% Time" - << std::setw(13) << std::right << "Cumul" - << std::setw(13) << std::right << "Self" - << std::setw(13) << std::right << "Total" - << std::setw(15) << std::right << "# of calls" - << std::setw(12) << std::right << "Self" - << std::setw(12) << std::right << "Total" << " " - << "Routine@" << "\n"; - std::cout << " " - << std::setw(73) << "" - << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; - - // Subheaders - std::cout - << " " - << std::setw(3) << std::left << "" - << std::setw(7) << std::right << "(self)" - << std::setw(13) << std::right << "(sec)" - << std::setw(13) << std::right << "(sec)" - << std::setw(13) << std::right << "(sec)" - << std::setw(15) << std::right << "" - << std::setw(12) << std::right << "ms/call" - << std::setw(12) << std::right << "ms/call" - << "\n\n"; - - // Find the highest walltime in table_, which should be the total runtime of - // the program. This is used later when calculating '% Time'. - double top_walltime = std::max_element - ( - std::begin(hashvec_), std::end(hashvec_), - [] (auto a, auto b) { - return a.second.total_walltime_ < b.second.total_walltime_; - } - )->second.total_walltime_.count(); - - // Declare any variables external to HashEntry - int region_number = 0; - double percent_time; - time_duration_t cumul_walltime = time_duration_t::zero(); - double self_per_call; - double total_per_call; - - // - // Write data to file - // - - std::cout << std::fixed << std::showpoint << std::setprecision(3); - - for (auto& [hash, entry] : hashvec_) { - - // Calculate non-HashEntry data - region_number++; - percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); - cumul_walltime += entry.self_walltime_; - self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); - total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); - - // Write everything out - std::cout - << " " - << std::setw(3) << std::left << region_number - << std::setw(7) << std::right << percent_time - << std::setw(13) << std::right << cumul_walltime.count() - << std::setw(13) << std::right << entry.self_walltime_.count() - << std::setw(13) << std::right << entry.total_walltime_.count() - << std::setw(15) << std::right << entry.call_count_ - << std::setw(12) << std::right << self_per_call - << std::setw(12) << std::right << total_per_call << " " - << entry.region_name_ << "\n"; - - } - -} \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h deleted file mode 100644 index 53906f10..00000000 --- a/src/c++/hashvec.h +++ /dev/null @@ -1,38 +0,0 @@ -/*----------------------------------------------------------------------------*\ - (c) Crown copyright 2022 Met Office. All rights reserved. - The file LICENCE, distributed with this code, contains details of the terms - under which the code may be used. -\*----------------------------------------------------------------------------*/ - -#ifndef HASHVEC_H -#define HASHVEC_H - -#include "hashtable.h" - -/** - * @brief Class containing the "hashvec", a STL vector of key-value pairs. - * - * The hashvec is used to sort from high to low self walltimes, since this - * can't be done on a traditional C++ hashtable. It also serves as a singular - * container for each thread's hashtable to go into. - * - */ - -class HashVec { - - private: - - // The hashvec - a vector of pairs, since std::unordered_maps' cannot - // be sorted via std::sort - std::vector> hashvec_; - - public: - - // Member functions - std::vector>& get(); - void sort(); - void write() const; - -}; - -#endif diff --git a/src/c++/prof_gettime.h b/src/c++/prof_gettime.h index 43eb368c..c0759bf5 100644 --- a/src/c++/prof_gettime.h +++ b/src/c++/prof_gettime.h @@ -5,6 +5,8 @@ * ----------------------------------------------------------------------------- */ +#include + /** * @file prof_gettime.h * @brief Declares time-measurement functionality. diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index b5a41f09..cf50182c 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -5,10 +5,8 @@ \*----------------------------------------------------------------------------*/ #include "profiler.h" -#include "prof_gettime.h" - #include "writer.h" -#include "hashvec.h" +#include "formatter.h" #include #include @@ -171,7 +169,14 @@ void Profiler::stop(size_t const hash) void Profiler::write() { - + const char* user_strat = std::getenv("PROF_IO_MODE"); + + if ( user_strat == NULL ) + { + Writer writer(Writer::MultiFile); + writer.executeStrategy(Format::DrHook,thread_hashtables_); + } + else throw std::runtime_error("Invalid PROF_IO_MODE choice"); } diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index 8ce78027..7426d2cb 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -6,6 +6,7 @@ */ #include "writer.h" +#include "formatter.h" #include #include @@ -14,7 +15,7 @@ * */ -Writer::Writer(std::function)> in) +Writer::Writer(std::function, std::vector)> in) : strategy_(std::move(in)) {} @@ -24,9 +25,9 @@ Writer::Writer(std::function)> in) * */ -void Writer::executeStrategy(std::vector htv) +void Writer::executeStrategy(std::function f_in, std::vector htv) { - strategy_(htv); + strategy_(f_in, htv); } /** @@ -37,7 +38,7 @@ void Writer::executeStrategy(std::vector htv) * */ -void Writer::MultiFile(std::vector htv) +void Writer::MultiFile(std::function f_in, std::vector htv) { // Find current MPI rank int current_rank; @@ -66,16 +67,16 @@ void Writer::MultiFile(std::vector htv) // // Write to file // - HashVec new_hashvec; + Formatter formatter(f_in); - for (auto& it : thread_hashtables_) + for (auto& it : htv) { - it.append_to(new_hashvec.get()); + it.append_to(formatter.get_hashvec()); } - new_hashvec.sort(); - new_hashvec.write(); - + formatter.sort_hashvec(); + formatter.executeStrategy(output_stream, formatter); + output_stream.flush(); output_stream.close(); } \ No newline at end of file diff --git a/src/c++/writer.h b/src/c++/writer.h index a161a5e5..f49d538d 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -20,6 +20,7 @@ #include #include #include "hashtable.h" +#include "formatter.h" /** * @brief Writer class, which sits above hashtable and is constructed @@ -36,18 +37,18 @@ class Writer { private: // Strategy - std::function)> strategy_; + std::function, std::vector)> strategy_; public: // Constructor - explicit Writer(std::function)>); + explicit Writer(std::function, std::vector)>); // Execution method - void executeStrategy(std::vector); + void executeStrategy(std::function, std::vector); // Strategies - static void MultiFile(std::vector); + static void MultiFile(std::function, std::vector); }; diff --git a/tests/unit_tests/c++/test_hashtable.cpp b/tests/unit_tests/c++/test_hashtable.cpp index 64376ec1..86f6556d 100644 --- a/tests/unit_tests/c++/test_hashtable.cpp +++ b/tests/unit_tests/c++/test_hashtable.cpp @@ -38,8 +38,8 @@ TEST(HashTableTest,HashFunctionTest) { // - query_insert'ing Penne or Rigatoni just returns the hash // - the regions have different hashes // - the regions have the hashes returned by hash_function_ which uses std::hash - EXPECT_EQ(prof.start("Rigatoni"), std::hash{}("Rigatoni")); - EXPECT_EQ(prof.start("Penne"), std::hash{}("Penne")); + EXPECT_EQ(prof.start("Rigatoni"), std::hash{}("Rigatoni@0")); + EXPECT_EQ(prof.start("Penne"), std::hash{}("Penne@0")); } } @@ -53,7 +53,7 @@ TEST(HashTableTest,HashFunctionTest) { TEST(HashTableTest,UpdateTimesTest) { // Create new hash - size_t prof_pie = std::hash{}("Pie"); + size_t prof_pie = std::hash{}("Pie@0"); // Trying to find a time before .start() will throw an exception EXPECT_THROW(prof.get_total_walltime(prof_pie, 0), std::out_of_range); diff --git a/tests/unit_tests/c++/test_regionname.cpp b/tests/unit_tests/c++/test_regionname.cpp index a20d2f06..3fd4ea11 100644 --- a/tests/unit_tests/c++/test_regionname.cpp +++ b/tests/unit_tests/c++/test_regionname.cpp @@ -32,7 +32,7 @@ TEST(RegionNameTest,NamesMatchTest) { // Get subregion name out from profiler and check it is what we expect std::string subregionName = prof.get_region_name(prof_latte,0); - EXPECT_EQ("Latte", subregionName); + EXPECT_EQ("Latte@0", subregionName); prof.stop(prof_latte); } @@ -42,7 +42,7 @@ TEST(RegionNameTest,NamesMatchTest) { // Get main region name out from profiler and test std::string regionName = prof.get_region_name(prof_cappucino,0); - EXPECT_EQ("Cappucino", regionName); + EXPECT_EQ("Cappucino@0", regionName); } prof.stop(prof_cappucino); From 588f264ea67c7f0595fbdb121beb32e648d1f8a6 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Mon, 21 Nov 2022 11:29:06 +0000 Subject: [PATCH 071/128] Correct handling of child time for top-level calls. --- src/c++/profiler.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index aca6438a..cca411a1 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -16,9 +16,10 @@ extern time_point_t logged_calliper_start_time; #pragma omp threadprivate(logged_calliper_start_time) time_point_t logged_calliper_start_time; +// The call depth must be initialised on all threads, so do it here. extern int call_depth; #pragma omp threadprivate(call_depth) -int call_depth; +int call_depth = -1; /** * @brief Constructor for StartCalliperValues struct. @@ -62,10 +63,6 @@ Profiler::Profiler() thread_traceback_.push_back(new_list); } - // Initialise the call depth, which is threadprivate. Used as an array index, - // so the first element will be indexed by 0. - call_depth = -1; - // Assertions assert ( static_cast (thread_hashtables_.size()) == max_threads_); assert ( static_cast (thread_traceback_.size() ) == max_threads_); @@ -190,17 +187,14 @@ void Profiler::stop(size_t const hash) // calliper_time = (t4-t1) - (t3-t2) = t4 - ( t3-t2 + t1) auto temp_sum = start_calliper_times.calliper_start_time_ + region_duration; - // Move the end of the list. - --call_depth; - // The sequence of code that follows is aimed at leaving only minimal and // simple operations after the call to prof_gettime(). time_duration_t* parent_overhead_time_ptr = nullptr; // Acquire parent pointers if (call_depth > 0){ - call_depth_it = static_cast(call_depth); - size_t parent_hash = thread_traceback_[tid].at(call_depth_it).first; + auto parent_depth_it = static_cast(call_depth-1); + size_t parent_hash = thread_traceback_[tid].at(parent_depth_it).first; parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( parent_hash, region_duration); } @@ -208,6 +202,9 @@ void Profiler::stop(size_t const hash) // Increment profiler calls, and get a reference to the total overhead time. auto& total_overhead_time = thread_hashtables_[tid].increment_profiler_calls(); + // Decrement index to last entry in the traceback. + --call_depth; + // Account for time spent in the profiler itself. auto calliper_stop_time = prof_gettime(); auto calliper_time = calliper_stop_time - temp_sum; From e7303b7afc62c4dbd135ed7b50388ee477434e69 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 22 Nov 2022 10:16:10 +0000 Subject: [PATCH 072/128] Draft of visitor pattern solution (#72) --- src/c++/CMakeLists.txt | 1 + src/c++/formatter.cpp | 80 ++++++++++++++---------------------------- src/c++/formatter.h | 48 ++++++++++++------------- src/c++/hashvec.cpp | 29 +++++++++++++++ src/c++/hashvec.h | 31 ++++++++++++++++ src/c++/profiler.cpp | 14 ++++---- src/c++/writer.cpp | 60 +++++++++---------------------- src/c++/writer.h | 62 ++++++++++++++++++++++---------- 8 files changed, 175 insertions(+), 150 deletions(-) create mode 100644 src/c++/hashvec.cpp create mode 100644 src/c++/hashvec.h diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index 7100360e..75f2ba7e 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -14,6 +14,7 @@ include(CMakePackageConfigHelpers) add_library(${CMAKE_PROJECT_NAME} SHARED profiler.h profiler.cpp prof_gettime.h prof_gettime.cpp + hashvec.h hashvec.cpp writer.h writer.cpp formatter.h formatter.cpp hashtable.h hashtable.cpp diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 59c99719..85f2d7c0 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -6,48 +6,16 @@ */ #include "formatter.h" -#include +#include "writer.h" #include +#include -/** - * @brief Formatter constructor. - * - */ - -Formatter::Formatter(std::function in) - : strategy_(std::move(in)) - {} - -// - -std::vector>& Formatter::get_hashvec() +void Standard::accept(std::unique_ptr writer, std::vector> hashvec) { - return hashvec_; + writer->VisitStandard(std::make_unique(), hashvec); } -// - -void Formatter::sort_hashvec() -{ - std::sort - ( - begin(hashvec_), end(hashvec_), - [] (auto a, auto b) { - return a.second.self_walltime_ > b.second.self_walltime_; - } - ); -} - -// - -void Formatter::executeStrategy(std::ofstream& output_stream, Formatter& formatter) -{ - strategy_(output_stream, formatter); -} - -// - -void Format::Standard(std::ofstream& output_stream, Formatter& formatter) +void Standard::write(std::ofstream& output_stream, std::vector> hashvec) { std::string routine_at_thread = "Thread: " /*+ std::to_string(tid_)*/; @@ -71,7 +39,7 @@ void Format::Standard(std::ofstream& output_stream, Formatter& formatter) output_stream << std::setfill(' '); // Data entries - for (auto& [hash, entry] : formatter.get_hashvec()) { + for (auto& [hash, entry] : hashvec) { output_stream << std::setw(40) << std::left << entry.region_name_ << " " << std::setw(15) << std::right << entry.self_walltime_.count() << " " @@ -79,13 +47,17 @@ void Format::Standard(std::ofstream& output_stream, Formatter& formatter) << std::setw(15) << std::right << entry.total_walltime_.count() << " " << std::setw(10) << std::right << entry.call_count_ << "\n"; } - + } -// +void DrHook::accept(std::unique_ptr writer, std::vector> hashvec) +{ + writer->VisitDrHook(std::make_unique(), hashvec); +} -void Format::DrHook(std::ofstream& output_stream, Formatter& formatter) +void DrHook::write(std::ofstream& output_stream, std::vector> hashvec) { + // Preliminary info output_stream << " " << "No. of instrumented routines called : 9\n"; output_stream << " " << "Instrumentation started : 20100521 171238\n"; @@ -130,7 +102,7 @@ void Format::DrHook(std::ofstream& output_stream, Formatter& formatter) // the program. This is used later when calculating '% Time'. double top_walltime = std::max_element ( - std::begin(formatter.get_hashvec()), std::end(formatter.get_hashvec()), + std::begin(hashvec), std::end(hashvec), [] (auto a, auto b) { return a.second.total_walltime_ < b.second.total_walltime_; } @@ -149,7 +121,7 @@ void Format::DrHook(std::ofstream& output_stream, Formatter& formatter) output_stream << std::fixed << std::showpoint << std::setprecision(3); - for (auto& [hash, entry] : formatter.get_hashvec()) { + for (auto& [hash, entry] : hashvec) { // Calculate non-HashEntry data region_number++; @@ -160,17 +132,17 @@ void Format::DrHook(std::ofstream& output_stream, Formatter& formatter) // Write everything out output_stream - << " " - << std::setw(3) << std::left << region_number - << std::setw(7) << std::right << percent_time - << std::setw(13) << std::right << cumul_walltime.count() - << std::setw(13) << std::right << entry.self_walltime_.count() - << std::setw(13) << std::right << entry.total_walltime_.count() - << std::setw(15) << std::right << entry.call_count_ - << std::setw(12) << std::right << self_per_call - << std::setw(12) << std::right << total_per_call << " " - << entry.region_name_ << "\n"; + << " " + << std::setw(3) << std::left << region_number + << std::setw(7) << std::right << percent_time + << std::setw(13) << std::right << cumul_walltime.count() + << std::setw(13) << std::right << entry.self_walltime_.count() + << std::setw(13) << std::right << entry.total_walltime_.count() + << std::setw(15) << std::right << entry.call_count_ + << std::setw(12) << std::right << self_per_call + << std::setw(12) << std::right << total_per_call << " " + << entry.region_name_ << "\n"; } - + } \ No newline at end of file diff --git a/src/c++/formatter.h b/src/c++/formatter.h index 4af9b96e..1936ca58 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -16,46 +16,44 @@ #define FORMATTER_H #include -#include +#include +#include #include "hashtable.h" -/** - * @brief - * - * - */ - -class Formatter { - private: +class Writer; // Forward declaration of writer - // Write strategy - std::function strategy_; - // Hashvec - std::vector> hashvec_; +class Format { public: - // Constructor - explicit Formatter(std::function); + // Virtual destructor + virtual ~Format() = default; - // Member functions - std::vector>& get_hashvec(); - void sort_hashvec(); + // Members + virtual void accept(std::unique_ptr writer, std::vector> hashvec) = 0; + virtual void write(std::ofstream& output_stream, std::vector> hashvec) = 0; - // Execution method - void executeStrategy(std::ofstream&, Formatter&); +}; + +class Standard : public Format { + + public: + + // Members + void accept(std::unique_ptr writer, std::vector> hashvec) override; + void write(std::ofstream& output_stream, std::vector> hashvec) override; }; -struct Format { +class DrHook : public Format { - public: + public: - // Strategies - static void Standard(std::ofstream&, Formatter&); - static void DrHook(std::ofstream&, Formatter&); + // Members + void accept(std::unique_ptr writer, std::vector> hashvec) override; + void write(std::ofstream& output_stream, std::vector> hashvec) override; }; diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp new file mode 100644 index 00000000..b9875800 --- /dev/null +++ b/src/c++/hashvec.cpp @@ -0,0 +1,29 @@ + +#include "hashvec.h" + +HashVec::HashVec() : iomode_(std::getenv("PROF_IO_MODE")), format_(std::getenv("PROF_OUT_FORMAT")) {} + +std::vector>& HashVec::get() +{ + return hashvec_; +} + +void HashVec::sort() +{ + std::sort + ( + begin(hashvec_), end(hashvec_), + [] (auto a, auto b) { + return a.second.self_walltime_ > b.second.self_walltime_; + } + ); +} + +void HashVec::write() +{ + if ( format_ == NULL && iomode_ == NULL ) + { + std::make_unique()->accept(std::make_unique(), get()); + } + else throw std::runtime_error("Invalid PROF_IO_MODE choice"); +} \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h new file mode 100644 index 00000000..833aa155 --- /dev/null +++ b/src/c++/hashvec.h @@ -0,0 +1,31 @@ +#include "formatter.h" +#include "writer.h" +#include + +#ifndef HASHVEC_H +#define HASHVEC_H + +class HashVec { + + private: + + // The hashvec + std::vector> hashvec_; + + // Environment variables + const char* format_; + const char* iomode_; + + public: + + // Constructor + HashVec(); + + // Member functions + std::vector>& get(); + void sort(); + void write(); + +}; + +#endif \ No newline at end of file diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index cf50182c..fc0d2fdd 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -5,8 +5,7 @@ \*----------------------------------------------------------------------------*/ #include "profiler.h" -#include "writer.h" -#include "formatter.h" +#include "hashvec.h" #include #include @@ -168,16 +167,15 @@ void Profiler::stop(size_t const hash) void Profiler::write() { + HashVec new_hashvec; - const char* user_strat = std::getenv("PROF_IO_MODE"); - - if ( user_strat == NULL ) + for (auto& it : thread_hashtables_) { - Writer writer(Writer::MultiFile); - writer.executeStrategy(Format::DrHook,thread_hashtables_); + it.append_to(new_hashvec.get()); } - else throw std::runtime_error("Invalid PROF_IO_MODE choice"); + new_hashvec.sort(); + new_hashvec.write(); } diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index 7426d2cb..f4ec158d 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -8,37 +8,8 @@ #include "writer.h" #include "formatter.h" #include -#include -/** - * @brief Writer constructor. - * - */ - -Writer::Writer(std::function, std::vector)> in) - : strategy_(std::move(in)) - {} - -/** - * @brief Method that executes whatever function is passed to strategy_ via the - * constructor. - * - */ - -void Writer::executeStrategy(std::function f_in, std::vector htv) -{ - strategy_(f_in, htv); -} - -/** - * @brief The multiple-output-files strategy. - * - * Writes out data from the vector of HashTables given to it into one file per - * mpi rank. - * - */ - -void Writer::MultiFile(std::function f_in, std::vector htv) +void Multifile::write() { // Find current MPI rank int current_rank; @@ -61,22 +32,23 @@ void Writer::MultiFile(std::function f_in, std { out_filename = "profiler-output" + mpi_filename_tail; } - std::ofstream output_stream; + output_stream.open(out_filename); +} - // - // Write to file - // - Formatter formatter(f_in); - - for (auto& it : htv) - { - it.append_to(formatter.get_hashvec()); - } +void Multifile::VisitStandard(const std::unique_ptr standard, std::vector> hashvec) +{ + write(); + standard->write(output_stream, hashvec); + output_stream.flush(); + output_stream.close(); +} - formatter.sort_hashvec(); - formatter.executeStrategy(output_stream, formatter); - +void Multifile::VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) +{ + write(); + drhook->write(output_stream, hashvec); output_stream.flush(); output_stream.close(); -} \ No newline at end of file +} + diff --git a/src/c++/writer.h b/src/c++/writer.h index f49d538d..d4ee318b 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -18,38 +18,62 @@ #define WRITER_H #include -#include +#include +#include #include "hashtable.h" -#include "formatter.h" -/** - * @brief Writer class, which sits above hashtable and is constructed - * within the profiler. - * - * Any callable function can be passed into the writer class constructor and - * subsequently executed using the execute method. Different strategies can be - * included as static member functions, such as MultiFile. - * - */ + +class Standard; // Forward declaration of formats +class DrHook; + class Writer { + protected: + + // + std::ofstream output_stream; + + public: + + // Virtual destructor + virtual ~Writer() = default; + + // Members + virtual void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) = 0; + virtual void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) = 0; + +}; + +class Singlefile : public Writer { + private: - // Strategy - std::function, std::vector)> strategy_; + // + void write(); public: - // Constructor - explicit Writer(std::function, std::vector)>); + // + void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) override; + void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) override; - // Execution method - void executeStrategy(std::function, std::vector); +}; + +class Multifile : public Writer { - // Strategies - static void MultiFile(std::function, std::vector); + private: + + // + void write(); + + public: + + // + void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) override; + void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) override; }; + #endif From e256b4e1025b4420923081d565a2838aab6e6f57 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 22 Nov 2022 11:59:11 +0000 Subject: [PATCH 073/128] Better comment blocks in visitor pattern solution (#72) --- src/c++/formatter.cpp | 20 ++++++++++++++++++++ src/c++/formatter.h | 27 ++++++++++++++++++++++++--- src/c++/hashvec.cpp | 29 +++++++++++++++++++++++++++++ src/c++/hashvec.h | 28 ++++++++++++++++++++++++++-- src/c++/writer.cpp | 39 ++++++++++++++++++++++++++++++++------- src/c++/writer.h | 43 +++++++++++++++++++------------------------ 6 files changed, 150 insertions(+), 36 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 85f2d7c0..cb3529fa 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -10,11 +10,21 @@ #include #include +/** + * @brief Accepts any unique Writer pointer and tells it to visit this format + * + */ + void Standard::accept(std::unique_ptr writer, std::vector> hashvec) { writer->VisitStandard(std::make_unique(), hashvec); } +/** + * @brief Write method that incorporates the "standard"/default format option + * + */ + void Standard::write(std::ofstream& output_stream, std::vector> hashvec) { @@ -50,11 +60,21 @@ void Standard::write(std::ofstream& output_stream, std::vector writer, std::vector> hashvec) { writer->VisitDrHook(std::make_unique(), hashvec); } +/** + * @brief Write method that incorporates the drhook format option + * + */ + void DrHook::write(std::ofstream& output_stream, std::vector> hashvec) { diff --git a/src/c++/formatter.h b/src/c++/formatter.h index 1936ca58..cb7b5dc8 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -7,8 +7,8 @@ /** * @file formatter.h - * @brief - + * @brief Contains base format class and all derived classes. + * * */ @@ -21,9 +21,16 @@ #include "hashtable.h" -class Writer; // Forward declaration of writer +// Forward declaration of writer +class Writer; +/** + * @brief Base format class + * + * Contains virtual write method and "accept" method for accepting visitors. + */ + class Format { public: @@ -37,6 +44,13 @@ class Format { }; +/** + * @brief Standard format + * + * Overloads the virtual write method in order to write an input hashvec out in + * the profiler's standard format + */ + class Standard : public Format { public: @@ -47,6 +61,13 @@ class Standard : public Format { }; +/** + * @brief DrHook format + * + * Overloads the virtual write method in order to write an input hashvec out in + * a drhook-style format + */ + class DrHook : public Format { public: diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index b9875800..f8175448 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -1,13 +1,34 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ #include "hashvec.h" +/** + * @brief HashVec constructor + * + */ + HashVec::HashVec() : iomode_(std::getenv("PROF_IO_MODE")), format_(std::getenv("PROF_OUT_FORMAT")) {} +/** + * @brief hashvec_ getter + * + */ + std::vector>& HashVec::get() { return hashvec_; } +/** + * @brief Sorts entries in the hashvec from high to low self walltime + * + */ + void HashVec::sort() { std::sort @@ -19,6 +40,14 @@ void HashVec::sort() ); } +/** + * @brief Makes the appropriate visitor pattern calls depending on what + * format_ and iomode_ are + * + * The HashVec's hashvec_ is passed down to whatever format is chosen via get() + * + */ + void HashVec::write() { if ( format_ == NULL && iomode_ == NULL ) diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 833aa155..e2eaa73e 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -1,9 +1,33 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +/** + * @file hashvec.h + * @brief Contains the hashvec and decides on format/IO mode. + * + * The hashvec is a vector of pairs that acts as an alternative hashtable for + * sorting via self-walltime. The HashVec constructor grabs the appropriate + * format and IO strategy environment variables then uses the writer/formatter + * visitor pattern. + * + */ + +#ifndef HASHVEC_H +#define HASHVEC_H + #include "formatter.h" #include "writer.h" #include -#ifndef HASHVEC_H -#define HASHVEC_H +/** + * @brief Base writer class + * + * Essentially a visitor class that all IO modes will derive from. + */ class HashVec { diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index f4ec158d..df36bf29 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -9,7 +9,12 @@ #include "formatter.h" #include -void Multifile::write() +/** + * @brief Function that opens a file corresponding to the current mpirank + * + */ + +void Multifile::openfile() { // Find current MPI rank int current_rank; @@ -36,19 +41,39 @@ void Multifile::write() output_stream.open(out_filename); } -void Multifile::VisitStandard(const std::unique_ptr standard, std::vector> hashvec) +/** + * @brief Performs a flush-and-close of the output stream. + * + */ + +void Multifile::closefile() { - write(); - standard->write(output_stream, hashvec); output_stream.flush(); output_stream.close(); } +/** + * @brief Bundles private methods together with a call to the standard format + * + */ + +void Multifile::VisitStandard(const std::unique_ptr standard, std::vector> hashvec) +{ + openfile(); + standard->write(output_stream, hashvec); + closefile(); +} + +/** + * @brief Bundles private methods together with a call to the drhook format + * + */ + + void Multifile::VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) { - write(); + openfile(); drhook->write(output_stream, hashvec); - output_stream.flush(); - output_stream.close(); + closefile(); } diff --git a/src/c++/writer.h b/src/c++/writer.h index d4ee318b..394c7cdd 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -23,53 +23,48 @@ #include "hashtable.h" -class Standard; // Forward declaration of formats +// Forward declaration of formats +class Standard; class DrHook; -class Writer { - - protected: +/** + * @brief Base writer class + * + * Essentially a visitor class that all IO modes will derive from. + */ - // - std::ofstream output_stream; +class Writer { public: // Virtual destructor virtual ~Writer() = default; - // Members + // Members - any writer can "visit" any format virtual void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) = 0; virtual void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) = 0; }; -class Singlefile : public Writer { - - private: - - // - void write(); - - public: - - // - void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) override; - void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) override; - -}; +/** + * @brief Class for the multiple-file output option + */ class Multifile : public Writer { private: - // - void write(); + // Output stream + std::ofstream output_stream; + + // Private init/finalise methods + void openfile(); + void closefile(); public: - // + // Override the visit methods void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) override; void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) override; From a4c89142367321509ebf8ce1f6bf1cc8ec7e6e3f Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 22 Nov 2022 14:01:50 +0000 Subject: [PATCH 074/128] Slight change in how the visitor method is called (#72) --- src/c++/hashvec.cpp | 6 +++++- src/c++/hashvec.h | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index f8175448..2b227073 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -52,7 +52,11 @@ void HashVec::write() { if ( format_ == NULL && iomode_ == NULL ) { - std::make_unique()->accept(std::make_unique(), get()); + // Create unique ptr + std::unique_ptr standard_component = std::make_unique(); + + // The standard format (component) accepts the multiple-file method (visitor) + standard_component->accept(std::make_unique(), this->get()); } else throw std::runtime_error("Invalid PROF_IO_MODE choice"); } \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index e2eaa73e..2f3f5e48 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -37,9 +37,9 @@ class HashVec { std::vector> hashvec_; // Environment variables - const char* format_; const char* iomode_; - + const char* format_; + public: // Constructor From 2d87e708c4cc24eac97572abcc79b2e1bfc9d86d Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Thu, 24 Nov 2022 17:35:16 +0000 Subject: [PATCH 075/128] Combination of std::function and virtual strategy methods (#72) --- src/c++/formatter.cpp | 74 ++++++++++++---------------------- src/c++/formatter.h | 59 ++++++--------------------- src/c++/hashvec.cpp | 42 +++++++++++++++----- src/c++/hashvec.h | 5 ++- src/c++/writer.cpp | 92 ++++++++++++++++--------------------------- src/c++/writer.h | 46 +++++++++------------- 6 files changed, 129 insertions(+), 189 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index cb3529fa..705a7dcb 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -6,51 +6,45 @@ */ #include "formatter.h" -#include "writer.h" #include #include -/** - * @brief Accepts any unique Writer pointer and tells it to visit this format - * - */ +Formatter::Formatter(std::function>)> format) + : format_(std::move(format)) + {} -void Standard::accept(std::unique_ptr writer, std::vector> hashvec) +void Formatter::executeFormat(std::ofstream& os, std::vector> hashvec) { - writer->VisitStandard(std::make_unique(), hashvec); + format_(os, hashvec); } -/** - * @brief Write method that incorporates the "standard"/default format option - * - */ -void Standard::write(std::ofstream& output_stream, std::vector> hashvec) +void Formats::standard(std::ofstream& os, std::vector> hashvec) { std::string routine_at_thread = "Thread: " /*+ std::to_string(tid_)*/; // Write headings - output_stream << "\n"; - output_stream + os << "\n"; + os << std::setw(40) << std::left << routine_at_thread << " " << std::setw(15) << std::right << "Self (s)" << " " << std::setw(15) << std::right << "Total (raw) (s)" << " " << std::setw(15) << std::right << "Total (s)" << " " << std::setw(10) << std::right << "Calls" << "\n"; - output_stream << std::setfill('-'); - output_stream + os << std::setfill('-'); + os << std::setw(40) << "-" << " " << std::setw(15) << "-" << " " << std::setw(15) << "-" << " " << std::setw(15) << "-" << " " << std::setw(10) << "-" << "\n"; - output_stream << std::setfill(' '); + os << std::setfill(' '); // Data entries for (auto& [hash, entry] : hashvec) { - output_stream + os << std::setw(40) << std::left << entry.region_name_ << " " << std::setw(15) << std::right << entry.self_walltime_.count() << " " << std::setw(15) << std::right << entry.total_raw_walltime_.count() << " " @@ -60,36 +54,20 @@ void Standard::write(std::ofstream& output_stream, std::vector writer, std::vector> hashvec) -{ - writer->VisitDrHook(std::make_unique(), hashvec); -} - -/** - * @brief Write method that incorporates the drhook format option - * - */ - -void DrHook::write(std::ofstream& output_stream, std::vector> hashvec) +void Formats::drhook(std::ofstream& os, std::vector> hashvec) { - // Preliminary info - output_stream << " " << "No. of instrumented routines called : 9\n"; - output_stream << " " << "Instrumentation started : 20100521 171238\n"; - output_stream << " " << "Instrumentation ended : 20100521 172033\n"; - output_stream << " " << "Instrumentation overhead: 0.90%\n"; - output_stream << " " << "Memory usage : Memory usage : 1346 MBytes (heap), 1315 MBytes (rss), 796 MBytes (stack), 1116 (paging)\n"; - output_stream << " " << "Wall-time is 472.28 sec on proc#1 (192 procs, 1 threads)\n"; - output_stream << " " << "Thread#1: 472.28 sec (100.00%)" << std::endl; + os << " " << "No. of instrumented routines called : 9\n"; + os << " " << "Instrumentation started : 20100521 171238\n"; + os << " " << "Instrumentation ended : 20100521 172033\n"; + os << " " << "Instrumentation overhead: 0.90%\n"; + os << " " << "Memory usage : Memory usage : 1346 MBytes (heap), 1315 MBytes (rss), 796 MBytes (stack), 1116 (paging)\n"; + os << " " << "Wall-time is 472.28 sec on proc#1 (192 procs, 1 threads)\n"; + os << " " << "Thread#1: 472.28 sec (100.00%)" << std::endl; // Table Headers - output_stream << "\n"; - output_stream + os << "\n"; + os << " " << std::setw(3) << std::left << "#" << std::setw(7) << std::left << "% Time" @@ -100,13 +78,13 @@ void DrHook::write(std::ofstream& output_stream, std::vector(entry.call_count_) ); // Write everything out - output_stream + os << " " << std::setw(3) << std::left << region_number << std::setw(7) << std::right << percent_time diff --git a/src/c++/formatter.h b/src/c++/formatter.h index cb7b5dc8..62fa2db2 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -16,66 +16,33 @@ #define FORMATTER_H #include -#include +#include #include #include "hashtable.h" +class Formatter { -// Forward declaration of writer -class Writer; + private: - -/** - * @brief Base format class - * - * Contains virtual write method and "accept" method for accepting visitors. - */ - -class Format { + std::function>)> format_; public: - // Virtual destructor - virtual ~Format() = default; - - // Members - virtual void accept(std::unique_ptr writer, std::vector> hashvec) = 0; - virtual void write(std::ofstream& output_stream, std::vector> hashvec) = 0; + // Constructor + explicit Formatter(std::function>)> format); + void executeFormat(std::ofstream& os, std::vector> hashvec); + }; -/** - * @brief Standard format - * - * Overloads the virtual write method in order to write an input hashvec out in - * the profiler's standard format - */ - -class Standard : public Format { +struct Formats { public: - // Members - void accept(std::unique_ptr writer, std::vector> hashvec) override; - void write(std::ofstream& output_stream, std::vector> hashvec) override; - -}; - -/** - * @brief DrHook format - * - * Overloads the virtual write method in order to write an input hashvec out in - * a drhook-style format - */ - -class DrHook : public Format { - - public: - - // Members - void accept(std::unique_ptr writer, std::vector> hashvec) override; - void write(std::ofstream& output_stream, std::vector> hashvec) override; - + // Format options + static void standard(std::ofstream& os, std::vector> hashvec); + static void drhook(std::ofstream& os, std::vector> hashvec); + }; #endif \ No newline at end of file diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 2b227073..07b6b17e 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -12,7 +12,36 @@ * */ -HashVec::HashVec() : iomode_(std::getenv("PROF_IO_MODE")), format_(std::getenv("PROF_OUT_FORMAT")) {} +HashVec::HashVec() + : format_(std::getenv("PROF_OUT_FORMAT")) + , iomode_(std::getenv("PROF_IO_MODE")) + {} + +std::unique_ptr HashVec::createWriter() +{ + std::string format = static_cast(format_); + std::string iomode = static_cast(iomode_); + + // Creates format ptr first + std::unique_ptr formatter; + if (format.empty() || format == "standard") + { + formatter = std::make_unique(Formats::standard); + } + else if (format == "drhook") + { + formatter = std::make_unique(Formats::drhook); + } + else throw std::runtime_error("Invalid format choice"); + + + // Uses format in creation of writer + if (iomode.empty() || iomode == "multi") + { + return std::make_unique(std::move(formatter)); + } + else throw std::runtime_error("Invalid io mode choice"); +} /** * @brief hashvec_ getter @@ -50,13 +79,8 @@ void HashVec::sort() void HashVec::write() { - if ( format_ == NULL && iomode_ == NULL ) - { - // Create unique ptr - std::unique_ptr standard_component = std::make_unique(); + std::ofstream os; - // The standard format (component) accepts the multiple-file method (visitor) - standard_component->accept(std::make_unique(), this->get()); - } - else throw std::runtime_error("Invalid PROF_IO_MODE choice"); + auto writer = this->createWriter(); + writer->write(os, hashvec_); } \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 2f3f5e48..2b795269 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -37,8 +37,11 @@ class HashVec { std::vector> hashvec_; // Environment variables - const char* iomode_; const char* format_; + const char* iomode_; + + // Private creation function + std::unique_ptr createWriter(); public: diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index df36bf29..018d0e7f 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -6,74 +6,52 @@ */ #include "writer.h" -#include "formatter.h" #include -/** - * @brief Function that opens a file corresponding to the current mpirank - * - */ +Writer::Writer(std::unique_ptr formatter) + : formatter_(std::move(formatter)) + {} -void Multifile::openfile() +std::unique_ptr& Writer::get_formatter() { - // Find current MPI rank - int current_rank; - MPI_Comm prof_comm_ = MPI_COMM_WORLD; - MPI_Comm_rank(prof_comm_, ¤t_rank); - - // For later appending onto the end of the output file name - std::string mpi_filename_tail = "-" + std::to_string(current_rank); - - // Pickup environment variable filename if it exists, if not use the - // default name of "profiler-output". In either case, include the MPI rank - // in the name of the file. - std::string out_filename; - const char* env_variable = std::getenv("PROF_OUTFILE"); - if (env_variable != NULL) - { - out_filename = static_cast(env_variable) + mpi_filename_tail; - } - else - { - out_filename = "profiler-output" + mpi_filename_tail; - } - - output_stream.open(out_filename); + return formatter_; } -/** - * @brief Performs a flush-and-close of the output stream. - * - */ - -void Multifile::closefile() +void Multi::prep(std::ofstream& os) { - output_stream.flush(); - output_stream.close(); + // Find current MPI rank + int current_rank; + MPI_Comm prof_comm_ = MPI_COMM_WORLD; + MPI_Comm_rank(prof_comm_, ¤t_rank); + + // For later appending onto the end of the output file name + std::string mpi_filename_tail = "-" + std::to_string(current_rank); + + // Pickup environment variable filename if it exists, if not use the + // default name of "profiler-output". In either case, include the MPI rank + // in the name of the file. + std::string out_filename; + const char* env_variable = std::getenv("PROF_OUTFILE"); + if (env_variable != NULL) + { + out_filename = static_cast(env_variable) + mpi_filename_tail; + } + else + { + out_filename = "profiler-output" + mpi_filename_tail; + } + + os.open(out_filename); } -/** - * @brief Bundles private methods together with a call to the standard format - * - */ +Multi::Multi(std::unique_ptr formatter) + : Writer(std::move(formatter)) + {} -void Multifile::VisitStandard(const std::unique_ptr standard, std::vector> hashvec) +void Multi::write(std::ofstream& os, std::vector> hashvec) { - openfile(); - standard->write(output_stream, hashvec); - closefile(); + this->prep(os); + this->get_formatter()->executeFormat(os, hashvec); } -/** - * @brief Bundles private methods together with a call to the drhook format - * - */ - - -void Multifile::VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) -{ - openfile(); - drhook->write(output_stream, hashvec); - closefile(); -} diff --git a/src/c++/writer.h b/src/c++/writer.h index 394c7cdd..b074451c 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -21,54 +21,44 @@ #include #include #include "hashtable.h" +#include "formatter.h" +class Writer { -// Forward declaration of formats -class Standard; -class DrHook; + private: + std::unique_ptr formatter_; -/** - * @brief Base writer class - * - * Essentially a visitor class that all IO modes will derive from. - */ + protected: -class Writer { + // Constructor + explicit Writer(std::unique_ptr formatter); + + std::unique_ptr& get_formatter(); public: - // Virtual destructor virtual ~Writer() = default; + Writer() = delete; - // Members - any writer can "visit" any format - virtual void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) = 0; - virtual void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) = 0; + virtual void write(std::ofstream& os, std::vector> hashvec) = 0; }; -/** - * @brief Class for the multiple-file output option - */ - -class Multifile : public Writer { +class Multi : public Writer { private: - // Output stream - std::ofstream output_stream; - - // Private init/finalise methods - void openfile(); - void closefile(); + void prep(std::ofstream& os); public: - // Override the visit methods - void VisitStandard(const std::unique_ptr standard, std::vector> hashvec) override; - void VisitDrHook(const std::unique_ptr drhook, std::vector> hashvec) override; + ~Multi() override = default; -}; + explicit Multi(std::unique_ptr formatter); + + void write(std::ofstream& os, std::vector> hashvec) override; +}; #endif From 472d0453492e61a931ec955b6020c240e3b2631e Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 25 Nov 2022 11:09:11 +0000 Subject: [PATCH 076/128] Updating comment blocks (#72) --- src/c++/formatter.cpp | 74 +++++++++++++++++++++++++++++-------------- src/c++/formatter.h | 12 +++++++ src/c++/hashvec.cpp | 13 +++++++- src/c++/hashvec.h | 11 ++++--- src/c++/writer.cpp | 38 +++++++++++++++++++++- src/c++/writer.h | 18 +++++++++-- 6 files changed, 135 insertions(+), 31 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 705a7dcb..0d3947ed 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -9,15 +9,35 @@ #include #include +/** + * @brief Formatter constructor + * + * @param[in] format An input callable function that will replace format_ + * + */ + Formatter::Formatter(std::function>)> format) : format_(std::move(format)) {} +/** + * @brief Executes format_ + * + * @param[in] os Output stream that the format method will write to + * @param[in] hashvec Vector of pairs that the format method will operate on + */ + void Formatter::executeFormat(std::ofstream& os, std::vector> hashvec) { format_(os, hashvec); } +/** + * @brief Our standard output format + * + * @param[in] os Output stream to write to + * @param[in] hashvec Vector containing all the necessary data + */ void Formats::standard(std::ofstream& os, std::vector> hashvec) { @@ -54,6 +74,14 @@ void Formats::standard(std::ofstream& os, std::vector> hashvec) { // Preliminary info @@ -68,33 +96,33 @@ void Formats::drhook(std::ofstream& os, std::vector // Table Headers os << "\n"; os - << " " - << std::setw(3) << std::left << "#" - << std::setw(7) << std::left << "% Time" - << std::setw(13) << std::right << "Cumul" - << std::setw(13) << std::right << "Self" - << std::setw(13) << std::right << "Total" - << std::setw(15) << std::right << "# of calls" - << std::setw(12) << std::right << "Self" - << std::setw(12) << std::right << "Total" << " " - << "Routine@" << "\n"; + << " " + << std::setw(3) << std::left << "#" + << std::setw(7) << std::left << "% Time" + << std::setw(13) << std::right << "Cumul" + << std::setw(13) << std::right << "Self" + << std::setw(13) << std::right << "Total" + << std::setw(15) << std::right << "# of calls" + << std::setw(12) << std::right << "Self" + << std::setw(12) << std::right << "Total" << " " + << "Routine@" << "\n"; os - << " " - << std::setw(73) << "" - << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; + << " " + << std::setw(73) << "" + << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; // Subheaders os - << " " - << std::setw(3) << std::left << "" - << std::setw(7) << std::right << "(self)" - << std::setw(13) << std::right << "(sec)" - << std::setw(13) << std::right << "(sec)" - << std::setw(13) << std::right << "(sec)" - << std::setw(15) << std::right << "" - << std::setw(12) << std::right << "ms/call" - << std::setw(12) << std::right << "ms/call" - << "\n\n"; + << " " + << std::setw(3) << std::left << "" + << std::setw(7) << std::right << "(self)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(15) << std::right << "" + << std::setw(12) << std::right << "ms/call" + << std::setw(12) << std::right << "ms/call" + << "\n\n"; // Find the highest walltime in table_, which should be the total runtime of // the program. This is used later when calculating '% Time'. diff --git a/src/c++/formatter.h b/src/c++/formatter.h index 62fa2db2..af4afe2d 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -20,10 +20,18 @@ #include #include "hashtable.h" +/** + * @brief Formatting class + * + * Any callable function can be passed to this class and ran via the execute + * method. + */ + class Formatter { private: + // Format method std::function>)> format_; public: @@ -35,6 +43,10 @@ class Formatter { }; +/** + * @brief Struct to store the formats of interest. + */ + struct Formats { public: diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 07b6b17e..b3faa8b2 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -9,6 +9,9 @@ /** * @brief HashVec constructor + * + * The format_ and iomode_ variables are set via std::getenv, which checks for + * the given environment variable. * */ @@ -17,6 +20,14 @@ HashVec::HashVec() , iomode_(std::getenv("PROF_IO_MODE")) {} +/** + * @brief Creates a new unique Writer pointer for a particular format and + * "IO mode" - i.e. singular file output or multiple file output + * + * @return std::unique_ptr Unique Writer pointer that hashvec_ is + * passed to in order to write out data + */ + std::unique_ptr HashVec::createWriter() { std::string format = static_cast(format_); @@ -81,6 +92,6 @@ void HashVec::write() { std::ofstream os; - auto writer = this->createWriter(); + auto writer = createWriter(); writer->write(os, hashvec_); } \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 2b795269..2fd30330 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -24,9 +24,12 @@ #include /** - * @brief Base writer class - * - * Essentially a visitor class that all IO modes will derive from. + * @brief HashVec class + * + * The hashvec is a vector of pairs containing information from one or more + * hashtables, since data in unordered_map's cannot be sorted. This class wraps + * the hashvec container with additional functionality that lets it pick a + * format/iomode and create a pointer to the writer class. */ class HashVec { @@ -40,7 +43,7 @@ class HashVec { const char* format_; const char* iomode_; - // Private creation function + // Writer creation function std::unique_ptr createWriter(); public: diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index 018d0e7f..646c7344 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -8,15 +8,34 @@ #include "writer.h" #include +/** + * @brief Writer constructor + * + * @param[in] formatter A pointer to the formatter class that will replace + * Writer::formatter_ + */ + Writer::Writer(std::unique_ptr formatter) : formatter_(std::move(formatter)) {} +/** + * @brief Tool via which any derived classes can access formatter_ + * + * @returns std::unique_ptr& Returns the private formatter pointer + */ + std::unique_ptr& Writer::get_formatter() { return formatter_; } +/** + * @brief Opens a unique file per mpi rank + * + * @param[in] os Output stream to write to + */ + void Multi::prep(std::ofstream& os) { // Find current MPI rank @@ -44,14 +63,31 @@ void Multi::prep(std::ofstream& os) os.open(out_filename); } +/** + * @brief Multiple-file-output "Multi" class constructor + * + * @param[in] formatter An input formatter pointer that will be used to call + * Writer's constructor + */ + Multi::Multi(std::unique_ptr formatter) : Writer(std::move(formatter)) {} +/** + * @brief The main write method, combines prep() with formatter_'s format, + * before then flushing and closing the output stream. + * + * @param[in] os The output stream to write to + * @param[in] hashvec The vector containing all necessary data + */ + void Multi::write(std::ofstream& os, std::vector> hashvec) { - this->prep(os); + prep(os); this->get_formatter()->executeFormat(os, hashvec); + os.flush(); + os.close(); } diff --git a/src/c++/writer.h b/src/c++/writer.h index b074451c..aab77c26 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -23,15 +23,24 @@ #include "hashtable.h" #include "formatter.h" +/** + * @brief Writer class + * + * Abstract class that stores a pointer to the formatter. Both the formatter + * and the virtual write method can be changed by any derived classes. + */ + class Writer { private: + // Ptr to formatting class std::unique_ptr formatter_; protected: - // Constructor + // The constructor, this is the only way in which the formatter pointer + // above can be changed. It will be inherited by any derived classes. explicit Writer(std::unique_ptr formatter); std::unique_ptr& get_formatter(); @@ -41,20 +50,25 @@ class Writer { virtual ~Writer() = default; Writer() = delete; + // Virtual write method virtual void write(std::ofstream& os, std::vector> hashvec) = 0; }; +/** + * @brief Class for multiple-file output + */ + class Multi : public Writer { private: + // Method void prep(std::ofstream& os); public: ~Multi() override = default; - explicit Multi(std::unique_ptr formatter); void write(std::ofstream& os, std::vector> hashvec) override; From 20c258b94a90c977ecfda4a63465d8d866017f2f Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 25 Nov 2022 15:19:01 +0000 Subject: [PATCH 077/128] Tidying up and updating call count unit test (#72) --- src/c++/formatter.cpp | 2 +- src/c++/formatter.h | 2 ++ src/c++/hashvec.h | 1 + src/c++/writer.h | 3 ++- tests/unit_tests/c++/test_callcount.cpp | 17 +++++++++-------- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 0d3947ed..996e025d 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -42,7 +42,7 @@ void Formatter::executeFormat(std::ofstream& os, std::vector> hashvec) { - std::string routine_at_thread = "Thread: " /*+ std::to_string(tid_)*/; + std::string routine_at_thread = "Thread: All" /*+ std::to_string(tid_)*/; // Write headings os << "\n"; diff --git a/src/c++/formatter.h b/src/c++/formatter.h index af4afe2d..7716fd9b 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -18,8 +18,10 @@ #include #include #include + #include "hashtable.h" + /** * @brief Formatting class * diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 2fd30330..c5d8c536 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -21,6 +21,7 @@ #include "formatter.h" #include "writer.h" +#include "hashtable.h" #include /** diff --git a/src/c++/writer.h b/src/c++/writer.h index aab77c26..92a07234 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -20,9 +20,10 @@ #include #include #include -#include "hashtable.h" #include "formatter.h" +struct HashEntry; + /** * @brief Writer class * diff --git a/tests/unit_tests/c++/test_callcount.cpp b/tests/unit_tests/c++/test_callcount.cpp index f041db73..de6b7d0f 100644 --- a/tests/unit_tests/c++/test_callcount.cpp +++ b/tests/unit_tests/c++/test_callcount.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "profiler.h" @@ -17,11 +18,12 @@ TEST(HashEntryTest,CallCountTest) // Declare a shared sub-region hash. Initialise num_threads so that the // compiler knows the 'for' loop inside the parallel region will definitely // happen, and therefore doesn't think prof_sub_private remains unitialised. - size_t prof_sub_shared; + //size_t prof_sub_shared; + std::vector> prof_sub_shared; int num_threads = 1; // Start parallel region -#pragma omp parallel default(none) shared(prof_sub_shared, prof, num_threads) +#pragma omp parallel default(none) shared(prof_sub_shared, prof, num_threads, std::cout) { // Get total number of threads, only need to calculate on a single thread // since value won't change. @@ -48,10 +50,7 @@ TEST(HashEntryTest,CallCountTest) } // Give prof_sub_shared a value for later use in EXPECT's -#pragma omp single - { - prof_sub_shared = prof_sub_private; - } + prof_sub_shared.push_back( std::make_pair(thread_id, prof_sub_private) ); } // Stop main region @@ -62,8 +61,10 @@ TEST(HashEntryTest,CallCountTest) // zero which includes the main region callipers. for (int j = 0; j < num_threads; ++j) { - EXPECT_EQ(prof.get_call_count(prof_sub_shared,j),num_threads-j); - + size_t J = static_cast(j); + int i = prof_sub_shared[J].first; + EXPECT_EQ(prof.get_call_count(prof_sub_shared[J].second,i),num_threads-i); + int incr = (j==0) ? 1 : 0; EXPECT_EQ(prof.get_prof_call_count(j),num_threads-j+incr); } From c3f143f0722214be680089503bce7af280e19001 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Fri, 25 Nov 2022 15:53:28 +0000 Subject: [PATCH 078/128] Adding some const's where appropriate and slight comment changes (#72) --- src/c++/formatter.cpp | 2 +- src/c++/formatter.h | 4 ++-- src/c++/hashvec.cpp | 2 +- src/c++/hashvec.h | 9 +++------ src/c++/writer.cpp | 6 +++--- src/c++/writer.h | 4 ++-- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 996e025d..b6307261 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -27,7 +27,7 @@ Formatter::Formatter(std::function> hashvec) +void Formatter::executeFormat(std::ofstream& os, std::vector> hashvec) const { format_(os, hashvec); } diff --git a/src/c++/formatter.h b/src/c++/formatter.h index 7716fd9b..292d54de 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -34,14 +34,14 @@ class Formatter { private: // Format method - std::function>)> format_; + const std::function>)> format_; public: // Constructor explicit Formatter(std::function>)> format); - void executeFormat(std::ofstream& os, std::vector> hashvec); + void executeFormat(std::ofstream& os, std::vector> hashvec) const; }; diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index b3faa8b2..753aeac7 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -28,7 +28,7 @@ HashVec::HashVec() * passed to in order to write out data */ -std::unique_ptr HashVec::createWriter() +const std::unique_ptr HashVec::createWriter() const { std::string format = static_cast(format_); std::string iomode = static_cast(iomode_); diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index c5d8c536..2fa5c717 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -9,11 +9,8 @@ * @file hashvec.h * @brief Contains the hashvec and decides on format/IO mode. * - * The hashvec is a vector of pairs that acts as an alternative hashtable for - * sorting via self-walltime. The HashVec constructor grabs the appropriate - * format and IO strategy environment variables then uses the writer/formatter - * visitor pattern. - * + * Contains hashvec get and sort methods, aswell as a write method that + * utilises the Formatter and Writer classes. */ #ifndef HASHVEC_H @@ -45,7 +42,7 @@ class HashVec { const char* iomode_; // Writer creation function - std::unique_ptr createWriter(); + const std::unique_ptr createWriter() const; public: diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index 646c7344..01cd7e18 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -25,7 +25,7 @@ Writer::Writer(std::unique_ptr formatter) * @returns std::unique_ptr& Returns the private formatter pointer */ -std::unique_ptr& Writer::get_formatter() +const std::unique_ptr& Writer::get_formatter() { return formatter_; } @@ -36,7 +36,7 @@ std::unique_ptr& Writer::get_formatter() * @param[in] os Output stream to write to */ -void Multi::prep(std::ofstream& os) +void Multi::prep(std::ofstream& os) { // Find current MPI rank int current_rank; @@ -82,7 +82,7 @@ Multi::Multi(std::unique_ptr formatter) * @param[in] hashvec The vector containing all necessary data */ -void Multi::write(std::ofstream& os, std::vector> hashvec) +void Multi::write(std::ofstream& os, std::vector> hashvec) { prep(os); this->get_formatter()->executeFormat(os, hashvec); diff --git a/src/c++/writer.h b/src/c++/writer.h index 92a07234..4332ec28 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -36,7 +36,7 @@ class Writer { private: // Ptr to formatting class - std::unique_ptr formatter_; + const std::unique_ptr formatter_; protected: @@ -44,7 +44,7 @@ class Writer { // above can be changed. It will be inherited by any derived classes. explicit Writer(std::unique_ptr formatter); - std::unique_ptr& get_formatter(); + const std::unique_ptr& get_formatter(); public: From ee43d2b228f7797369f4c7764cc0dc95d2d2926b Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Mon, 28 Nov 2022 09:24:44 +0000 Subject: [PATCH 079/128] Modifying string concatenation (#72) --- src/c++/profiler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index fc0d2fdd..0eab059e 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -80,8 +80,10 @@ size_t Profiler::start(std::string_view region_name) assert (tid <= thread_traceback_.size()); // Insert this region into the thread's hash table. - std::string new_region_name = std::string(region_name) + "@" + std::to_string(tid); - size_t const hash = thread_hashtables_[tid].query_insert(new_region_name); + //std::string new_region_name = std::string(region_name) + "@" + std::to_string(tid); + region_name += "@"; + region_name += std::to_string(tid); + size_t const hash = thread_hashtables_[tid].query_insert(region_name); // Store the calliper and region start times. auto region_start_time = prof_gettime(); From 3821115203a2075b061eb1053c78fc5443d6ba26 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Tue, 29 Nov 2022 09:25:02 +0000 Subject: [PATCH 080/128] Persistent entries for each region stored in vector; use iterators. --- src/c++/hashtable.cpp | 171 +++++++++++++++++++++++++----------------- src/c++/hashtable.h | 30 +++++--- src/c++/profiler.cpp | 17 +++-- src/c++/profiler.h | 3 +- 4 files changed, 137 insertions(+), 84 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index f97b6e4e..75b53f1d 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -16,8 +16,9 @@ * */ -HashEntry::HashEntry(std::string_view region_name) - : region_name_(region_name) +RegionRecord::RegionRecord(size_t const region_hash, std::string_view region_name) + : region_hash_(region_hash) + , region_name_(region_name) , total_walltime_ (time_duration_t::zero()) , total_raw_walltime_ (time_duration_t::zero()) , self_walltime_ (time_duration_t::zero()) @@ -36,14 +37,19 @@ HashTable::HashTable(int const tid) : tid_(tid) { + // Reserve enough places in hashvec_ + hashvec_.reserve(1000); + // Set the name and hash of the profiler entry. std::string const profiler_name = "__profiler__"; profiler_hash_ = hash_function_(profiler_name); // Insert special entry for the profiler overhead time. - assert (table_.count(profiler_hash_) == 0); - table_.emplace(profiler_hash_, HashEntry(profiler_name)); - assert (table_.count(profiler_hash_) > 0); + assert (lookup_table_.count(profiler_hash_) == 0); + hashvec_.emplace_back(RegionRecord(profiler_hash_, profiler_name)); + record_iterator_t profiler_iterator = static_cast(hashvec_.size()-1); + lookup_table_.emplace(profiler_hash_, profiler_iterator); + assert (lookup_table_.count(profiler_hash_) > 0); } /** @@ -51,16 +57,23 @@ HashTable::HashTable(int const tid) * */ -size_t HashTable::query_insert(std::string_view region_name) noexcept +void HashTable::query_insert(std::string_view region_name, + size_t& hash, + record_iterator_t& record_it) noexcept { - size_t hash = hash_function_(region_name); + hash = hash_function_(region_name); - if (table_.count(hash) == 0){ - table_.emplace(hash,HashEntry(region_name)); - assert (table_.count(hash) > 0); + if (lookup_table_.count(hash) == 0){ + hashvec_.emplace_back(RegionRecord(hash, region_name)); + record_it = static_cast(hashvec_.size()-1); + lookup_table_.emplace(hash, record_it); + assert (lookup_table_.count(hash) > 0); + } + else + { + record_it = hash2iterator(hash); } - return hash; } /** @@ -69,18 +82,16 @@ size_t HashTable::query_insert(std::string_view region_name) noexcept * @param [in] time_delta The time increment to add. */ -void HashTable::update(size_t const hash, time_duration_t const time_delta) +void HashTable::update(record_iterator_t record_it, time_duration_t const time_delta) { - // Assertions - assert (table_.size() > 0); - assert (table_.count(hash) > 0); + + auto& record = hashvec_[record_it]; // Increment the walltime for this hash entry. - auto& entry = table_.at(hash); - entry.total_walltime_ += time_delta; + record.total_walltime_ += time_delta; // Update the number of times this region has been called - ++entry.call_count_; + ++record.call_count_; } @@ -90,16 +101,12 @@ void HashTable::update(size_t const hash, time_duration_t const time_delta) */ time_duration_t* HashTable::add_child_time( - size_t const hash, + record_iterator_t record_it, time_duration_t child_walltime) { - // Assertions - assert (table_.size() > 0); - assert (table_.count(hash) > 0); - - auto& entry = table_.at(hash); - entry.child_walltime_ += child_walltime; - return &entry.overhead_walltime_; + auto& record = hashvec_[record_it]; + record.child_walltime_ += child_walltime; + return &record.overhead_walltime_; } /** @@ -109,9 +116,9 @@ time_duration_t* HashTable::add_child_time( time_duration_t& HashTable::increment_profiler_calls() { - auto& entry = table_.at(profiler_hash_); - ++entry.call_count_; - return entry.total_walltime_; + auto& record = hashvec_[profiler_iterator_]; + ++record.call_count_; + return record.total_walltime_; } /** @@ -155,18 +162,17 @@ void HashTable::write() // walltime. If optimisation of this is needed, it ought to be possible to // acquire a vector of hash-selftime pairs in the correct order, then use the // hashes to look up other information directly from the hashtable. - hashvec = std::vector>(table_.cbegin(), table_.cend()); - std::sort(begin(hashvec), end(hashvec), - [](auto a, auto b) { return a.second.self_walltime_ > b.second.self_walltime_;}); + std::sort(begin(hashvec_), end(hashvec_), + [](auto a, auto b) { return a.self_walltime_ > b.self_walltime_;}); // Data entries - for (auto& [hash, entry] : hashvec) { + for (auto& record : hashvec_) { std::cout - << std::setw(40) << std::left << entry.region_name_ << " " - << std::setw(15) << std::right << entry.self_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_raw_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_walltime_.count() << " " - << std::setw(10) << std::right << entry.call_count_ << "\n"; + << std::setw(40) << std::left << record.region_name_ << " " + << std::setw(15) << std::right << record.self_walltime_.count() << " " + << std::setw(15) << std::right << record.total_raw_walltime_.count() << " " + << std::setw(15) << std::right << record.total_walltime_.count() << " " + << std::setw(10) << std::right << record.call_count_ << "\n"; } } @@ -179,18 +185,17 @@ void HashTable::write() * @param [in] hash The hash of the region to compute. */ -void HashTable::prepare_computed_times(size_t const hash) +void HashTable::prepare_computed_times(RegionRecord& record) { - auto& entry = table_.at(hash); // Self time - entry.self_walltime_ = entry.total_walltime_ - - entry.child_walltime_ - - entry.overhead_walltime_; + record.self_walltime_ = record.total_walltime_ + - record.child_walltime_ + - record.overhead_walltime_; // Total walltime with overheads attributed to the parent removed. - entry.total_raw_walltime_ = entry.total_walltime_ - - entry.overhead_walltime_; + record.total_raw_walltime_ = record.total_walltime_ + - record.overhead_walltime_; } /** @@ -204,24 +209,24 @@ void HashTable::prepare_computed_times(size_t const hash) auto total_overhead_time = time_duration_t::zero(); // Loop over entries in the hashtable. This would include the special - // profiler entry, but the HashEntry constructor will have ensured that all + // profiler entry, but the RegionRecord constructor will have ensured that all // corresponding values are zero thus far. - for (auto& [hash, entry] : table_) { - prepare_computed_times(hash); + for (auto& [hash, entry] : lookup_table_) { + prepare_computed_times(hashvec_[hash2iterator(hash)]); } /// // Check that the special profiler hash entries are all zero, even after the /// // above loop. - /// assert(table_.at(profiler_hash_).self_walltime_ == time_duration_t::zero()); - /// assert(table_.at(profiler_hash_).child_walltime_ == time_duration_t::zero()); - /// assert(table_.at(profiler_hash_).total_walltime_ == time_duration_t::zero()); - /// assert(table_.at(profiler_hash_).total_raw_walltime_ == time_duration_t::zero()); + /// assert(lookup_table_.at(profiler_hash_).self_walltime_ == time_duration_t::zero()); + /// assert(lookup_table_.at(profiler_hash_).child_walltime_ == time_duration_t::zero()); + /// assert(lookup_table_.at(profiler_hash_).total_walltime_ == time_duration_t::zero()); + /// assert(lookup_table_.at(profiler_hash_).total_raw_walltime_ == time_duration_t::zero()); // Set values for the profiler entry specifically in the hashtable. - // table_.at(profiler_hash_).self_walltime_ = total_overhead_time_; - // table_.at(profiler_hash_).child_walltime_ = time_duration_t::zero(); - // table_.at(profiler_hash_).total_walltime_ = total_overhead_time_; - // table_.at(profiler_hash_).total_raw_walltime_ = total_overhead_time_; + // lookup_table_.at(profiler_hash_).self_walltime_ = total_overhead_time_; + // lookup_table_.at(profiler_hash_).child_walltime_ = time_duration_t::zero(); + // lookup_table_.at(profiler_hash_).total_walltime_ = total_overhead_time_; + // lookup_table_.at(profiler_hash_).total_raw_walltime_ = total_overhead_time_; // std::cout << "My thread ID: " << omp_get_thread_num() << std::endl; // std::cout << "Table thread ID: " << tid_ << std::endl; @@ -236,7 +241,7 @@ void HashTable::prepare_computed_times(size_t const hash) std::vector HashTable::list_keys() { std::vector keys; - for (auto const& key : table_) + for (auto const& key : lookup_table_) { keys.push_back(key.first); } @@ -250,7 +255,8 @@ std::vector HashTable::list_keys() double HashTable::get_total_walltime(size_t const hash) const { - return table_.at(hash).total_walltime_.count(); + auto& record = hashvec_[hash2iterator_const(hash)]; + return record.total_walltime_.count(); } /** @@ -263,8 +269,9 @@ double HashTable::get_total_walltime(size_t const hash) const double HashTable::get_total_raw_walltime(size_t const hash) { - prepare_computed_times(hash); - return table_.at(hash).total_raw_walltime_.count(); + auto& record = hashvec_[hash2iterator(hash)]; + prepare_computed_times(record); + return record.total_raw_walltime_.count(); } /** @@ -275,7 +282,8 @@ double HashTable::get_total_raw_walltime(size_t const hash) double HashTable::get_overhead_walltime(size_t const hash) const { - return table_.at(hash).overhead_walltime_.count(); + auto& record = hashvec_[hash2iterator_const(hash)]; + return record.overhead_walltime_.count(); } /** @@ -287,8 +295,9 @@ double HashTable::get_overhead_walltime(size_t const hash) const double HashTable::get_self_walltime(size_t const hash) { - prepare_computed_times(hash); - return table_.at(hash).self_walltime_.count(); + auto& record = hashvec_[hash2iterator(hash)]; + prepare_computed_times(record); + return record.self_walltime_.count(); } /** @@ -300,7 +309,8 @@ double HashTable::get_self_walltime(size_t const hash) double HashTable::get_child_walltime(size_t const hash) const { - return table_.at(hash).child_walltime_.count(); + auto& record = hashvec_[hash2iterator_const(hash)]; + return record.child_walltime_.count(); } /** @@ -310,7 +320,8 @@ double HashTable::get_child_walltime(size_t const hash) const std::string HashTable::get_region_name(size_t const hash) const { - return table_.at(hash).region_name_; + auto& record = hashvec_[hash2iterator_const(hash)]; + return record.region_name_; } /** @@ -325,7 +336,8 @@ std::string HashTable::get_region_name(size_t const hash) const unsigned long long int HashTable::get_call_count(size_t const hash) const { - return table_.at(hash).call_count_; + auto& record = hashvec_[hash2iterator_const(hash)]; + return record.call_count_; } /** @@ -338,6 +350,31 @@ unsigned long long int HashTable::get_call_count(size_t const hash) const unsigned long long int HashTable::get_prof_call_count() const { - return table_.at(profiler_hash_).call_count_; + auto& record = hashvec_[hash2iterator_const(profiler_hash_)]; + return record.call_count_; +} + +/** + * @ brief + * + */ + +record_iterator_t HashTable::hash2iterator(size_t const hash) +{ + // Assertions + assert (lookup_table_.size() > 0); + assert (lookup_table_.count(hash) > 0); + + return lookup_table_.at(hash); } +record_iterator_t HashTable::hash2iterator_const(size_t const hash) const +{ + // Assertions + assert (lookup_table_.size() > 0); + assert (lookup_table_.count(hash) > 0); + + return lookup_table_.at(hash); +} + + diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index cfdcc620..cc6883fc 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -9,7 +9,7 @@ * @brief Handles entries for each timed region. * * In order to store region timings, one struct and one class are defined. The - * struct (HashEntry) collects together information pertinent to a single + * struct (RegionRecord) collects together information pertinent to a single * profiled region, such as its name, total time and self time. * * The HashTable class contains a hashtable to hold the hash entries (see @@ -39,14 +39,15 @@ * */ -struct HashEntry{ +struct RegionRecord{ public: // Constructor - HashEntry() = delete; - explicit HashEntry(std::string_view); + RegionRecord() = delete; + explicit RegionRecord(size_t const, std::string_view); // Data members + size_t region_hash_; std::string region_name_; time_duration_t total_walltime_; time_duration_t total_raw_walltime_; @@ -57,6 +58,8 @@ struct HashEntry{ }; +typedef std::vector::size_type record_iterator_t; + /** * @brief Wraps STL hashtables with additional functionality. * @@ -72,12 +75,19 @@ class HashTable{ // Members int tid_; size_t profiler_hash_; - std::unordered_map table_; + record_iterator_t profiler_iterator_; + + // Hash function std::hash hash_function_; - std::vector> hashvec; + + // Hashtable containing locations of region records. + std::unordered_map lookup_table_; + + // Vector of region records. + std::vector hashvec_; // Private member functions - void prepare_computed_times(size_t const); + void prepare_computed_times(RegionRecord&); void prepare_computed_times_all(); public: @@ -87,8 +97,8 @@ class HashTable{ HashTable(int); // Prototypes - size_t query_insert(std::string_view) noexcept; - void update(size_t const, time_duration_t const); + void query_insert(std::string_view, size_t&, record_iterator_t&) noexcept; + void update(record_iterator_t, time_duration_t const); void write(); // Member functions @@ -105,6 +115,8 @@ class HashTable{ std::string get_region_name(size_t const hash) const; unsigned long long int get_call_count(size_t const hash) const; unsigned long long int get_prof_call_count() const; + record_iterator_t hash2iterator(size_t const); + record_iterator_t hash2iterator_const(size_t const) const; }; #endif diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index cca411a1..f73e5892 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -30,9 +30,11 @@ Profiler::StartCalliperValues::StartCalliperValues() {} Profiler::StartCalliperValues::StartCalliperValues( + record_iterator_t my_iterator, time_point_t region_start_time, time_point_t calliper_start_time) - : region_start_time_(region_start_time) + : my_iterator_ (my_iterator) + , region_start_time_ (region_start_time) , calliper_start_time_(calliper_start_time) {} @@ -117,13 +119,14 @@ size_t Profiler::start2(std::string_view region_name) assert (tid <= thread_traceback_.size()); // Insert this region into the thread's hash table. - size_t const hash = thread_hashtables_[tid].query_insert(region_name); + size_t hash; + record_iterator_t record_iterator; + thread_hashtables_[tid].query_insert(region_name, hash, record_iterator); // Store the calliper and region start times. auto region_start_time = prof_gettime(); StartCalliperValues new_times = StartCalliperValues( - region_start_time, logged_calliper_start_time); - + record_iterator, region_start_time, logged_calliper_start_time); ++call_depth; auto call_depth_it = static_cast(call_depth); @@ -178,7 +181,7 @@ void Profiler::stop(size_t const hash) auto region_duration = region_stop_time - start_calliper_times.region_start_time_; // Do the hashtable update for the child region. - thread_hashtables_[tid].update(hash, region_duration); + thread_hashtables_[tid].update(start_calliper_times.my_iterator_, region_duration); // Precompute times as far as possible. We just need the calliper stop time // later. @@ -194,9 +197,9 @@ void Profiler::stop(size_t const hash) // Acquire parent pointers if (call_depth > 0){ auto parent_depth_it = static_cast(call_depth-1); - size_t parent_hash = thread_traceback_[tid].at(parent_depth_it).first; + record_iterator_t parent_iterator = thread_traceback_[tid].at(parent_depth_it).second.my_iterator_; parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( - parent_hash, region_duration); + parent_iterator, region_duration); } // Increment profiler calls, and get a reference to the total overhead time. diff --git a/src/c++/profiler.h b/src/c++/profiler.h index b5984d3c..3bface40 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -48,9 +48,10 @@ class Profiler // Constructors StartCalliperValues(); - StartCalliperValues(time_point_t, time_point_t); + StartCalliperValues(record_iterator_t, time_point_t, time_point_t); // Data members + record_iterator_t my_iterator_; time_point_t region_start_time_; time_point_t calliper_start_time_; }; From 32dd8505482bc64b913c670246658c9d08778238 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 29 Nov 2022 14:40:07 +0000 Subject: [PATCH 081/128] Fixing mistake in string concatenation (#72) --- src/c++/hashvec.h | 2 -- src/c++/profiler.cpp | 8 ++++---- src/c++/writer.cpp | 10 +++++----- src/c++/writer.h | 12 +++--------- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 2fa5c717..0178c516 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -16,9 +16,7 @@ #ifndef HASHVEC_H #define HASHVEC_H -#include "formatter.h" #include "writer.h" -#include "hashtable.h" #include /** diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 0eab059e..25c06ef7 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -80,10 +80,10 @@ size_t Profiler::start(std::string_view region_name) assert (tid <= thread_traceback_.size()); // Insert this region into the thread's hash table. - //std::string new_region_name = std::string(region_name) + "@" + std::to_string(tid); - region_name += "@"; - region_name += std::to_string(tid); - size_t const hash = thread_hashtables_[tid].query_insert(region_name); + std::string new_region_name = std::string(region_name); + new_region_name += '@'; + new_region_name += std::to_string(tid); + size_t const hash = thread_hashtables_[tid].query_insert(new_region_name); // Store the calliper and region start times. auto region_start_time = prof_gettime(); diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index 01cd7e18..a9a326bd 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -25,10 +25,10 @@ Writer::Writer(std::unique_ptr formatter) * @returns std::unique_ptr& Returns the private formatter pointer */ -const std::unique_ptr& Writer::get_formatter() -{ - return formatter_; -} +// const std::unique_ptr Writer::get_formatter() +// { +// return formatter_; +// } /** * @brief Opens a unique file per mpi rank @@ -85,7 +85,7 @@ Multi::Multi(std::unique_ptr formatter) void Multi::write(std::ofstream& os, std::vector> hashvec) { prep(os); - this->get_formatter()->executeFormat(os, hashvec); + this->formatter_->executeFormat(os, hashvec); os.flush(); os.close(); } diff --git a/src/c++/writer.h b/src/c++/writer.h index 4332ec28..c6e98ae2 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -22,8 +22,6 @@ #include #include "formatter.h" -struct HashEntry; - /** * @brief Writer class * @@ -33,19 +31,15 @@ struct HashEntry; class Writer { - private: + protected: // Ptr to formatting class const std::unique_ptr formatter_; - protected: - - // The constructor, this is the only way in which the formatter pointer - // above can be changed. It will be inherited by any derived classes. + // The constructor, which gives the formatter pointer above a value. + // It will be inherited by any derived classes. explicit Writer(std::unique_ptr formatter); - const std::unique_ptr& get_formatter(); - public: virtual ~Writer() = default; From 1b82942dd1c5ff02ce1209192aa530c3273290f7 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 29 Nov 2022 14:45:00 +0000 Subject: [PATCH 082/128] Erroneous line in HashTable::append_to() (#72) --- src/c++/hashtable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index a05d108c..0a71326a 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -196,7 +196,7 @@ void HashTable::append_to(std::vector>& hashvec) // Remove __profiler__ entries with 0 calls auto it = table_.find(profiler_hash_); - if (it != NULL && it->second.call_count_ == 0) { table_.erase(it); } + if (it != table_.end() && it->second.call_count_ == 0) { table_.erase(it); } // Insert local table into hashvec hashvec.insert(hashvec.end(), table_.begin(), table_.end()); From 4200288274ffce2e7e42926e83cd4ea49a391394 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:51:28 +0000 Subject: [PATCH 083/128] Better timings - don't rehash the hashes (plus a few consts). --- src/c++/hashtable.cpp | 24 ++++++++++++------------ src/c++/hashtable.h | 11 ++++++++--- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 75b53f1d..00ff2a12 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -59,21 +59,21 @@ HashTable::HashTable(int const tid) void HashTable::query_insert(std::string_view region_name, size_t& hash, - record_iterator_t& record_it) noexcept + record_iterator_t& record_iterator) noexcept { hash = hash_function_(region_name); - if (lookup_table_.count(hash) == 0){ - hashvec_.emplace_back(RegionRecord(hash, region_name)); - record_it = static_cast(hashvec_.size()-1); - lookup_table_.emplace(hash, record_it); - assert (lookup_table_.count(hash) > 0); + if (auto search = lookup_table_.find(hash); search != lookup_table_.end()) + { + record_iterator = search->second; } else { - record_it = hash2iterator(hash); + hashvec_.emplace_back(RegionRecord(hash, region_name)); + record_iterator = static_cast(hashvec_.size()-1); + lookup_table_.emplace(hash, record_iterator); + assert (lookup_table_.count(hash) > 0); } - } /** @@ -82,10 +82,10 @@ void HashTable::query_insert(std::string_view region_name, * @param [in] time_delta The time increment to add. */ -void HashTable::update(record_iterator_t record_it, time_duration_t const time_delta) +void HashTable::update(record_iterator_t const record_iterator, time_duration_t const time_delta) { - auto& record = hashvec_[record_it]; + auto& record = hashvec_[record_iterator]; // Increment the walltime for this hash entry. record.total_walltime_ += time_delta; @@ -101,10 +101,10 @@ void HashTable::update(record_iterator_t record_it, time_duration_t const time_d */ time_duration_t* HashTable::add_child_time( - record_iterator_t record_it, + record_iterator_t const record_iterator, time_duration_t child_walltime) { - auto& record = hashvec_[record_it]; + auto& record = hashvec_[record_iterator]; record.child_walltime_ += child_walltime; return &record.overhead_walltime_; } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index cc6883fc..35df349f 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -31,6 +31,11 @@ #include "prof_gettime.h" +struct NullHashFunction { + std::size_t operator()(std::size_t const& key) const { + return key; + } +}; /** * @brief Structure to hold information for a particular routine. @@ -81,7 +86,7 @@ class HashTable{ std::hash hash_function_; // Hashtable containing locations of region records. - std::unordered_map lookup_table_; + std::unordered_map lookup_table_; // Vector of region records. std::vector hashvec_; @@ -98,12 +103,12 @@ class HashTable{ // Prototypes void query_insert(std::string_view, size_t&, record_iterator_t&) noexcept; - void update(record_iterator_t, time_duration_t const); + void update(record_iterator_t const, time_duration_t const); void write(); // Member functions std::vector list_keys(); - time_duration_t* add_child_time(size_t const, time_duration_t); + time_duration_t* add_child_time(record_iterator_t const, time_duration_t); time_duration_t& increment_profiler_calls(); // Getters From b8ebee4ba8e641b25b7a25ac1c07f6308cc1296a Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 29 Nov 2022 15:08:27 +0000 Subject: [PATCH 084/128] Changing final loop in call count unit test (#72) --- tests/unit_tests/c++/test_callcount.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/unit_tests/c++/test_callcount.cpp b/tests/unit_tests/c++/test_callcount.cpp index de6b7d0f..caf109ac 100644 --- a/tests/unit_tests/c++/test_callcount.cpp +++ b/tests/unit_tests/c++/test_callcount.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "profiler.h" @@ -50,7 +51,7 @@ TEST(HashEntryTest,CallCountTest) } // Give prof_sub_shared a value for later use in EXPECT's - prof_sub_shared.push_back( std::make_pair(thread_id, prof_sub_private) ); + prof_sub_shared.push_back(std::make_pair(thread_id, prof_sub_private)); } // Stop main region @@ -59,14 +60,12 @@ TEST(HashEntryTest,CallCountTest) // Check call_count_ is the number expected on all threads. On most threads, // the profiler calliper call count should match this number, except on thread // zero which includes the main region callipers. - for (int j = 0; j < num_threads; ++j) + for (auto& [thread, hash] : prof_sub_shared) { - size_t J = static_cast(j); - int i = prof_sub_shared[J].first; - EXPECT_EQ(prof.get_call_count(prof_sub_shared[J].second,i),num_threads-i); + EXPECT_EQ(prof.get_call_count(hash,thread), num_threads-thread); - int incr = (j==0) ? 1 : 0; - EXPECT_EQ(prof.get_prof_call_count(j),num_threads-j+incr); + int incr = (thread==0) ? 1 : 0; + EXPECT_EQ(prof.get_prof_call_count(thread), num_threads-thread+incr); } } From 73293186b9fdd0aaebe64394f183dd6feb9eb651 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 29 Nov 2022 16:00:18 +0000 Subject: [PATCH 085/128] Removing accidental addition of new coverage worklow (#72) --- .github/workflows/coverage.yml | 102 --------------------------------- 1 file changed, 102 deletions(-) delete mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 256e620b..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Unit test coverage - -on: - workflow_dispatch: - push: - branches: [ main ] - paths: - - 'src/**.h' - - 'src/**.cpp' - - 'tests/**' - - '.github/workflows/coverage.yml' - -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - compiler: [ - {c: gcc-10, cpp: g++-10, fortran: gfortran-10} - ] - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Create conda environment - # Setup a virtual conda environment - uses: conda-incubator/setup-miniconda@v2 - with: - auto-activate-base: false - python-version: 3.9 - channels: conda-forge, defaults - activate-environment: myenv - - - name: Install Lcov - # Install lcov from conda (graphical front-end for gcov reports) - shell: bash -l {0} - run: conda install lcov - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory - # Key thing here is the "--coverage" CXX flag which generates - # .gcno files for gcov. - run: > - cmake -B ${{github.workspace}}/build - -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} - -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} - -DCMAKE_Fortran_COMPILER=${{ matrix.compiler.fortran }} - -DBUILD_TESTS=ON - -DBUILD_FORTRAN_TESTS=OFF - -DINCLUDE_GTEST=ON - -DCMAKE_CXX_FLAGS="-g --coverage" - -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON - -DENABLE_DOXYGEN=OFF - - - name: Build - # Build with the given configuration - run: cmake --build ${{github.workspace}}/build - - - name: Make - working-directory: ${{github.workspace}}/build - run: make - - - name: Run tests - # Run all the unit test executables - working-directory: ${{github.workspace}}/build - run: ctest - - - name: Run Gcov - run: gcov --demangled-names --human-readable ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir/*.gcno - - - name: Inital Lcov Run - shell: bash -l {0} - run: lcov --capture --directory ${{github.workspace}}/build/src/c++/CMakeFiles/profiler.dir --output-file basecoverage.info - - - name: Extract relevant info - # Ignore all the STL header files from the initial lcov .info file - shell: bash -l {0} - run: lcov --extract basecoverage.info '${{github.workspace}}/src/*' --output-file coverage.info - - - name: Generate html - shell: bash -l {0} - run: genhtml coverage.info --output-directory out - - - name: Update github pages - run: | - mkdir -p ${{github.workspace}}/docs/coverage - cp -rf ${{github.workspace}}/out/* ${{github.workspace}}/docs/coverage/. - git config user.name 'github-actions[bot]' - git config user.email 'github-actions[bot]' - git add --force docs/coverage - git stash - git checkout -b gh-pages - rm -rf ${{github.workspace}}/docs/coverage - git stash pop - git reset - git add --force docs/coverage - git commit --allow-empty -m "Update coverage documentation" - git push -f origin gh-pages - - \ No newline at end of file From fa1c2adf12b264642464f5b53a19573c19d1ac17 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 29 Nov 2022 16:04:39 +0000 Subject: [PATCH 086/128] Fixing documentation workflow based on removal of coverage one (#72) --- .github/workflows/documentation.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index eeb7c545..836a708a 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -42,17 +42,17 @@ jobs: - name: Update Doxygen Pages run: | - mkdir -p ${{github.workspace}}/docs/documentation - cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/documentation/. + mkdir -p ${{github.workspace}}/docs + cp -rf ${{github.workspace}}/build/html/* ${{github.workspace}}/docs/. git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - git add --force docs/documentation + git add --force docs git stash git remote update git checkout gh-pages - rm -rf ${{github.workspace}}/docs/documentation + git rm -rf docs git stash pop git reset - git add --force docs/documentation + git add --force docs git commit --allow-empty -m "Update Doxygen documentation" git push From 75935dbc9a9a475934c06c15007bff9edca9b7d4 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 29 Nov 2022 16:11:12 +0000 Subject: [PATCH 087/128] Hopeful solution to call count unit test error (#72) --- tests/unit_tests/c++/test_callcount.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/c++/test_callcount.cpp b/tests/unit_tests/c++/test_callcount.cpp index caf109ac..0e384a2c 100644 --- a/tests/unit_tests/c++/test_callcount.cpp +++ b/tests/unit_tests/c++/test_callcount.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "profiler.h" @@ -24,7 +23,7 @@ TEST(HashEntryTest,CallCountTest) int num_threads = 1; // Start parallel region -#pragma omp parallel default(none) shared(prof_sub_shared, prof, num_threads, std::cout) +#pragma omp parallel default(none) shared(prof_sub_shared, prof, num_threads) { // Get total number of threads, only need to calculate on a single thread // since value won't change. @@ -51,7 +50,10 @@ TEST(HashEntryTest,CallCountTest) } // Give prof_sub_shared a value for later use in EXPECT's - prof_sub_shared.push_back(std::make_pair(thread_id, prof_sub_private)); +#pragma omp critical + { + prof_sub_shared.push_back(std::make_pair(thread_id, prof_sub_private)); + } } // Stop main region From 383fd347fbb26d492fed63306fbdd762d799847a Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:08:39 +0000 Subject: [PATCH 088/128] Modifications to query_insert() --- src/c++/hashtable.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 00ff2a12..86e59fc1 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -46,7 +46,7 @@ HashTable::HashTable(int const tid) // Insert special entry for the profiler overhead time. assert (lookup_table_.count(profiler_hash_) == 0); - hashvec_.emplace_back(RegionRecord(profiler_hash_, profiler_name)); + hashvec_.emplace_back(profiler_hash_, profiler_name); record_iterator_t profiler_iterator = static_cast(hashvec_.size()-1); lookup_table_.emplace(profiler_hash_, profiler_iterator); assert (lookup_table_.count(profiler_hash_) > 0); @@ -63,17 +63,21 @@ void HashTable::query_insert(std::string_view region_name, { hash = hash_function_(region_name); - if (auto search = lookup_table_.find(hash); search != lookup_table_.end()) - { - record_iterator = search->second; - } - else + auto null_iterator = static_cast(-1); + auto [it, inserted] = lookup_table_.try_emplace(hash, null_iterator); + + // If insertion happened, then this is new entry ... + if (inserted) { - hashvec_.emplace_back(RegionRecord(hash, region_name)); + hashvec_.emplace_back(hash, region_name); record_iterator = static_cast(hashvec_.size()-1); - lookup_table_.emplace(hash, record_iterator); + it->second = record_iterator; assert (lookup_table_.count(hash) > 0); } + else{ + record_iterator = it->second; + } + } /** From fa4e23966e6e4baa6dc97ae2266ed695fae94492 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 30 Nov 2022 11:05:04 +0000 Subject: [PATCH 089/128] Altering call count test - removing pair from vector (#72) --- .github/workflows/build.yml | 2 +- tests/unit_tests/c++/test_callcount.cpp | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d652640e..5366b6a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: diff --git a/tests/unit_tests/c++/test_callcount.cpp b/tests/unit_tests/c++/test_callcount.cpp index 0e384a2c..51ff01ed 100644 --- a/tests/unit_tests/c++/test_callcount.cpp +++ b/tests/unit_tests/c++/test_callcount.cpp @@ -18,8 +18,7 @@ TEST(HashEntryTest,CallCountTest) // Declare a shared sub-region hash. Initialise num_threads so that the // compiler knows the 'for' loop inside the parallel region will definitely // happen, and therefore doesn't think prof_sub_private remains unitialised. - //size_t prof_sub_shared; - std::vector> prof_sub_shared; + std::vector prof_sub_shared; int num_threads = 1; // Start parallel region @@ -30,10 +29,11 @@ TEST(HashEntryTest,CallCountTest) #pragma omp single { num_threads = omp_get_num_threads(); + prof_sub_shared.resize(static_cast(num_threads)); } // Current thread ID - int thread_id = omp_get_thread_num(); + int thread_id = omp_get_thread_num(); // Also initialise prof_sub_private. The compiler doesn't know how many // iterations of the subsequent 'for' loop there will be, and may flag @@ -51,8 +51,8 @@ TEST(HashEntryTest,CallCountTest) // Give prof_sub_shared a value for later use in EXPECT's #pragma omp critical - { - prof_sub_shared.push_back(std::make_pair(thread_id, prof_sub_private)); + { + prof_sub_shared[static_cast(thread_id)] = prof_sub_private; } } @@ -62,10 +62,19 @@ TEST(HashEntryTest,CallCountTest) // Check call_count_ is the number expected on all threads. On most threads, // the profiler calliper call count should match this number, except on thread // zero which includes the main region callipers. - for (auto& [thread, hash] : prof_sub_shared) + /*for (auto& hash : prof_sub_shared) { EXPECT_EQ(prof.get_call_count(hash,thread), num_threads-thread); + int incr = (thread==0) ? 1 : 0; + EXPECT_EQ(prof.get_prof_call_count(thread), num_threads-thread+incr); + } */ + + for (int thread = 0; thread < num_threads; ++thread) + { + size_t hash = prof_sub_shared[static_cast(thread)]; + EXPECT_EQ(prof.get_call_count(hash,thread), num_threads-thread); + int incr = (thread==0) ? 1 : 0; EXPECT_EQ(prof.get_prof_call_count(thread), num_threads-thread+incr); } From 060c551d046dca5320b298e06dcec1436e3877ea Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 30 Nov 2022 11:40:55 +0000 Subject: [PATCH 090/128] Final tidying up (#72) --- .github/workflows/build.yml | 2 +- .gitignore | 2 -- tests/unit_tests/c++/test_callcount.cpp | 8 -------- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5366b6a8..550ace2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: {c: gcc-10, cpp: g++-10, fortran: gfortran-10}, {c: clang-12, cpp: clang++-12, fortran: gfortran-10} ] - + steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index a4849c00..3ee4c074 100644 --- a/.gitignore +++ b/.gitignore @@ -50,9 +50,7 @@ flycheck_*.el ### Visual Studio Code template -.vscode* .vscode/* -.vscode/settings.json !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json diff --git a/tests/unit_tests/c++/test_callcount.cpp b/tests/unit_tests/c++/test_callcount.cpp index 51ff01ed..b5343678 100644 --- a/tests/unit_tests/c++/test_callcount.cpp +++ b/tests/unit_tests/c++/test_callcount.cpp @@ -62,14 +62,6 @@ TEST(HashEntryTest,CallCountTest) // Check call_count_ is the number expected on all threads. On most threads, // the profiler calliper call count should match this number, except on thread // zero which includes the main region callipers. - /*for (auto& hash : prof_sub_shared) - { - EXPECT_EQ(prof.get_call_count(hash,thread), num_threads-thread); - - int incr = (thread==0) ? 1 : 0; - EXPECT_EQ(prof.get_prof_call_count(thread), num_threads-thread+incr); - } */ - for (int thread = 0; thread < num_threads; ++thread) { size_t hash = prof_sub_shared[static_cast(thread)]; From 165d09a093643d317c6447f5d94d7f253135b9d5 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 30 Nov 2022 18:18:37 +0000 Subject: [PATCH 091/128] Index-based addressing for the vector. Sorting needs attention. --- src/c++/hashtable.cpp | 65 +++++++++++++++++++++---------------------- src/c++/hashtable.h | 16 +++++------ src/c++/profiler.cpp | 14 +++++----- src/c++/profiler.h | 4 +-- 4 files changed, 49 insertions(+), 50 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 86e59fc1..c906f493 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -47,8 +47,8 @@ HashTable::HashTable(int const tid) // Insert special entry for the profiler overhead time. assert (lookup_table_.count(profiler_hash_) == 0); hashvec_.emplace_back(profiler_hash_, profiler_name); - record_iterator_t profiler_iterator = static_cast(hashvec_.size()-1); - lookup_table_.emplace(profiler_hash_, profiler_iterator); + profiler_index_ = hashvec_.size()-1; + lookup_table_.emplace(profiler_hash_, profiler_index_); assert (lookup_table_.count(profiler_hash_) > 0); } @@ -59,24 +59,22 @@ HashTable::HashTable(int const tid) void HashTable::query_insert(std::string_view region_name, size_t& hash, - record_iterator_t& record_iterator) noexcept + record_index_t& record_index) noexcept { hash = hash_function_(region_name); - auto null_iterator = static_cast(-1); - auto [it, inserted] = lookup_table_.try_emplace(hash, null_iterator); - - // If insertion happened, then this is new entry ... - if (inserted) + // Found entry, + if (auto search = lookup_table_.find(hash); search != lookup_table_.end()) + { + record_index = search->second; + } + else { hashvec_.emplace_back(hash, region_name); - record_iterator = static_cast(hashvec_.size()-1); - it->second = record_iterator; + record_index = hashvec_.size()-1; + lookup_table_.emplace(hash, record_index); assert (lookup_table_.count(hash) > 0); } - else{ - record_iterator = it->second; - } } @@ -86,10 +84,10 @@ void HashTable::query_insert(std::string_view region_name, * @param [in] time_delta The time increment to add. */ -void HashTable::update(record_iterator_t const record_iterator, time_duration_t const time_delta) +void HashTable::update(record_index_t const record_index, time_duration_t const time_delta) { - auto& record = hashvec_[record_iterator]; + auto& record = hashvec_[record_index]; // Increment the walltime for this hash entry. record.total_walltime_ += time_delta; @@ -105,10 +103,10 @@ void HashTable::update(record_iterator_t const record_iterator, time_duration_t */ time_duration_t* HashTable::add_child_time( - record_iterator_t const record_iterator, - time_duration_t child_walltime) + record_index_t const record_index, + time_duration_t const child_walltime) { - auto& record = hashvec_[record_iterator]; + auto& record = hashvec_[record_index]; record.child_walltime_ += child_walltime; return &record.overhead_walltime_; } @@ -120,7 +118,7 @@ time_duration_t* HashTable::add_child_time( time_duration_t& HashTable::increment_profiler_calls() { - auto& record = hashvec_[profiler_iterator_]; + auto& record = hashvec_[profiler_index_]; ++record.call_count_; return record.total_walltime_; } @@ -215,8 +213,8 @@ void HashTable::prepare_computed_times(RegionRecord& record) // Loop over entries in the hashtable. This would include the special // profiler entry, but the RegionRecord constructor will have ensured that all // corresponding values are zero thus far. - for (auto& [hash, entry] : lookup_table_) { - prepare_computed_times(hashvec_[hash2iterator(hash)]); + for (auto& [hash, index] : lookup_table_) { + prepare_computed_times(hash2record(hash)); } /// // Check that the special profiler hash entries are all zero, even after the @@ -259,7 +257,8 @@ std::vector HashTable::list_keys() double HashTable::get_total_walltime(size_t const hash) const { - auto& record = hashvec_[hash2iterator_const(hash)]; + auto& record = hash2record_const(hash); + return record.total_walltime_.count(); } @@ -273,7 +272,7 @@ double HashTable::get_total_walltime(size_t const hash) const double HashTable::get_total_raw_walltime(size_t const hash) { - auto& record = hashvec_[hash2iterator(hash)]; + auto& record = hash2record(hash); prepare_computed_times(record); return record.total_raw_walltime_.count(); } @@ -286,7 +285,7 @@ double HashTable::get_total_raw_walltime(size_t const hash) double HashTable::get_overhead_walltime(size_t const hash) const { - auto& record = hashvec_[hash2iterator_const(hash)]; + auto& record = hash2record_const(hash); return record.overhead_walltime_.count(); } @@ -299,7 +298,7 @@ double HashTable::get_overhead_walltime(size_t const hash) const double HashTable::get_self_walltime(size_t const hash) { - auto& record = hashvec_[hash2iterator(hash)]; + auto& record = hash2record(hash); prepare_computed_times(record); return record.self_walltime_.count(); } @@ -313,7 +312,7 @@ double HashTable::get_self_walltime(size_t const hash) double HashTable::get_child_walltime(size_t const hash) const { - auto& record = hashvec_[hash2iterator_const(hash)]; + auto& record = hash2record_const(hash); return record.child_walltime_.count(); } @@ -324,7 +323,7 @@ double HashTable::get_child_walltime(size_t const hash) const std::string HashTable::get_region_name(size_t const hash) const { - auto& record = hashvec_[hash2iterator_const(hash)]; + auto& record = hash2record_const(hash); return record.region_name_; } @@ -340,7 +339,7 @@ std::string HashTable::get_region_name(size_t const hash) const unsigned long long int HashTable::get_call_count(size_t const hash) const { - auto& record = hashvec_[hash2iterator_const(hash)]; + auto& record = hash2record_const(hash); return record.call_count_; } @@ -354,7 +353,7 @@ unsigned long long int HashTable::get_call_count(size_t const hash) const unsigned long long int HashTable::get_prof_call_count() const { - auto& record = hashvec_[hash2iterator_const(profiler_hash_)]; + auto& record = hash2record_const(profiler_hash_); return record.call_count_; } @@ -363,22 +362,22 @@ unsigned long long int HashTable::get_prof_call_count() const * */ -record_iterator_t HashTable::hash2iterator(size_t const hash) +RegionRecord& HashTable::hash2record(size_t const hash) { // Assertions assert (lookup_table_.size() > 0); assert (lookup_table_.count(hash) > 0); - return lookup_table_.at(hash); + return hashvec_[lookup_table_.at(hash)]; } -record_iterator_t HashTable::hash2iterator_const(size_t const hash) const +RegionRecord const& HashTable::hash2record_const(size_t const hash) const { // Assertions assert (lookup_table_.size() > 0); assert (lookup_table_.count(hash) > 0); - return lookup_table_.at(hash); + return hashvec_[lookup_table_.at(hash)]; } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 35df349f..0e6e1200 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -63,7 +63,7 @@ struct RegionRecord{ }; -typedef std::vector::size_type record_iterator_t; +typedef std::vector::size_type record_index_t; /** * @brief Wraps STL hashtables with additional functionality. @@ -80,13 +80,13 @@ class HashTable{ // Members int tid_; size_t profiler_hash_; - record_iterator_t profiler_iterator_; + record_index_t profiler_index_; // Hash function std::hash hash_function_; // Hashtable containing locations of region records. - std::unordered_map lookup_table_; + std::unordered_map lookup_table_; // Vector of region records. std::vector hashvec_; @@ -94,6 +94,8 @@ class HashTable{ // Private member functions void prepare_computed_times(RegionRecord&); void prepare_computed_times_all(); + RegionRecord& hash2record(size_t const); + RegionRecord const& hash2record_const(size_t const) const; public: @@ -102,13 +104,13 @@ class HashTable{ HashTable(int); // Prototypes - void query_insert(std::string_view, size_t&, record_iterator_t&) noexcept; - void update(record_iterator_t const, time_duration_t const); + void query_insert(std::string_view, size_t&, record_index_t&) noexcept; + void update(record_index_t const, time_duration_t const); void write(); // Member functions std::vector list_keys(); - time_duration_t* add_child_time(record_iterator_t const, time_duration_t); + time_duration_t* add_child_time(record_index_t const, time_duration_t const); time_duration_t& increment_profiler_calls(); // Getters @@ -120,8 +122,6 @@ class HashTable{ std::string get_region_name(size_t const hash) const; unsigned long long int get_call_count(size_t const hash) const; unsigned long long int get_prof_call_count() const; - record_iterator_t hash2iterator(size_t const); - record_iterator_t hash2iterator_const(size_t const) const; }; #endif diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index f73e5892..521df7e6 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -30,7 +30,7 @@ Profiler::StartCalliperValues::StartCalliperValues() {} Profiler::StartCalliperValues::StartCalliperValues( - record_iterator_t my_iterator, + record_index_t my_iterator, time_point_t region_start_time, time_point_t calliper_start_time) : my_iterator_ (my_iterator) @@ -120,13 +120,13 @@ size_t Profiler::start2(std::string_view region_name) // Insert this region into the thread's hash table. size_t hash; - record_iterator_t record_iterator; - thread_hashtables_[tid].query_insert(region_name, hash, record_iterator); + record_index_t record_index; + thread_hashtables_[tid].query_insert(region_name, hash, record_index); // Store the calliper and region start times. auto region_start_time = prof_gettime(); StartCalliperValues new_times = StartCalliperValues( - record_iterator, region_start_time, logged_calliper_start_time); + record_index, region_start_time, logged_calliper_start_time); ++call_depth; auto call_depth_it = static_cast(call_depth); @@ -196,10 +196,10 @@ void Profiler::stop(size_t const hash) // Acquire parent pointers if (call_depth > 0){ - auto parent_depth_it = static_cast(call_depth-1); - record_iterator_t parent_iterator = thread_traceback_[tid].at(parent_depth_it).second.my_iterator_; + auto parent_depth = static_cast(call_depth-1); + record_index_t parent_index = thread_traceback_[tid].at(parent_depth).second.my_iterator_; parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( - parent_iterator, region_duration); + parent_index, region_duration); } // Increment profiler calls, and get a reference to the total overhead time. diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 3bface40..a68aa2d2 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -48,10 +48,10 @@ class Profiler // Constructors StartCalliperValues(); - StartCalliperValues(record_iterator_t, time_point_t, time_point_t); + StartCalliperValues(record_index_t, time_point_t, time_point_t); // Data members - record_iterator_t my_iterator_; + record_index_t my_iterator_; time_point_t region_start_time_; time_point_t calliper_start_time_; }; From 35e88593d8cbdb386801588466c8f9f317942488 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 30 Nov 2022 22:56:28 +0000 Subject: [PATCH 092/128] Correct lookup_table's view of hashvec indices after sort. --- src/c++/hashtable.cpp | 7 +++++++ src/c++/profiler.cpp | 10 +++++----- src/c++/profiler.h | 6 +++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index c906f493..2c004e30 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -166,6 +166,13 @@ void HashTable::write() // hashes to look up other information directly from the hashtable. std::sort(begin(hashvec_), end(hashvec_), [](auto a, auto b) { return a.self_walltime_ > b.self_walltime_;}); + + // Need to re-store the indices in the lookup table, since they will have all + // moved around as a result of the above sort. + for (auto it = begin(hashvec_); it != end(hashvec_); ++it) { + auto current_index = it - hashvec_.begin(); + lookup_table_[it->region_hash_] = static_cast(current_index); + } // Data entries for (auto& record : hashvec_) { diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 521df7e6..3fb3c0f5 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -30,10 +30,10 @@ Profiler::StartCalliperValues::StartCalliperValues() {} Profiler::StartCalliperValues::StartCalliperValues( - record_index_t my_iterator, + record_index_t record_index, time_point_t region_start_time, time_point_t calliper_start_time) - : my_iterator_ (my_iterator) + : record_index_ (record_index) , region_start_time_ (region_start_time) , calliper_start_time_(calliper_start_time) {} @@ -126,7 +126,7 @@ size_t Profiler::start2(std::string_view region_name) // Store the calliper and region start times. auto region_start_time = prof_gettime(); StartCalliperValues new_times = StartCalliperValues( - record_index, region_start_time, logged_calliper_start_time); + record_index, region_start_time, logged_calliper_start_time); ++call_depth; auto call_depth_it = static_cast(call_depth); @@ -181,7 +181,7 @@ void Profiler::stop(size_t const hash) auto region_duration = region_stop_time - start_calliper_times.region_start_time_; // Do the hashtable update for the child region. - thread_hashtables_[tid].update(start_calliper_times.my_iterator_, region_duration); + thread_hashtables_[tid].update(start_calliper_times.record_index_, region_duration); // Precompute times as far as possible. We just need the calliper stop time // later. @@ -197,7 +197,7 @@ void Profiler::stop(size_t const hash) // Acquire parent pointers if (call_depth > 0){ auto parent_depth = static_cast(call_depth-1); - record_index_t parent_index = thread_traceback_[tid].at(parent_depth).second.my_iterator_; + record_index_t parent_index = thread_traceback_[tid].at(parent_depth).second.record_index_; parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( parent_index, region_duration); } diff --git a/src/c++/profiler.h b/src/c++/profiler.h index a68aa2d2..1aafb04c 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -51,9 +51,9 @@ class Profiler StartCalliperValues(record_index_t, time_point_t, time_point_t); // Data members - record_index_t my_iterator_; - time_point_t region_start_time_; - time_point_t calliper_start_time_; + record_index_t record_index_; + time_point_t region_start_time_; + time_point_t calliper_start_time_; }; // Data members From da863ea89f748d961ae03ffecf801a24b1f8a7a2 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 2 Dec 2022 09:54:41 +0000 Subject: [PATCH 093/128] Separate sort method; TracebackEntry; other tweaks. --- src/c++/hashtable.cpp | 34 +++++++++++++++++++--------------- src/c++/hashtable.h | 3 ++- src/c++/profiler.cpp | 43 +++++++++++++++++++++---------------------- src/c++/profiler.h | 17 +++++++++-------- 4 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 2c004e30..9a87e867 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -57,7 +57,7 @@ HashTable::HashTable(int const tid) * */ -void HashTable::query_insert(std::string_view region_name, +void HashTable::query_insert(std::string_view const region_name, size_t& hash, record_index_t& record_index) noexcept { @@ -129,6 +129,23 @@ time_duration_t& HashTable::increment_profiler_calls() * @param [in] time_delta The time spent in the child region. */ + +void HashTable::sort_records() +{ + + // Sort the entries according to self walltime. + std::sort(begin(hashvec_), end(hashvec_), + [](auto a, auto b) { return a.self_walltime_ > b.self_walltime_;}); + + // Need to re-store the indices in the lookup table, since they will have all + // moved around as a result of the above sort. + for (auto it = begin(hashvec_); it != end(hashvec_); ++it) { + auto current_index = it - hashvec_.begin(); + lookup_table_[it->region_hash_] = static_cast(current_index); + } + +} + /** * @brief Writes all entries in the hashtable, sorted according to self times. * @@ -139,6 +156,7 @@ void HashTable::write() // Ensure all computed times are up-to-date. prepare_computed_times_all(); + sort_records(); std::string routine_at_thread = "Thread: " + std::to_string(tid_); @@ -160,20 +178,6 @@ void HashTable::write() << std::setw(10) << "-" << "\n"; std::cout << std::setfill(' '); - // Create a vector from the hashtable and sort the entries according to self - // walltime. If optimisation of this is needed, it ought to be possible to - // acquire a vector of hash-selftime pairs in the correct order, then use the - // hashes to look up other information directly from the hashtable. - std::sort(begin(hashvec_), end(hashvec_), - [](auto a, auto b) { return a.self_walltime_ > b.self_walltime_;}); - - // Need to re-store the indices in the lookup table, since they will have all - // moved around as a result of the above sort. - for (auto it = begin(hashvec_); it != end(hashvec_); ++it) { - auto current_index = it - hashvec_.begin(); - lookup_table_[it->region_hash_] = static_cast(current_index); - } - // Data entries for (auto& record : hashvec_) { std::cout diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 0e6e1200..7d0afe71 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -94,6 +94,7 @@ class HashTable{ // Private member functions void prepare_computed_times(RegionRecord&); void prepare_computed_times_all(); + void sort_records(); RegionRecord& hash2record(size_t const); RegionRecord const& hash2record_const(size_t const) const; @@ -104,7 +105,7 @@ class HashTable{ HashTable(int); // Prototypes - void query_insert(std::string_view, size_t&, record_index_t&) noexcept; + void query_insert(std::string_view const, size_t&, record_index_t&) noexcept; void update(record_index_t const, time_duration_t const); void write(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 3fb3c0f5..0e8e74c6 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -22,18 +22,20 @@ extern int call_depth; int call_depth = -1; /** - * @brief Constructor for StartCalliperValues struct. + * @brief Constructor for TracebackEntry struct. * */ -Profiler::StartCalliperValues::StartCalliperValues() +Profiler::TracebackEntry::TracebackEntry() {} -Profiler::StartCalliperValues::StartCalliperValues( +Profiler::TracebackEntry::TracebackEntry( + size_t record_hash, record_index_t record_index, time_point_t region_start_time, time_point_t calliper_start_time) - : record_index_ (record_index) + : record_hash_ (record_hash) + , record_index_ (record_index) , region_start_time_ (region_start_time) , calliper_start_time_(calliper_start_time) {} @@ -61,7 +63,7 @@ Profiler::Profiler() thread_hashtables_.push_back(new_table); // Create a new list - std::array, PROF_MAX_TRACEBACK_SIZE> new_list; + std::array new_list; thread_traceback_.push_back(new_list); } @@ -124,13 +126,11 @@ size_t Profiler::start2(std::string_view region_name) thread_hashtables_[tid].query_insert(region_name, hash, record_index); // Store the calliper and region start times. - auto region_start_time = prof_gettime(); - StartCalliperValues new_times = StartCalliperValues( - record_index, region_start_time, logged_calliper_start_time); - ++call_depth; - auto call_depth_it = static_cast(call_depth); - thread_traceback_[tid].at(call_depth_it) = std::make_pair(hash, std::move(new_times)); + auto call_depth_index = static_cast(call_depth); + auto region_start_time = prof_gettime(); + thread_traceback_[tid].at(call_depth_index) + = TracebackEntry(hash, record_index, region_start_time, logged_calliper_start_time); return hash; } @@ -158,8 +158,6 @@ void Profiler::stop(size_t const hash) tid = static_cast(omp_get_thread_num()); #endif - auto call_depth_it = static_cast(call_depth); - // Check that we have called a start calliper before the stop calliper. // If not, then the call depth would be -1. if (call_depth < 0) { @@ -167,28 +165,29 @@ void Profiler::stop(size_t const hash) exit (101); } + // Get reference to the traceback entry. + auto call_depth_index = static_cast(call_depth); + auto& traceback_entry = thread_traceback_[tid].at(call_depth_index); + // Check: which hash is last on the traceback list? - size_t last_hash_on_list = thread_traceback_[tid].at(call_depth_it).first; + size_t last_hash_on_list = traceback_entry.record_hash_; if (hash != last_hash_on_list){ std::cerr << "EMERGENCY STOP: hashes don't match." << "\n"; exit (100); } - // Get start calliper values needed for subsequent computation. - auto& start_calliper_times = thread_traceback_[tid].at(call_depth_it).second; - // Compute the region time - auto region_duration = region_stop_time - start_calliper_times.region_start_time_; + auto region_duration = region_stop_time - traceback_entry.region_start_time_; // Do the hashtable update for the child region. - thread_hashtables_[tid].update(start_calliper_times.record_index_, region_duration); + thread_hashtables_[tid].update(traceback_entry.record_index_, region_duration); // Precompute times as far as possible. We just need the calliper stop time // later. // (t4-t1) = calliper time + region duration // (t3-t2) = region_duration // calliper_time = (t4-t1) - (t3-t2) = t4 - ( t3-t2 + t1) - auto temp_sum = start_calliper_times.calliper_start_time_ + region_duration; + auto temp_sum = traceback_entry.calliper_start_time_ + region_duration; // The sequence of code that follows is aimed at leaving only minimal and // simple operations after the call to prof_gettime(). @@ -196,8 +195,8 @@ void Profiler::stop(size_t const hash) // Acquire parent pointers if (call_depth > 0){ - auto parent_depth = static_cast(call_depth-1); - record_index_t parent_index = thread_traceback_[tid].at(parent_depth).second.record_index_; + auto parent_depth = static_cast(call_depth-1); + record_index_t parent_index = thread_traceback_[tid].at(parent_depth).record_index_; parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( parent_index, region_duration); } diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 1aafb04c..8d406878 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -42,15 +42,16 @@ class Profiler * the stop calliper. */ - struct StartCalliperValues + struct TracebackEntry { public: // Constructors - StartCalliperValues(); - StartCalliperValues(record_index_t, time_point_t, time_point_t); + TracebackEntry(); + TracebackEntry(size_t, record_index_t, time_point_t, time_point_t); // Data members + size_t record_hash_; record_index_t record_index_; time_point_t region_start_time_; time_point_t calliper_start_time_; @@ -59,13 +60,13 @@ class Profiler // Data members int max_threads_; - std::vector thread_hashtables_; - std::vector,PROF_MAX_TRACEBACK_SIZE>> thread_traceback_; + std::vector thread_hashtables_; + std::vector> thread_traceback_; // Type definitions for vector array indexing. - typedef std::vector::size_type hashtable_iterator_t_; - typedef std::vector,PROF_MAX_TRACEBACK_SIZE>> - ::size_type pair_iterator_t_; + typedef std::vector::size_type hashtable_iterator_t_; + typedef std::vector> + ::size_type traceback_index_t; public: From f0be730ca904e60d43bffd27d37b554dda95cf3b Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:21:20 +0000 Subject: [PATCH 094/128] Modified comment blocks, plus other tweaks. --- src/c++/hashtable.cpp | 100 +++++++++++++++++++----------------------- src/c++/hashtable.h | 12 ++++- src/c++/profiler.cpp | 21 +++++++-- src/c++/profiler.h | 4 +- 4 files changed, 75 insertions(+), 62 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 9a87e867..28a5bd12 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -11,12 +11,16 @@ #include #include +#define PROF_HASHVEC_RESERVE_SIZE 1000 + /** - * @brief Constructs a new entry in the hash table. + * @brief Constructs a new region record. + * @param [in] region_hash_ Hash of the region name. + * @param [in] region_name_ The region name. * */ -RegionRecord::RegionRecord(size_t const region_hash, std::string_view region_name) +RegionRecord::RegionRecord(size_t const region_hash, std::string_view const region_name) : region_hash_(region_hash) , region_name_(region_name) , total_walltime_ (time_duration_t::zero()) @@ -36,24 +40,18 @@ RegionRecord::RegionRecord(size_t const region_hash, std::string_view region_nam HashTable::HashTable(int const tid) : tid_(tid) { - // Reserve enough places in hashvec_ - hashvec_.reserve(1000); - - // Set the name and hash of the profiler entry. - std::string const profiler_name = "__profiler__"; - profiler_hash_ = hash_function_(profiler_name); + hashvec_.reserve(PROF_HASHVEC_RESERVE_SIZE); // Insert special entry for the profiler overhead time. - assert (lookup_table_.count(profiler_hash_) == 0); - hashvec_.emplace_back(profiler_hash_, profiler_name); - profiler_index_ = hashvec_.size()-1; - lookup_table_.emplace(profiler_hash_, profiler_index_); - assert (lookup_table_.count(profiler_hash_) > 0); + query_insert("__profiler__", profiler_hash_, profiler_index_); } /** * @brief Inserts a new entry into the hashtable. + * @param [in] region_name The name of the region. + * @param [out] hash Hash of the region name. + * @param [out] record_index Array index of the region record. * */ @@ -75,7 +73,6 @@ void HashTable::query_insert(std::string_view const region_name, lookup_table_.emplace(hash, record_index); assert (lookup_table_.count(hash) > 0); } - } /** @@ -98,12 +95,14 @@ void HashTable::update(record_index_t const record_index, time_duration_t const } /** - * @brief - * + * @brief Add child region and overhead times to parent. + * @param [in] record_index The index corresponding to the region record. + * @param [in] time_delta The time spent in the child region. + * @returns Pointer to the overhead time for this region. */ time_duration_t* HashTable::add_child_time( - record_index_t const record_index, + record_index_t const record_index, time_duration_t const child_walltime) { auto& record = hashvec_[record_index]; @@ -112,8 +111,8 @@ time_duration_t* HashTable::add_child_time( } /** - * @brief - * + * @brief Add child region and overhead times to parent. + * @returns Reference to the total profiling overhead time. */ time_duration_t& HashTable::increment_profiler_calls() @@ -124,18 +123,16 @@ time_duration_t& HashTable::increment_profiler_calls() } /** - * @brief Add child region and overhead times to parent. - * @param [in] hash The hash of the child region to update. - * @param [in] time_delta The time spent in the child region. + * @brief Sorts entries in the vector of region records according to self time + * and updates the hashtable with the new indices. */ - void HashTable::sort_records() { - // Sort the entries according to self walltime. - std::sort(begin(hashvec_), end(hashvec_), - [](auto a, auto b) { return a.self_walltime_ > b.self_walltime_;}); + // Sort the entries according to self walltime. + std::sort(begin(hashvec_), end(hashvec_), + [](auto a, auto b) { return a.self_walltime_ > b.self_walltime_;}); // Need to re-store the indices in the lookup table, since they will have all // moved around as a result of the above sort. @@ -147,7 +144,8 @@ void HashTable::sort_records() } /** - * @brief Writes all entries in the hashtable, sorted according to self times. + * @brief Writes all entries in the hashtable. + * @note Calls the method to sort times according to their region self-time. * */ @@ -195,7 +193,7 @@ void HashTable::write() * @detail Times computed are: the region self time and the total time minus * directly incurred profiling overhead costs. * - * @param [in] hash The hash of the region to compute. + * @param [in] record The region record to compute. */ void HashTable::prepare_computed_times(RegionRecord& record) @@ -213,36 +211,18 @@ void HashTable::prepare_computed_times(RegionRecord& record) /** * @brief Evaluates times derived from other times measured, looping over all - * code regions; includes updating the special profiling overhead entry. + * code regions. */ - void HashTable::prepare_computed_times_all() - { - - auto total_overhead_time = time_duration_t::zero(); - - // Loop over entries in the hashtable. This would include the special - // profiler entry, but the RegionRecord constructor will have ensured that all - // corresponding values are zero thus far. - for (auto& [hash, index] : lookup_table_) { - prepare_computed_times(hash2record(hash)); - } - - /// // Check that the special profiler hash entries are all zero, even after the - /// // above loop. - /// assert(lookup_table_.at(profiler_hash_).self_walltime_ == time_duration_t::zero()); - /// assert(lookup_table_.at(profiler_hash_).child_walltime_ == time_duration_t::zero()); - /// assert(lookup_table_.at(profiler_hash_).total_walltime_ == time_duration_t::zero()); - /// assert(lookup_table_.at(profiler_hash_).total_raw_walltime_ == time_duration_t::zero()); +void HashTable::prepare_computed_times_all() +{ - // Set values for the profiler entry specifically in the hashtable. - // lookup_table_.at(profiler_hash_).self_walltime_ = total_overhead_time_; - // lookup_table_.at(profiler_hash_).child_walltime_ = time_duration_t::zero(); - // lookup_table_.at(profiler_hash_).total_walltime_ = total_overhead_time_; - // lookup_table_.at(profiler_hash_).total_raw_walltime_ = total_overhead_time_; + auto total_overhead_time = time_duration_t::zero(); - // std::cout << "My thread ID: " << omp_get_thread_num() << std::endl; - // std::cout << "Table thread ID: " << tid_ << std::endl; + // Loop over entries in the hashtable. + for (auto& [hash, index] : lookup_table_) { + prepare_computed_times(hash2record(hash)); + } } @@ -369,7 +349,9 @@ unsigned long long int HashTable::get_prof_call_count() const } /** - * @ brief + * @brief Gets a reference to a region record for a given hash. + * @param [in] hash The region + * @returns Region record reference. * */ @@ -382,6 +364,14 @@ RegionRecord& HashTable::hash2record(size_t const hash) return hashvec_[lookup_table_.at(hash)]; } +/** + * @brief Gets a const reference to a region record for a given hash. Can be + * called from const methods. + * @param [in] hash The region + * @returns Region record reference. + * + */ + RegionRecord const& HashTable::hash2record_const(size_t const hash) const { // Assertions diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 7d0afe71..4ccb6187 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -31,6 +31,13 @@ #include "prof_gettime.h" +/** + * @brief Defines a null hash function. + * + * Having already hashed region names, we won't need to hash the hashtable keys. + * + */ + struct NullHashFunction { std::size_t operator()(std::size_t const& key) const { return key; @@ -49,7 +56,7 @@ struct RegionRecord{ // Constructor RegionRecord() = delete; - explicit RegionRecord(size_t const, std::string_view); + explicit RegionRecord(size_t const, std::string_view const); // Data members size_t region_hash_; @@ -63,7 +70,8 @@ struct RegionRecord{ }; -typedef std::vector::size_type record_index_t; +// Type definitions +using record_index_t = std::vector::size_type; /** * @brief Wraps STL hashtables with additional functionality. diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 0e8e74c6..a25c0391 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -11,7 +11,11 @@ #include #include -// Work around a GNU compiler bug. +// The following two code blocks contain a workaround for a GNU compiler bug. +// Formally, the `threadprivate` pragma ought to be *after* the variable +// declaration. GNU does not allow this at present. The `extern` is merely a +// portable way of getting around this. + extern time_point_t logged_calliper_start_time; #pragma omp threadprivate(logged_calliper_start_time) time_point_t logged_calliper_start_time; @@ -29,6 +33,17 @@ int call_depth = -1; Profiler::TracebackEntry::TracebackEntry() {} +/** + * @brief Constructor for TracebackEntry struct. + * @param [in] record_hash The hash of the region name. + * @param [in] record_index The index of the region record. + * @param [in] region_start_time The clock measurement just before leaving the + * start calliper. + * @param [in] calliper_start_time The clock measurement on entry to the start + * calliper. + * + */ + Profiler::TracebackEntry::TracebackEntry( size_t record_hash, record_index_t record_index, @@ -81,7 +96,7 @@ Profiler::Profiler() * @todo Revisit profiling overhead measurement. (#64) */ -size_t Profiler::start(std::string_view region_name) +size_t Profiler::start(std::string_view const region_name) { start1(); auto hash = start2(region_name); @@ -108,7 +123,7 @@ void Profiler::start1() * @todo Revisit profiling overhead measurement. (#64) */ -size_t Profiler::start2(std::string_view region_name) +size_t Profiler::start2(std::string_view const region_name) { // Determine the thread number diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 8d406878..b53565f2 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -74,9 +74,9 @@ class Profiler Profiler(); // Member functions - size_t start(std::string_view); + size_t start(std::string_view const); void start1(); - size_t start2(std::string_view); + size_t start2(std::string_view const); void stop (size_t const); void write(); From 16b2955aa0deb573dbcf82570b31fa130c9659f7 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Mon, 5 Dec 2022 11:22:43 +0000 Subject: [PATCH 095/128] Remove unused variable. --- src/c++/hashtable.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 28a5bd12..ff30b000 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -217,8 +217,6 @@ void HashTable::prepare_computed_times(RegionRecord& record) void HashTable::prepare_computed_times_all() { - auto total_overhead_time = time_duration_t::zero(); - // Loop over entries in the hashtable. for (auto& [hash, index] : lookup_table_) { prepare_computed_times(hash2record(hash)); From d24c614230441522ccffc5e482142c001ea18e88 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Mon, 5 Dec 2022 15:33:15 +0000 Subject: [PATCH 096/128] Don't include asserts that might trap application-level coding faults. --- src/c++/hashtable.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index ff30b000..94104744 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -343,6 +343,7 @@ unsigned long long int HashTable::get_call_count(size_t const hash) const unsigned long long int HashTable::get_prof_call_count() const { auto& record = hash2record_const(profiler_hash_); + assert (lookup_table_.count(profiler_hash_) > 0); return record.call_count_; } @@ -355,10 +356,6 @@ unsigned long long int HashTable::get_prof_call_count() const RegionRecord& HashTable::hash2record(size_t const hash) { - // Assertions - assert (lookup_table_.size() > 0); - assert (lookup_table_.count(hash) > 0); - return hashvec_[lookup_table_.at(hash)]; } @@ -372,10 +369,6 @@ RegionRecord& HashTable::hash2record(size_t const hash) RegionRecord const& HashTable::hash2record_const(size_t const hash) const { - // Assertions - assert (lookup_table_.size() > 0); - assert (lookup_table_.count(hash) > 0); - return hashvec_[lookup_table_.at(hash)]; } From e8919a907c72a43ed1bc87f5fe0ccd5796dca00f Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 5 Jan 2023 13:56:18 +0000 Subject: [PATCH 097/128] Add test for case where traceback array exhausted. --- src/c++/profiler.cpp | 15 ++++++++++----- tests/unit_tests/c++/test_proftests.cpp | 21 +++++++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index a25c0391..a8138573 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -142,11 +142,16 @@ size_t Profiler::start2(std::string_view const region_name) // Store the calliper and region start times. ++call_depth; - auto call_depth_index = static_cast(call_depth); - auto region_start_time = prof_gettime(); - thread_traceback_[tid].at(call_depth_index) - = TracebackEntry(hash, record_index, region_start_time, logged_calliper_start_time); - + if (call_depth < PROF_MAX_TRACEBACK_SIZE){ + auto call_depth_index = static_cast(call_depth); + auto region_start_time = prof_gettime(); + thread_traceback_[tid].at(call_depth_index) + = TracebackEntry(hash, record_index, region_start_time, logged_calliper_start_time); + } + else { + std::cerr << "EMERGENCY STOP: Traceback array exhausted." << "\n"; + exit (102); + } return hash; } diff --git a/tests/unit_tests/c++/test_proftests.cpp b/tests/unit_tests/c++/test_proftests.cpp index 72a40a17..408d2c09 100644 --- a/tests/unit_tests/c++/test_proftests.cpp +++ b/tests/unit_tests/c++/test_proftests.cpp @@ -16,13 +16,12 @@ using ::testing::ExitedWithCode; using ::testing::KilledBySignal; + // -// Tests and death tests more related to profiler class members. WriteTest -// checks 'hashvec' in profiler.write() is correctly ordered. One death test -// makes sure the code breaks when a hash mismatch happens, and the other tests -// for a segfault when stopping before anything else. +// Tests and death tests related to profiler class members. // +// Make sure the code exits when a hash mismatch happens. TEST(ProfilerDeathTest,WrongHashTest) { EXPECT_EXIT({ @@ -44,6 +43,7 @@ TEST(ProfilerDeathTest,WrongHashTest) { } +// Tests for a segfault when stopping before anything else. TEST(ProfilerDeathTest,StopBeforeStartTest) { EXPECT_DEATH({ @@ -56,3 +56,16 @@ TEST(ProfilerDeathTest,StopBeforeStartTest) { }, "" ); } + +// The traceback array is not a growable vector. Check that the code exits +// when available array elements are exhaused. +TEST(ProfilerDeathTest, TooManyTracebackEntries) { + + EXPECT_EXIT({ + const int beyond_maximum = PROF_MAX_TRACEBACK_SIZE+1; + for (int i=0; i Date: Thu, 5 Jan 2023 14:04:34 +0000 Subject: [PATCH 098/128] Add [[maybe_unused]] in traceback entry exhaustion test. --- tests/unit_tests/c++/test_proftests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/c++/test_proftests.cpp b/tests/unit_tests/c++/test_proftests.cpp index 408d2c09..79053fa6 100644 --- a/tests/unit_tests/c++/test_proftests.cpp +++ b/tests/unit_tests/c++/test_proftests.cpp @@ -64,7 +64,7 @@ TEST(ProfilerDeathTest, TooManyTracebackEntries) { EXPECT_EXIT({ const int beyond_maximum = PROF_MAX_TRACEBACK_SIZE+1; for (int i=0; i Date: Fri, 6 Jan 2023 11:09:18 +0000 Subject: [PATCH 099/128] Tidy up around start parts and namespacing threadprivate storage. --- src/c++/profiler.cpp | 49 +++++++++++++++++++++++------------------- src/c++/profiler.h | 4 ++-- src/c/profiler_c.cpp | 18 +++++++++------- src/f/profiler_mod.F90 | 15 +++++++------ 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index a8138573..34f1417c 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -11,19 +11,29 @@ #include #include -// The following two code blocks contain a workaround for a GNU compiler bug. -// Formally, the `threadprivate` pragma ought to be *after* the variable +// Define threadprivate variables. +// `extern` keywords in the code below represent a workaround for a GNU compiler +// bug. Formally, the `threadprivate` pragma ought to be *after* the variable // declaration. GNU does not allow this at present. The `extern` is merely a -// portable way of getting around this. - -extern time_point_t logged_calliper_start_time; +// portable way of getting around this, with the unwanted side effect of +// introducing external linkage. The anonymous namespace removes that. +namespace{ + // `logged_calliper_start_time` is needed for the Fortran interface. + // Noting that: + // (i) the start region procedure must be separated into two parts, and + // (ii) the time point is an instance of a C++ class, + // we avoid passing time objects into other interface layers by declaring + // storage here. + extern time_point_t logged_calliper_start_time; #pragma omp threadprivate(logged_calliper_start_time) -time_point_t logged_calliper_start_time; + time_point_t logged_calliper_start_time; -// The call depth must be initialised on all threads, so do it here. -extern int call_depth; + // The call depth must be stored separately for all threads. Important to + // initialise it here, so that it's initialised correctly on all threads. + extern int call_depth; #pragma omp threadprivate(call_depth) -int call_depth = -1; + int call_depth = -1; +} /** * @brief Constructor for TracebackEntry struct. @@ -90,40 +100,36 @@ Profiler::Profiler() /** * @brief Start timing a profiled code region. - * @detail Calls two other start routines. + * @detail Calls both part1 and part2 start routines in succession. * @param [in] region_name The code region name. * @returns Unique hash for the code region being started. - * @todo Revisit profiling overhead measurement. (#64) */ size_t Profiler::start(std::string_view const region_name) { - start1(); - auto hash = start2(region_name); + start_part1(); + auto hash = start_part2(region_name); return hash; } /** - * @brief Start timing a profiled code region, part 1: make a + * @brief Start timing a profiled code region, part 1 of 2: make a * threadprivate note of the time. - * @param [in] region_name The code region name. - * @returns Unique hash for the code region being started. - * @todo Revisit profiling overhead measurement. (#64) */ -void Profiler::start1() +void Profiler::start_part1() { + // Store the calliper start time, which is used in part2. logged_calliper_start_time = prof_gettime(); } /** - * @brief Start timing a profiled code region, part 2. + * @brief Start timing a profiled code region, part 2 of 2. * @param [in] region_name The code region name. * @returns Unique hash for the code region being started. - * @todo Revisit profiling overhead measurement. (#64) */ -size_t Profiler::start2(std::string_view const region_name) +size_t Profiler::start_part2(std::string_view const region_name) { // Determine the thread number @@ -163,7 +169,6 @@ size_t Profiler::start2(std::string_view const region_name) * calliper, and subtracting the measured region time. Hence larger * absolute times are being measured, which are less likely to suffer * fractional error from precision limitations of the clock. - * @todo Revisit profiling overhead measurement. (#64) */ void Profiler::stop(size_t const hash) diff --git a/src/c++/profiler.h b/src/c++/profiler.h index b53565f2..10ebd96c 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -75,8 +75,8 @@ class Profiler // Member functions size_t start(std::string_view const); - void start1(); - size_t start2(std::string_view const); + void start_part1(); + size_t start_part2(std::string_view const); void stop (size_t const); void write(); diff --git a/src/c/profiler_c.cpp b/src/c/profiler_c.cpp index 269f3171..be626c9e 100644 --- a/src/c/profiler_c.cpp +++ b/src/c/profiler_c.cpp @@ -21,29 +21,31 @@ #include extern "C" { - void c_profiler_start1(); - void c_profiler_start2(long int&, char const*); + void c_profiler_start_part1(); + void c_profiler_start_part2(long int&, char const*); void c_profiler_stop (long int const&); void c_profiler_write(); double c_get_total_walltime(long int const&, int const&); } /** - * @brief Start timing a named region and return a unique handle. + * @brief Start timing, part 1 of 2. */ -void c_profiler_start1() +void c_profiler_start_part1() { - prof.start1(); + prof.start_part1(); } /** - * @brief Start timing a named region and return a unique handle. + * @brief Start timing, part 2 of 2. a named region and return a unique handle. + * @param [in] name The region name, null terminated. + * @param [out] hash_out The returned unique hash for this region. */ -void c_profiler_start2(long int& hash_out, char const* name) +void c_profiler_start_part2(long int& hash_out, char const* name) { - size_t hash = prof.start2( name ); + size_t hash = prof.start_part2( name ); // Ensure that the source and destination have the same size. static_assert(sizeof(hash) == sizeof(hash_out), "Hash/Out size mismatch."); diff --git a/src/f/profiler_mod.F90 b/src/f/profiler_mod.F90 index 242898d4..de0f7687 100644 --- a/src/f/profiler_mod.F90 +++ b/src/f/profiler_mod.F90 @@ -37,16 +37,17 @@ module profiler_mod interface - subroutine interface_profiler_start1() bind(C, name='c_profiler_start1') + subroutine interface_profiler_start_part1() & + bind(C, name='c_profiler_start_part1') !No arguments to handle - end subroutine interface_profiler_start1 + end subroutine interface_profiler_start_part1 - subroutine interface_profiler_start2(hash_out, region_name) & - bind(C, name='c_profiler_start2') + subroutine interface_profiler_start_part2(hash_out, region_name) & + bind(C, name='c_profiler_start_part2') import :: c_char, pik character(kind=c_char, len=1), intent(in) :: region_name(*) integer(kind=pik), intent(out) :: hash_out - end subroutine interface_profiler_start2 + end subroutine interface_profiler_start_part2 subroutine profiler_stop(hash_in) bind(C, name='c_profiler_stop') import :: pik @@ -87,11 +88,11 @@ subroutine profiler_start(hash_out, region_name) !Local variables character(len=len_trim(region_name)+1) :: local_region_name - call interface_profiler_start1() + call interface_profiler_start_part1() call append_null_char(region_name, local_region_name, len_trim(region_name)) - call interface_profiler_start2(hash_out, local_region_name) + call interface_profiler_start_part2(hash_out, local_region_name) end subroutine profiler_start From cc44fd7356e7550f8d20e18ba750e79d29bfdd5e Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 20 Jan 2023 11:58:46 +0000 Subject: [PATCH 100/128] Minor correction to commenting. --- src/c++/hashtable.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 94104744..82346626 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -61,11 +61,13 @@ void HashTable::query_insert(std::string_view const region_name, { hash = hash_function_(region_name); - // Found entry, + // Does the entry exist already? if (auto search = lookup_table_.find(hash); search != lookup_table_.end()) { record_index = search->second; } + + // If not, create new entry. else { hashvec_.emplace_back(hash, region_name); From 269bf2c08c5b948bdff308df2f3e47af4232fc37 Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Tue, 7 Feb 2023 09:19:56 +0000 Subject: [PATCH 101/128] Merging suggested changes from MG branch (#72) --- src/c++/CMakeLists.txt | 13 +- src/c++/formatter.cpp | 281 +++++++++++++----------- src/c++/formatter.h | 44 ++-- src/c++/hashtable.cpp | 30 +-- src/c++/hashtable.h | 29 +-- src/c++/hashvec.cpp | 97 -------- src/c++/hashvec.h | 75 +++---- src/c++/hashvec_handler.cpp | 73 ++++++ src/c++/hashvec_handler.h | 54 +++++ src/c++/profiler.cpp | 36 +-- src/c++/writer.cpp | 82 ++----- src/c++/writer.h | 43 ++-- src/f/CMakeLists.txt | 5 +- tests/unit_tests/f/test_profiler_mod.pf | 11 + 14 files changed, 432 insertions(+), 441 deletions(-) delete mode 100644 src/c++/hashvec.cpp create mode 100644 src/c++/hashvec_handler.cpp create mode 100644 src/c++/hashvec_handler.h diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index 75f2ba7e..54ce3e99 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -12,12 +12,13 @@ include(CMakePackageConfigHelpers) # Add files source files to library. add_library(${CMAKE_PROJECT_NAME} SHARED - profiler.h profiler.cpp - prof_gettime.h prof_gettime.cpp - hashvec.h hashvec.cpp - writer.h writer.cpp - formatter.h formatter.cpp - hashtable.h hashtable.cpp + profiler.h profiler.cpp + prof_gettime.h prof_gettime.cpp + hashvec_handler.h hashvec_handler.cpp + writer.h writer.cpp + formatter.h formatter.cpp + hashtable.h hashtable.cpp + hashvec.h ) # Link library to and external libs (also use project warnings and options). diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index b6307261..70418539 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -1,9 +1,9 @@ /* ----------------------------------------------------------------------------- - * (c) Crown copyright 2021 Met Office. All rights reserved. - * The file LICENCE, distributed with this code, contains details of the terms - * under which the code may be used. - * ----------------------------------------------------------------------------- - */ +* (c) Crown copyright 2021 Met Office. All rights reserved. +* The file LICENCE, distributed with this code, contains details of the terms +* under which the code may be used. +* ----------------------------------------------------------------------------- +*/ #include "formatter.h" #include @@ -11,66 +11,80 @@ /** * @brief Formatter constructor - * - * @param[in] format An input callable function that will replace format_ - * + * @note Sets the output format based on the value of environment variable + * PROF_OUTPUT_FORMAT. */ -Formatter::Formatter(std::function>)> format) - : format_(std::move(format)) - {} +Formatter::Formatter() +{ + + std::string format = "drhook"; + + char const* env_format = std::getenv("PROF_OUTPUT_FORMAT"); + if(env_format){ format = env_format; } + + if ( format == "threads") + { + format_ = &Formatter::threads; + } + else if (format == "drhook") + { + format_ = &Formatter::drhook; + } + else throw std::runtime_error("Invalid format choice"); +} /** - * @brief Executes format_ + * @brief Executes the format_ method to write the data. * * @param[in] os Output stream that the format method will write to - * @param[in] hashvec Vector of pairs that the format method will operate on + * @param[in] hashvec Vector of data that the format method will operate on */ -void Formatter::executeFormat(std::ofstream& os, std::vector> hashvec) const +void Formatter::execute_format(std::ofstream& os, hashvec_t hashvec) { - format_(os, hashvec); + (this->*format_)(os, hashvec); } /** - * @brief Our standard output format + * @brief Per-thread timing output. * * @param[in] os Output stream to write to * @param[in] hashvec Vector containing all the necessary data */ -void Formats::standard(std::ofstream& os, std::vector> hashvec) +void Formatter::threads(std::ofstream& os, hashvec_t hashvec) { - std::string routine_at_thread = "Thread: All" /*+ std::to_string(tid_)*/; - - // Write headings - os << "\n"; - os - << std::setw(40) << std::left << routine_at_thread << " " - << std::setw(15) << std::right << "Self (s)" << " " - << std::setw(15) << std::right << "Total (raw) (s)" << " " - << std::setw(15) << std::right << "Total (s)" << " " - << std::setw(10) << std::right << "Calls" << "\n"; - - os << std::setfill('-'); - os - << std::setw(40) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(15) << "-" << " " - << std::setw(10) << "-" << "\n"; - os << std::setfill(' '); - - // Data entries - for (auto& [hash, entry] : hashvec) { - os - << std::setw(40) << std::left << entry.region_name_ << " " - << std::setw(15) << std::right << entry.self_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_raw_walltime_.count() << " " - << std::setw(15) << std::right << entry.total_walltime_.count() << " " - << std::setw(10) << std::right << entry.call_count_ << "\n"; - } + std::string routine_at_thread = "Thread: All" /*+ std::to_string(tid_)*/; + + // Write headings + os << "\n"; + os + << std::setw(40) << std::left << routine_at_thread << " " + << std::setw(15) << std::right << "Self (s)" << " " + << std::setw(15) << std::right << "Total (raw) (s)" << " " + << std::setw(15) << std::right << "Total (s)" << " " + << std::setw(10) << std::right << "Calls" << "\n"; + + os << std::setfill('-'); + os + << std::setw(40) << "-" << " " + << std::setw(15) << "-" << " " + << std::setw(15) << "-" << " " + << std::setw(15) << "-" << " " + << std::setw(10) << "-" << "\n"; + os << std::setfill(' '); + + // Data entries + for (auto& [hash, entry] : hashvec) { + os + << std::setw(40) << std::left << entry.region_name_ << " " + << std::setw(15) << std::right << entry.self_walltime_.count() << " " + << std::setw(15) << std::right << entry.total_raw_walltime_.count() << " " + << std::setw(15) << std::right << entry.total_walltime_.count() << " " + << std::setw(10) << std::right << entry.call_count_ << "\n"; + } } @@ -82,93 +96,94 @@ void Formats::standard(std::ofstream& os, std::vector> hashvec) +void Formatter::drhook(std::ofstream& os, hashvec_t hashvec) { - // Preliminary info - os << " " << "No. of instrumented routines called : 9\n"; - os << " " << "Instrumentation started : 20100521 171238\n"; - os << " " << "Instrumentation ended : 20100521 172033\n"; - os << " " << "Instrumentation overhead: 0.90%\n"; - os << " " << "Memory usage : Memory usage : 1346 MBytes (heap), 1315 MBytes (rss), 796 MBytes (stack), 1116 (paging)\n"; - os << " " << "Wall-time is 472.28 sec on proc#1 (192 procs, 1 threads)\n"; - os << " " << "Thread#1: 472.28 sec (100.00%)" << std::endl; - - // Table Headers - os << "\n"; - os - << " " - << std::setw(3) << std::left << "#" - << std::setw(7) << std::left << "% Time" - << std::setw(13) << std::right << "Cumul" - << std::setw(13) << std::right << "Self" - << std::setw(13) << std::right << "Total" - << std::setw(15) << std::right << "# of calls" - << std::setw(12) << std::right << "Self" - << std::setw(12) << std::right << "Total" << " " - << "Routine@" << "\n"; - os - << " " - << std::setw(73) << "" - << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; - - // Subheaders + + int num_threads = 1; +#ifdef _OPENMP + num_threads = omp_get_max_threads(); +#endif + + // Preliminary info + os << "Profiling on " << num_threads << " thread(s).\n"; + + // Table Headers + os << "\n"; + os + << " " + << std::setw(3) << std::left << "#" + << std::setw(7) << std::left << "% Time" + << std::setw(13) << std::right << "Cumul" + << std::setw(13) << std::right << "Self" + << std::setw(13) << std::right << "Total" + << std::setw(15) << std::right << "# of calls" + << std::setw(12) << std::right << "Self" + << std::setw(12) << std::right << "Total" << " " + << "Routine@" << "\n"; + os + << " " + << std::setw(73) << "" + << "(Size; Size/sec; Size/call; MinSize; MaxSize)" << "\n"; + + // Subheaders + os + << " " + << std::setw(3) << std::left << "" + << std::setw(7) << std::right << "(self)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(13) << std::right << "(sec)" + << std::setw(15) << std::right << "" + << std::setw(12) << std::right << "ms/call" + << std::setw(12) << std::right << "ms/call" + << "\n\n"; + + // Find the highest walltime in table_, which should be the total runtime of + // the program. This is used later when calculating '% Time'. + double top_walltime = std::max_element + ( + std::begin(hashvec), std::end(hashvec), + [] (auto a, auto b) { + return a.second.total_walltime_ < b.second.total_walltime_; + } + )->second.total_walltime_.count(); + + // Declare any variables external to HashEntry + int region_number = 0; + double percent_time; + time_duration_t cumul_walltime = time_duration_t::zero(); + double self_per_call; + double total_per_call; + + // + // Write data to file + // + + os << std::fixed << std::showpoint << std::setprecision(3); + + for (auto& [hash, entry] : hashvec) { + + // Calculate non-HashEntry data + region_number++; + percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); + cumul_walltime += entry.self_walltime_; + self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); + total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); + + // Write everything out os << " " - << std::setw(3) << std::left << "" - << std::setw(7) << std::right << "(self)" - << std::setw(13) << std::right << "(sec)" - << std::setw(13) << std::right << "(sec)" - << std::setw(13) << std::right << "(sec)" - << std::setw(15) << std::right << "" - << std::setw(12) << std::right << "ms/call" - << std::setw(12) << std::right << "ms/call" - << "\n\n"; - - // Find the highest walltime in table_, which should be the total runtime of - // the program. This is used later when calculating '% Time'. - double top_walltime = std::max_element - ( - std::begin(hashvec), std::end(hashvec), - [] (auto a, auto b) { - return a.second.total_walltime_ < b.second.total_walltime_; - } - )->second.total_walltime_.count(); - - // Declare any variables external to HashEntry - int region_number = 0; - double percent_time; - time_duration_t cumul_walltime = time_duration_t::zero(); - double self_per_call; - double total_per_call; - - // - // Write data to file - // - - os << std::fixed << std::showpoint << std::setprecision(3); - - for (auto& [hash, entry] : hashvec) { - - // Calculate non-HashEntry data - region_number++; - percent_time = 100.0 * ( entry.self_walltime_.count() / top_walltime ); - cumul_walltime += entry.self_walltime_; - self_per_call = 1000.0 * ( entry.self_walltime_.count() / static_cast(entry.call_count_) ); - total_per_call = 1000.0 * ( entry.total_walltime_.count() / static_cast(entry.call_count_) ); - - // Write everything out - os - << " " - << std::setw(3) << std::left << region_number - << std::setw(7) << std::right << percent_time - << std::setw(13) << std::right << cumul_walltime.count() - << std::setw(13) << std::right << entry.self_walltime_.count() - << std::setw(13) << std::right << entry.total_walltime_.count() - << std::setw(15) << std::right << entry.call_count_ - << std::setw(12) << std::right << self_per_call - << std::setw(12) << std::right << total_per_call << " " - << entry.region_name_ << "\n"; - - } - -} \ No newline at end of file + << std::setw(3) << std::left << region_number + << std::setw(7) << std::right << percent_time + << std::setw(13) << std::right << cumul_walltime.count() + << std::setw(13) << std::right << entry.self_walltime_.count() + << std::setw(13) << std::right << entry.total_walltime_.count() + << std::setw(15) << std::right << entry.call_count_ + << std::setw(12) << std::right << self_per_call + << std::setw(12) << std::right << total_per_call << " " + << entry.region_name_ << "\n"; + + } + +} + diff --git a/src/c++/formatter.h b/src/c++/formatter.h index 292d54de..da8ae992 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -7,8 +7,8 @@ /** * @file formatter.h - * @brief Contains base format class and all derived classes. - * + * @brief Formatter class, which contains methods for writing in different + * output formats.. * */ @@ -16,17 +16,19 @@ #define FORMATTER_H #include -#include #include -#include "hashtable.h" +#ifdef _OPENMP + #include +#endif +#include "hashvec.h" /** - * @brief Formatting class - * - * Any callable function can be passed to this class and ran via the execute - * method. + * @brief Formatter class. Methods write profile data. + * @note Different formats are coded in different class methods. A function + * pointer to the appropriate method is set on construction, according + * to the value of the environment variable PROF_OUTPUT_FORMAT. */ class Formatter { @@ -34,29 +36,21 @@ class Formatter { private: // Format method - const std::function>)> format_; + void (Formatter::*format_)(std::ofstream&, hashvec_t); + + // Individual formatter functions + void threads (std::ofstream& os, hashvec_t); + void drhook (std::ofstream& os, hashvec_t); public: // Constructor - explicit Formatter(std::function>)> format); + explicit Formatter(); - void executeFormat(std::ofstream& os, std::vector> hashvec) const; + // Execute the format method + void execute_format(std::ofstream& os, hashvec_t); }; -/** - * @brief Struct to store the formats of interest. - */ - -struct Formats { - - public: - - // Format options - static void standard(std::ofstream& os, std::vector> hashvec); - static void drhook(std::ofstream& os, std::vector> hashvec); - -}; +#endif -#endif \ No newline at end of file diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 0a71326a..b805c001 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -113,11 +113,10 @@ void HashTable::add_overhead_time(size_t const hash, time_duration_t calliper_ti } /** - * @brief Evaluates times derived from other times measured, for a particular - * code region. - * @detail Times computed are: the region self time and the total time minus - * directly incurred profiling overhead costs. - * @brief Computes self times from total times. + * @brief Evaluates times derived from other times measured, for a particular + * code region. + * @details Times computed are: the region self time and the total time minus + * directly incurred profiling overhead costs. * * @param [in] hash The hash of the region to compute. */ @@ -185,11 +184,11 @@ std::vector HashTable::list_keys() } /** - * @brief Appends table_ onto the end of an input HashVec + * @brief Appends table_ onto the end of an input hashvec. * */ -void HashTable::append_to(std::vector>& hashvec) +void HashTable::append_to(HashVecHandler& hashvec) { // Compute overhead and self times before appending prepare_computed_times_all(); @@ -198,8 +197,11 @@ void HashTable::append_to(std::vector>& hashvec) auto it = table_.find(profiler_hash_); if (it != table_.end() && it->second.call_count_ == 0) { table_.erase(it); } - // Insert local table into hashvec - hashvec.insert(hashvec.end(), table_.begin(), table_.end()); + // Create hashvec from the table data. + + // Append hashvec to argument. + hashvec_t new_hashvec (table_.cbegin(), table_.cend()); + hashvec.append(new_hashvec); } /** @@ -215,7 +217,7 @@ double HashTable::get_total_walltime(size_t const hash) const /** * @brief Get the total time of the specified region, minus profiling overheads * incurred by calling direct children. - * @param [in] The hash corresponding to the region. + * @param [in] hash The hash corresponding to the region. * @note This time is derived from other measured times, therefore a to * `prepare_computed_times` is need to update its value. */ @@ -229,7 +231,7 @@ double HashTable::get_total_raw_walltime(size_t const hash) /** * @brief Get the profiling overhead time for a specified region, as incurred * by calling direct children. - * @param [in] The hash corresponding to the region. + * @param [in] hash The hash corresponding to the region. */ double HashTable::get_overhead_walltime(size_t const hash) const @@ -239,7 +241,7 @@ double HashTable::get_overhead_walltime(size_t const hash) const /** * @brief Get the profiler self (exclusive) time corresponding to the input hash. - * @param [in] The hash corresponding to the region. + * @param [in] hash The hash corresponding to the region. * @note This time is derived from other measured times, therefore a to * `prepare_computed_times` is need to update its value. */ @@ -252,7 +254,7 @@ double HashTable::get_self_walltime(size_t const hash) /** * @brief Get the child time corresponding to the input hash. - * @param [in] The hash corresponding to the region. + * @param [in] hash The hash corresponding to the region. * @note This time is derived from other measured times, therefore a to * `prepare_computed_times` is need to update its value. */ @@ -264,7 +266,7 @@ double HashTable::get_child_walltime(size_t const hash) const /** * @brief Get the region name corresponding to the input hash. - * @param [in] The hash corresponding to the region. + * @param [in] hash The hash corresponding to the region. */ std::string HashTable::get_region_name(size_t const hash) const diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index ceb8335c..5ff5e6e7 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -28,33 +28,10 @@ #include #include +#include "hashvec.h" +#include "hashvec_handler.h" #include "prof_gettime.h" -/** - * @brief Structure to hold information for a particular routine. - * - * Bundles together any information pertinent to a specific profiled region. - * - */ - -struct HashEntry{ - public: - - // Constructor - HashEntry() = delete; - explicit HashEntry(std::string_view); - - // Data members - std::string region_name_; - time_duration_t total_walltime_; - time_duration_t total_raw_walltime_; - time_duration_t self_walltime_; - time_duration_t child_walltime_; - time_duration_t overhead_walltime_; - unsigned long long int call_count_; - -}; - /** * @brief Wraps STL hashtables with additional functionality. * @@ -92,7 +69,7 @@ class HashTable{ void add_child_time(size_t const, time_duration_t); void add_overhead_time(size_t const, time_duration_t); void compute_self_times(); - void append_to(std::vector>& hashvec); + void append_to(HashVecHandler&); // Getters double get_total_walltime(size_t const hash) const; diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp deleted file mode 100644 index 753aeac7..00000000 --- a/src/c++/hashvec.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* ----------------------------------------------------------------------------- - * (c) Crown copyright 2021 Met Office. All rights reserved. - * The file LICENCE, distributed with this code, contains details of the terms - * under which the code may be used. - * ----------------------------------------------------------------------------- - */ - -#include "hashvec.h" - -/** - * @brief HashVec constructor - * - * The format_ and iomode_ variables are set via std::getenv, which checks for - * the given environment variable. - * - */ - -HashVec::HashVec() - : format_(std::getenv("PROF_OUT_FORMAT")) - , iomode_(std::getenv("PROF_IO_MODE")) - {} - -/** - * @brief Creates a new unique Writer pointer for a particular format and - * "IO mode" - i.e. singular file output or multiple file output - * - * @return std::unique_ptr Unique Writer pointer that hashvec_ is - * passed to in order to write out data - */ - -const std::unique_ptr HashVec::createWriter() const -{ - std::string format = static_cast(format_); - std::string iomode = static_cast(iomode_); - - // Creates format ptr first - std::unique_ptr formatter; - if (format.empty() || format == "standard") - { - formatter = std::make_unique(Formats::standard); - } - else if (format == "drhook") - { - formatter = std::make_unique(Formats::drhook); - } - else throw std::runtime_error("Invalid format choice"); - - - // Uses format in creation of writer - if (iomode.empty() || iomode == "multi") - { - return std::make_unique(std::move(formatter)); - } - else throw std::runtime_error("Invalid io mode choice"); -} - -/** - * @brief hashvec_ getter - * - */ - -std::vector>& HashVec::get() -{ - return hashvec_; -} - -/** - * @brief Sorts entries in the hashvec from high to low self walltime - * - */ - -void HashVec::sort() -{ - std::sort - ( - begin(hashvec_), end(hashvec_), - [] (auto a, auto b) { - return a.second.self_walltime_ > b.second.self_walltime_; - } - ); -} - -/** - * @brief Makes the appropriate visitor pattern calls depending on what - * format_ and iomode_ are - * - * The HashVec's hashvec_ is passed down to whatever format is chosen via get() - * - */ - -void HashVec::write() -{ - std::ofstream os; - - auto writer = createWriter(); - writer->write(os, hashvec_); -} \ No newline at end of file diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 0178c516..61ade2cf 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -1,57 +1,48 @@ -/* ----------------------------------------------------------------------------- - * (c) Crown copyright 2021 Met Office. All rights reserved. - * The file LICENCE, distributed with this code, contains details of the terms - * under which the code may be used. - * ----------------------------------------------------------------------------- - */ +/*----------------------------------------------------------------------------*\ + (c) Crown copyright 2023 Met Office. All rights reserved. + The file LICENCE, distributed with this code, contains details of the terms + under which the code may be used. +\*----------------------------------------------------------------------------*/ /** - * @file hashvec.h - * @brief Contains the hashvec and decides on format/IO mode. - * - * Contains hashvec get and sort methods, aswell as a write method that - * utilises the Formatter and Writer classes. + * @file hashvec.h + * @brief Defines struct and datatype for timed region entries in the + * hash vector. */ -#ifndef HASHVEC_H -#define HASHVEC_H +#ifndef PROFILER_HASHVEC_H +#define PROFILER_HASHVEC_H -#include "writer.h" -#include +#include +#include + +#include "prof_gettime.h" /** - * @brief HashVec class - * - * The hashvec is a vector of pairs containing information from one or more - * hashtables, since data in unordered_map's cannot be sorted. This class wraps - * the hashvec container with additional functionality that lets it pick a - * format/iomode and create a pointer to the writer class. + * @brief Structure to hold information for a particular region. + * */ -class HashVec { +struct HashEntry{ + public: - private: + // Constructor + HashEntry() = delete; + explicit HashEntry(std::string_view); - // The hashvec - std::vector> hashvec_; + // Data members + std::string region_name_; + time_duration_t total_walltime_; + time_duration_t total_raw_walltime_; + time_duration_t self_walltime_; + time_duration_t child_walltime_; + time_duration_t overhead_walltime_; + unsigned long long int call_count_; - // Environment variables - const char* format_; - const char* iomode_; - - // Writer creation function - const std::unique_ptr createWriter() const; - - public: +}; - // Constructor - HashVec(); +// Define the hashvec type. +using hashvec_t = std::vector>; - // Member functions - std::vector>& get(); - void sort(); - void write(); - -}; +#endif -#endif \ No newline at end of file diff --git a/src/c++/hashvec_handler.cpp b/src/c++/hashvec_handler.cpp new file mode 100644 index 00000000..0769b39f --- /dev/null +++ b/src/c++/hashvec_handler.cpp @@ -0,0 +1,73 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +#include "hashvec_handler.h" + +/** + * @brief HashVecHandler constructor + * + * @note Allocates the writer strategy based on the PROF_IO_MODE environment + * variable. + * + */ + +HashVecHandler::HashVecHandler() +{ + + // Default the IO mode to one file per MPI rank. + std::string io_mode = "multi"; + + // Read environment variable. + char const* env_io_mode = std::getenv("PROF_IO_MODE"); + if (env_io_mode) { io_mode = env_io_mode; } + + // Allocate writer to be of required type. + if (io_mode == "multi") + { + writer_strategy_ = std::make_unique(); + } + else throw std::runtime_error("Invalid IO mode choice"); +} + +/** + * @brief Appends a hashvec vector to the HashVecHandler data member. + * @param [in] append_hashvec The vector to append. + * + */ + +void HashVecHandler::append(hashvec_t const& append_hashvec) +{ + hashvec_.insert(hashvec_.end(), append_hashvec.begin(), append_hashvec.end()); +} + +/** + * @brief Sorts hash entries from high to low self walltime. + * + */ + +void HashVecHandler::sort() +{ + std::sort + ( + begin(hashvec_), end(hashvec_), + [] (auto a, auto b) { + return a.second.self_walltime_ > b.second.self_walltime_; + } + ); +} + +/** + * @brief Calls the writer strategy. + * + */ + +void HashVecHandler::write() +{ + std::ofstream os; + writer_strategy_->write(os, hashvec_); +} + diff --git a/src/c++/hashvec_handler.h b/src/c++/hashvec_handler.h new file mode 100644 index 00000000..e9386002 --- /dev/null +++ b/src/c++/hashvec_handler.h @@ -0,0 +1,54 @@ +/* ----------------------------------------------------------------------------- + * (c) Crown copyright 2021 Met Office. All rights reserved. + * The file LICENCE, distributed with this code, contains details of the terms + * under which the code may be used. + * ----------------------------------------------------------------------------- + */ + +/** + * @file hashvec_handler.h + * @brief Container for the hashvec and IO write strategy. + * + */ + +#ifndef PROFILER_HASHVEC_HANDLER_H +#define PROFILER_HASHVEC_HANDLER_H + +#include "writer.h" +#include "hashvec.h" +#include + +/** + * @brief HashVecHandler class + * + * @details The HashVecHandler contains a hashvec vector as a data member. + * It wraps this vector with additional functionality to sort entries + * and set a writer strategy based on the PROF_IO_MODE environment + * variable. + * + */ + +class HashVecHandler { + + private: + + // Vector of hash entries. + hashvec_t hashvec_; + + // Writer strategy + std::unique_ptr writer_strategy_; + + public: + + // Constructor + HashVecHandler(); + + // Member functions + void sort(); + void write(); + void append(hashvec_t const&); + +}; + +#endif + diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 25c06ef7..b7542d8e 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -4,8 +4,8 @@ under which the code may be used. \*----------------------------------------------------------------------------*/ +#include "hashvec_handler.h" #include "profiler.h" -#include "hashvec.h" #include #include @@ -80,7 +80,9 @@ size_t Profiler::start(std::string_view region_name) assert (tid <= thread_traceback_.size()); // Insert this region into the thread's hash table. - std::string new_region_name = std::string(region_name); + std::string new_region_name; + new_region_name.reserve(region_name.size()+10); + new_region_name += region_name; new_region_name += '@'; new_region_name += std::to_string(tid); size_t const hash = thread_hashtables_[tid].query_insert(new_region_name); @@ -95,7 +97,7 @@ size_t Profiler::start(std::string_view region_name) /** * @brief Stop timing a profiled code region. - * @param [in] Hash of the profiled code region being stopped. + * @param [in] hash Hash of the profiled code region being stopped. * @note The calliper time (spent in the profiler) is measured by * differencing the beginning of the start calliper from the end of the stop * calliper, and subtracting the measured region time. Hence larger @@ -161,23 +163,22 @@ void Profiler::stop(size_t const hash) /** * @brief Write profile information to file. * - * @note The default file that the profiler will spit information into is - * called "profiler-output". There also exists the option to set a - * custom name via an environment variable. + * @note The default output file seedname is "profiler-output". There also + * exists the option to set a custom name via an environment variable. * */ void Profiler::write() { - HashVec new_hashvec; + HashVecHandler output_data; - for (auto& it : thread_hashtables_) + for (auto& table : thread_hashtables_) { - it.append_to(new_hashvec.get()); + table.append_to(output_data); } - new_hashvec.sort(); - new_hashvec.write(); + output_data.sort(); + output_data.write(); } @@ -230,7 +231,7 @@ double Profiler::get_overhead_walltime(size_t const hash, int const thread_id) * cost of child regions. * * @param[in] hash The hash corresponding to the region of interest. - * @param[in] thread_id The thread ID for which to return the walltime. + * @param[in] input_tid The thread ID for which to return the walltime. * */ @@ -245,7 +246,7 @@ double Profiler::get_self_walltime(size_t const hash, int const input_tid) * the time taken by their descendents. * * @param[in] hash The hash corresponding to the region of interest. - * @param[in] thread_id The thread ID for which to return the walltime. + * @param[in] input_tid The thread ID for which to return the walltime. * * @note This time does not include profiling overhead costs incurred directly * by the region. @@ -262,7 +263,7 @@ double Profiler::get_child_walltime(size_t const hash, int const input_tid) cons * @brief Get the name of a region corresponding to a given hash. * * @param[in] hash The hash corresponding to the region of interest. - * @param[in] thread_id The thread ID for which to return the walltime. + * @param[in] input_tid The thread ID for which to return the walltime. * * @note The thread ID is included to future-proof against the possibility of * including the thread ID in hashed strings. Hence the hash may not be @@ -280,8 +281,8 @@ std::string Profiler::get_region_name(size_t const hash, int const input_tid) co * @brief Get the number of times the input hash region has been called on the * input thread ID. * - * @param[in] hash The hash corresponding to the region of interest. - * @param[in] tid The ID corresponding to the thread of interest. + * @param[in] hash The hash corresponding to the region of interest. + * @param[in] input_tid The ID corresponding to the thread of interest. * * @returns Returns an integer corresponding to the number of times the * region of interest has been called on the specified thread. @@ -297,8 +298,7 @@ unsigned long long int Profiler::get_call_count(size_t const hash, int const inp /** * @brief Get the number of calliper pairs called on the specified thread. * - * @param[in] hash The hash corresponding to the region of interest. - * @param[in] tid The ID corresponding to the thread of interest. + * @param[in] input_tid The ID corresponding to the thread of interest. * * @returns Returns an integer corresponding to the number of times the * region of interest has been called on the specified thread. diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp index a9a326bd..e7934ed9 100644 --- a/src/c++/writer.cpp +++ b/src/c++/writer.cpp @@ -6,29 +6,23 @@ */ #include "writer.h" -#include /** - * @brief Writer constructor - * - * @param[in] formatter A pointer to the formatter class that will replace - * Writer::formatter_ - */ - -Writer::Writer(std::unique_ptr formatter) - : formatter_(std::move(formatter)) - {} - -/** - * @brief Tool via which any derived classes can access formatter_ - * - * @returns std::unique_ptr& Returns the private formatter pointer + * @brief Set data members common to all Writer objects. + * */ -// const std::unique_ptr Writer::get_formatter() -// { -// return formatter_; -// } +Writer::Writer() +{ + // Pick up environment variable filename if it exists. If it's not set, a + // suitable default is set in the data member declaration. + const char* env_output_filename = std::getenv("PROF_OUTPUT_FILENAME"); + if (env_output_filename) {output_filename_ = env_output_filename;} + + // MPI handling + MPI_Comm_dup(MPI_COMM_WORLD, &prof_comm_); + MPI_Comm_rank(prof_comm_, &my_rank_); +} /** * @brief Opens a unique file per mpi rank @@ -36,56 +30,28 @@ Writer::Writer(std::unique_ptr formatter) * @param[in] os Output stream to write to */ -void Multi::prep(std::ofstream& os) +void Multi::open_files(std::ofstream& os) { - // Find current MPI rank - int current_rank; - MPI_Comm prof_comm_ = MPI_COMM_WORLD; - MPI_Comm_rank(prof_comm_, ¤t_rank); - // For later appending onto the end of the output file name - std::string mpi_filename_tail = "-" + std::to_string(current_rank); - - // Pickup environment variable filename if it exists, if not use the - // default name of "profiler-output". In either case, include the MPI rank - // in the name of the file. - std::string out_filename; - const char* env_variable = std::getenv("PROF_OUTFILE"); - if (env_variable != NULL) - { - out_filename = static_cast(env_variable) + mpi_filename_tail; - } - else - { - out_filename = "profiler-output" + mpi_filename_tail; - } + // Append the MPI rank to the output filename. + std::string mpi_filename_tail = "-" + std::to_string(my_rank_); + output_filename_ += mpi_filename_tail; - os.open(out_filename); + os.open(output_filename_); } /** - * @brief Multiple-file-output "Multi" class constructor - * - * @param[in] formatter An input formatter pointer that will be used to call - * Writer's constructor - */ - -Multi::Multi(std::unique_ptr formatter) - : Writer(std::move(formatter)) - {} - -/** - * @brief The main write method, combines prep() with formatter_'s format, - * before then flushing and closing the output stream. - * + * @brief The main write method. Includes filehandling and calls formatter + * strategy. + * * @param[in] os The output stream to write to * @param[in] hashvec The vector containing all necessary data */ -void Multi::write(std::ofstream& os, std::vector> hashvec) +void Multi::write(std::ofstream& os, hashvec_t hashvec) { - prep(os); - this->formatter_->executeFormat(os, hashvec); + open_files(os); + formatter_.execute_format(os, hashvec); os.flush(); os.close(); } diff --git a/src/c++/writer.h b/src/c++/writer.h index c6e98ae2..44eb8843 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -7,51 +7,54 @@ /** * @file writer.h - * @brief Contains the writer class, which handles IO. - * - * Implements a strategy pattern that favours using functions over - * single-method objects, hence reducing the number of classes required. + * @brief Writer strategy classes. * */ #ifndef WRITER_H #define WRITER_H +#include #include #include #include +#include "hashvec.h" #include "formatter.h" /** - * @brief Writer class + * @brief Abstract Writer strategy class. + * @details Specific implementations of this class override the `write` function + * to produce different behaviour. * - * Abstract class that stores a pointer to the formatter. Both the formatter - * and the virtual write method can be changed by any derived classes. */ class Writer { protected: - // Ptr to formatting class - const std::unique_ptr formatter_; + // Formatter strategy + Formatter formatter_; + + std::string output_filename_ = "profiler-output"; - // The constructor, which gives the formatter pointer above a value. - // It will be inherited by any derived classes. - explicit Writer(std::unique_ptr formatter); + // MPI handling + int my_rank_; + MPI_Comm prof_comm_; public: + explicit Writer(); virtual ~Writer() = default; - Writer() = delete; - // Virtual write method - virtual void write(std::ofstream& os, std::vector> hashvec) = 0; + // Pure virtual write method + virtual void write(std::ofstream& os, hashvec_t) = 0; }; /** - * @brief Class for multiple-file output + * @brief Multiple-file output strategy + * @details Creates one file per MPI rank. + * */ class Multi : public Writer { @@ -59,14 +62,12 @@ class Multi : public Writer { private: // Method - void prep(std::ofstream& os); + void open_files(std::ofstream& os); public: - ~Multi() override = default; - explicit Multi(std::unique_ptr formatter); - - void write(std::ofstream& os, std::vector> hashvec) override; + // Implementation of pure virtual function. + void write(std::ofstream& os, hashvec_t) override; }; diff --git a/src/f/CMakeLists.txt b/src/f/CMakeLists.txt index 0735571c..175bd542 100644 --- a/src/f/CMakeLists.txt +++ b/src/f/CMakeLists.txt @@ -7,7 +7,10 @@ set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/modules) # Testing for building a fortran lib and executable. add_library(profiler_f SHARED "profiler_mod.F90") -target_link_libraries(profiler_f PUBLIC OpenMP::OpenMP_Fortran profiler_c) +target_link_libraries(profiler_f PUBLIC + profiler_c + OpenMP::OpenMP_Fortran + MPI::MPI_Fortran) include(FortranCInterface) FortranCInterface_VERIFY() diff --git a/tests/unit_tests/f/test_profiler_mod.pf b/tests/unit_tests/f/test_profiler_mod.pf index 741a7ee2..31f7fe1d 100644 --- a/tests/unit_tests/f/test_profiler_mod.pf +++ b/tests/unit_tests/f/test_profiler_mod.pf @@ -8,6 +8,7 @@ module test_profiler_mod use profiler_mod use omp_lib + use mpi_f08 contains @@ -26,6 +27,12 @@ contains ! Handle declarations integer(kind=pik) :: prof_main + ! Error handler + integer :: ierr + + ! Initialise MPI + call mpi_init(ierr) + ! Start timing: noddy way, and using Profiler. call profiler_start(prof_main, 'FULL') t1 = omp_get_wtime() @@ -72,6 +79,10 @@ contains @assertEqual(actual_time, profiler_wallclock_time, tolerance=1.0e-4_dp) end block assertions + call profiler_write() + + call mpi_finalize(ierr) + end subroutine test_profiler end module test_profiler_mod From 7e4da7f6430a1af30d7e8ee131436838dd0885dc Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 8 Feb 2023 11:31:49 +0000 Subject: [PATCH 102/128] Minor cosmetic/comment additions or adjustments (#72) --- src/c++/formatter.h | 7 +++---- src/c++/hashtable.cpp | 6 +++--- src/c++/hashtable.h | 3 --- src/c++/hashvec.h | 3 ++- src/c++/hashvec_handler.h | 4 +++- src/c++/profiler.h | 6 ++---- src/c++/writer.h | 5 ++--- 7 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/c++/formatter.h b/src/c++/formatter.h index da8ae992..89a2eab7 100644 --- a/src/c++/formatter.h +++ b/src/c++/formatter.h @@ -8,14 +8,13 @@ /** * @file formatter.h * @brief Formatter class, which contains methods for writing in different - * output formats.. + * output formats. * */ #ifndef FORMATTER_H #define FORMATTER_H -#include #include #ifdef _OPENMP @@ -39,8 +38,8 @@ class Formatter { void (Formatter::*format_)(std::ofstream&, hashvec_t); // Individual formatter functions - void threads (std::ofstream& os, hashvec_t); - void drhook (std::ofstream& os, hashvec_t); + void threads(std::ofstream& os, hashvec_t); + void drhook (std::ofstream& os, hashvec_t); public: diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index b805c001..7ffa646d 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -188,7 +188,7 @@ std::vector HashTable::list_keys() * */ -void HashTable::append_to(HashVecHandler& hashvec) +void HashTable::append_to(HashVecHandler& hashvec_handler) { // Compute overhead and self times before appending prepare_computed_times_all(); @@ -198,10 +198,10 @@ void HashTable::append_to(HashVecHandler& hashvec) if (it != table_.end() && it->second.call_count_ == 0) { table_.erase(it); } // Create hashvec from the table data. + hashvec_t new_hashvec(table_.cbegin(), table_.cend()); // Append hashvec to argument. - hashvec_t new_hashvec (table_.cbegin(), table_.cend()); - hashvec.append(new_hashvec); + hashvec_handler.append(new_hashvec); } /** diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 5ff5e6e7..92bd602f 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -24,9 +24,6 @@ #define PROFILER_HASHTABLE_H #include -#include -#include -#include #include "hashvec.h" #include "hashvec_handler.h" diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 61ade2cf..a09901b1 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -13,8 +13,8 @@ #ifndef PROFILER_HASHVEC_H #define PROFILER_HASHVEC_H -#include #include +#include #include "prof_gettime.h" @@ -24,6 +24,7 @@ */ struct HashEntry{ + public: // Constructor diff --git a/src/c++/hashvec_handler.h b/src/c++/hashvec_handler.h index e9386002..0c1eb012 100644 --- a/src/c++/hashvec_handler.h +++ b/src/c++/hashvec_handler.h @@ -14,9 +14,11 @@ #ifndef PROFILER_HASHVEC_HANDLER_H #define PROFILER_HASHVEC_HANDLER_H +#include +#include + #include "writer.h" #include "hashvec.h" -#include /** * @brief HashVecHandler class diff --git a/src/c++/profiler.h b/src/c++/profiler.h index a2e5b80f..c5dbadfa 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -16,9 +16,7 @@ #ifndef PROFILER_H #define PROFILER_H -#include #include -#include #include #include "hashtable.h" @@ -55,8 +53,8 @@ class Profiler // Data members int max_threads_; - std::vector thread_hashtables_; - std::vector>> thread_traceback_; + std::vector thread_hashtables_; + std::vector>> thread_traceback_; // Type definitions for vector array indexing. typedef std::vector::size_type hashtable_iterator_t_; diff --git a/src/c++/writer.h b/src/c++/writer.h index 44eb8843..cfa04852 100644 --- a/src/c++/writer.h +++ b/src/c++/writer.h @@ -15,9 +15,7 @@ #define WRITER_H #include -#include -#include -#include + #include "hashvec.h" #include "formatter.h" @@ -35,6 +33,7 @@ class Writer { // Formatter strategy Formatter formatter_; + // Default filename std::string output_filename_ = "profiler-output"; // MPI handling From 861d0eb5b4813eca46b71cd47aee36017d7573fb Mon Sep 17 00:00:00 2001 From: "ben.copley" Date: Wed, 8 Feb 2023 11:33:47 +0000 Subject: [PATCH 103/128] Accidentally removed required include in last commit (#72) --- src/c++/hashvec.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index a09901b1..b49262fc 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -13,6 +13,7 @@ #ifndef PROFILER_HASHVEC_H #define PROFILER_HASHVEC_H +#include #include #include From fce0d9ae20bda599af3843dbb5f9e8de2f21b82b Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 20 Apr 2023 10:33:54 +0100 Subject: [PATCH 104/128] Corrections following merge of formatting changes on main. --- src/c++/CMakeLists.txt | 1 + src/c++/formatter.cpp | 23 +++++++------ src/c++/hashtable.cpp | 4 ++- src/c++/hashtable.h | 1 + src/c++/prof_gettime.cpp | 1 - src/c++/profiler.cpp | 1 + src/c++/writer.cpp | 59 -------------------------------- src/c++/writer.h | 73 ---------------------------------------- 8 files changed, 18 insertions(+), 145 deletions(-) delete mode 100644 src/c++/writer.cpp delete mode 100644 src/c++/writer.h diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index c48647c2..34747dd9 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -1,3 +1,4 @@ + # ------------------------------------------------------------------------------ # (c) Crown copyright 2022 Met Office. All rights reserved. # The file LICENCE, distributed with this code, contains details of the terms diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 4bcef745..165fd40a 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -6,6 +6,7 @@ */ #include "formatter.h" + #include #include @@ -83,7 +84,7 @@ void Formatter::threads(std::ofstream& os, hashvec_t hashvec) os << std::setfill(' '); // Data entries - for (auto& record : hashvec) { + for (auto const& record : hashvec) { os << std::setw(40) << std::left << record.region_name_ << " " << std::setw(15) << std::right << record.self_walltime_.count() << " " @@ -167,7 +168,7 @@ void Formatter::drhook(std::ofstream& os, hashvec_t hashvec) os << std::fixed << std::showpoint << std::setprecision(3); - for (auto& record : hashvec) { + for (auto const& record : hashvec) { // Calculate non-RegionRecord data region_number++; @@ -177,17 +178,17 @@ void Formatter::drhook(std::ofstream& os, hashvec_t hashvec) total_per_call = 1000.0 * ( record.total_walltime_.count() / static_cast(record.call_count_) ); // Write everything out - os + os << " " - << std::setw(3) << std::left << region_number - << std::setw(7) << std::right << percent_time - << std::setw(13) << std::right << cumul_walltime.count() - << std::setw(13) << std::right << record.self_walltime_.count() + << std::setw(3) << std::left << region_number + << std::setw(7) << std::right << percent_time + << std::setw(13) << std::right << cumul_walltime.count() + << std::setw(13) << std::right << record.self_walltime_.count() << std::setw(13) << std::right << record.total_walltime_.count() - << std::setw(15) << std::right << record.call_count_ - << std::setw(12) << std::right << self_per_call - << std::setw(12) << std::right << total_per_call << " " - << record.region_name_ << "\n"; + << std::setw(15) << std::right << record.call_count_ + << std::setw(12) << std::right << self_per_call + << std::setw(12) << std::right << total_per_call << " " + << record.region_name_ << "\n"; } } diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index ee8b61ed..a234b205 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -5,6 +5,8 @@ \*----------------------------------------------------------------------------*/ #include "hashtable.h" +#include "hashvec_handler.h" + #include #include @@ -45,7 +47,7 @@ HashTable::HashTable(int const tid) profiler_hash_ = hash_function_(profiler_name); // Insert special entry for the profiler overhead time. - query_insert("__profiler__", profiler_hash_, profiler_index_); + query_insert(profiler_name, profiler_hash_, profiler_index_); } /** diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 6f41d331..c94c5980 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -105,5 +105,6 @@ class HashTable{ unsigned long long int get_call_count(size_t const hash) const; unsigned long long int get_prof_call_count() const; }; + #endif diff --git a/src/c++/prof_gettime.cpp b/src/c++/prof_gettime.cpp index 3623e943..801e3c2f 100644 --- a/src/c++/prof_gettime.cpp +++ b/src/c++/prof_gettime.cpp @@ -17,4 +17,3 @@ time_point_t prof_gettime() { return std::chrono::steady_clock::now(); } - diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index cb48ea3a..fc82115d 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -91,6 +91,7 @@ Profiler::Profiler() // Create a new list std::array new_list; thread_traceback_.push_back(new_list); + } // Assertions diff --git a/src/c++/writer.cpp b/src/c++/writer.cpp deleted file mode 100644 index e7934ed9..00000000 --- a/src/c++/writer.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* ----------------------------------------------------------------------------- - * (c) Crown copyright 2021 Met Office. All rights reserved. - * The file LICENCE, distributed with this code, contains details of the terms - * under which the code may be used. - * ----------------------------------------------------------------------------- - */ - -#include "writer.h" - -/** - * @brief Set data members common to all Writer objects. - * - */ - -Writer::Writer() -{ - // Pick up environment variable filename if it exists. If it's not set, a - // suitable default is set in the data member declaration. - const char* env_output_filename = std::getenv("PROF_OUTPUT_FILENAME"); - if (env_output_filename) {output_filename_ = env_output_filename;} - - // MPI handling - MPI_Comm_dup(MPI_COMM_WORLD, &prof_comm_); - MPI_Comm_rank(prof_comm_, &my_rank_); -} - -/** - * @brief Opens a unique file per mpi rank - * - * @param[in] os Output stream to write to - */ - -void Multi::open_files(std::ofstream& os) -{ - - // Append the MPI rank to the output filename. - std::string mpi_filename_tail = "-" + std::to_string(my_rank_); - output_filename_ += mpi_filename_tail; - - os.open(output_filename_); -} - -/** - * @brief The main write method. Includes filehandling and calls formatter - * strategy. - * - * @param[in] os The output stream to write to - * @param[in] hashvec The vector containing all necessary data - */ - -void Multi::write(std::ofstream& os, hashvec_t hashvec) -{ - open_files(os); - formatter_.execute_format(os, hashvec); - os.flush(); - os.close(); -} - - diff --git a/src/c++/writer.h b/src/c++/writer.h deleted file mode 100644 index cfa04852..00000000 --- a/src/c++/writer.h +++ /dev/null @@ -1,73 +0,0 @@ -/* ----------------------------------------------------------------------------- - * (c) Crown copyright 2021 Met Office. All rights reserved. - * The file LICENCE, distributed with this code, contains details of the terms - * under which the code may be used. - * ----------------------------------------------------------------------------- - */ - -/** - * @file writer.h - * @brief Writer strategy classes. - * - */ - -#ifndef WRITER_H -#define WRITER_H - -#include - -#include "hashvec.h" -#include "formatter.h" - -/** - * @brief Abstract Writer strategy class. - * @details Specific implementations of this class override the `write` function - * to produce different behaviour. - * - */ - -class Writer { - - protected: - - // Formatter strategy - Formatter formatter_; - - // Default filename - std::string output_filename_ = "profiler-output"; - - // MPI handling - int my_rank_; - MPI_Comm prof_comm_; - - public: - - explicit Writer(); - virtual ~Writer() = default; - - // Pure virtual write method - virtual void write(std::ofstream& os, hashvec_t) = 0; - -}; - -/** - * @brief Multiple-file output strategy - * @details Creates one file per MPI rank. - * - */ - -class Multi : public Writer { - - private: - - // Method - void open_files(std::ofstream& os); - - public: - - // Implementation of pure virtual function. - void write(std::ofstream& os, hashvec_t) override; - -}; - -#endif From 28694dd3539fabd2cf8ef14b7be52965eb232c6a Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 20 Apr 2023 12:01:59 +0100 Subject: [PATCH 105/128] Add __profiler__ region name test, alongside tests for other regions. --- tests/unit_tests/c++/test_regionname.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/unit_tests/c++/test_regionname.cpp b/tests/unit_tests/c++/test_regionname.cpp index 3fd4ea11..14571a87 100644 --- a/tests/unit_tests/c++/test_regionname.cpp +++ b/tests/unit_tests/c++/test_regionname.cpp @@ -45,6 +45,15 @@ TEST(RegionNameTest,NamesMatchTest) { EXPECT_EQ("Cappucino@0", regionName); } + { + SCOPED_TRACE("Problem with the profiler region name"); + + // Get profiler region name out from the profiler and test + auto const prof_self_handle = std::hash{}("__profiler__@0"); + std::string profilerRegionName = prof.get_region_name(prof_self_handle,0); + EXPECT_EQ("__profiler__@0", profilerRegionName); + } + prof.stop(prof_cappucino); } From 8cb8d71611a2d3b84ebb6e20c28af5dfb9be01e7 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:25:30 +0100 Subject: [PATCH 106/128] Move RegionRecord constructor. --- src/c++/CMakeLists.txt | 2 +- src/c++/hashtable.cpp | 18 ------------------ src/c++/hashvec.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/c++/hashvec.cpp diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt index 34747dd9..a32b2e2b 100644 --- a/src/c++/CMakeLists.txt +++ b/src/c++/CMakeLists.txt @@ -20,7 +20,7 @@ add_library(${CMAKE_PROJECT_NAME} writer/writer.h writer/writer.cpp writer/multi.h writer/multi.cpp formatter.h formatter.cpp - hashvec.h + hashvec.h hashvec.cpp prof_gettime.h prof_gettime.cpp ) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index a234b205..09ca7182 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -12,24 +12,6 @@ #define PROF_HASHVEC_RESERVE_SIZE 1000 -/** - * @brief Constructs a new region record. - * @param [in] region_hash_ Hash of the region name. - * @param [in] region_name_ The region name. - * - */ - -RegionRecord::RegionRecord(size_t const region_hash, std::string_view const region_name) - : region_hash_(region_hash) - , region_name_(region_name) - , total_walltime_ (time_duration_t::zero()) - , total_raw_walltime_ (time_duration_t::zero()) - , self_walltime_ (time_duration_t::zero()) - , child_walltime_ (time_duration_t::zero()) - , overhead_walltime_ (time_duration_t::zero()) - , call_count_(0) - {} - /** * @brief Hashtable constructor * @param [in] tid The thread ID. diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp new file mode 100644 index 00000000..75ed858b --- /dev/null +++ b/src/c++/hashvec.cpp @@ -0,0 +1,27 @@ +/*----------------------------------------------------------------------------*\ + (c) Crown copyright 2023 Met Office. All rights reserved. + The file LICENCE, distributed with this code, contains details of the terms + under which the code may be used. +\*----------------------------------------------------------------------------*/ + +#include "hashvec.h" + +/** + * @brief Constructs a new region record. + * @param [in] region_hash Hash of the region name. + * @param [in] region_name The region name. + * @param [in] tid The thread ID. + */ + +RegionRecord::RegionRecord(size_t const region_hash, + std::string_view const region_name) +: region_hash_(region_hash) +, region_name_(region_name) +, total_walltime_ (time_duration_t::zero()) +, total_raw_walltime_ (time_duration_t::zero()) +, self_walltime_ (time_duration_t::zero()) +, child_walltime_ (time_duration_t::zero()) +, overhead_walltime_ (time_duration_t::zero()) +, call_count_(0) +{} + From c708ac10d5e6dcec33aa45a9d7a8bc775851efe1 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 5 May 2023 10:46:51 +0100 Subject: [PATCH 107/128] Working byte copying, with appropriate changes to unit tests. --- src/c++/formatter.cpp | 4 +- src/c++/hashtable.cpp | 91 +++++++++++++++++++++--- src/c++/hashtable.h | 5 +- src/c++/hashvec.cpp | 9 ++- src/c++/hashvec.h | 3 +- src/c++/profiler.cpp | 16 ++--- src/c++/profiler.h | 2 +- tests/unit_tests/c++/test_hashtable.cpp | 16 ++++- tests/unit_tests/c++/test_hashtiming.cpp | 12 ++-- tests/unit_tests/c++/test_regionname.cpp | 20 +++--- 10 files changed, 134 insertions(+), 44 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 165fd40a..24ee3a2a 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -187,8 +187,8 @@ void Formatter::drhook(std::ofstream& os, hashvec_t hashvec) << std::setw(13) << std::right << record.total_walltime_.count() << std::setw(15) << std::right << record.call_count_ << std::setw(12) << std::right << self_per_call - << std::setw(12) << std::right << total_per_call << " " - << record.region_name_ << "\n"; + << std::setw(12) << std::right << total_per_call << " " + << record.decorated_region_name_ << "\n"; } } diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 09ca7182..cbbefdb9 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -8,9 +8,13 @@ #include "hashvec_handler.h" #include +#include #include +#include #define PROF_HASHVEC_RESERVE_SIZE 1000 +#define PROF_STRING_BUFFER_LENGTH 100 + /** * @brief Hashtable constructor @@ -25,11 +29,77 @@ HashTable::HashTable(int const tid) hashvec_.reserve(PROF_HASHVEC_RESERVE_SIZE); // Set the name and hash of the profiler entry. - std::string const profiler_name = "__profiler__@" + std::to_string(tid); - profiler_hash_ = hash_function_(profiler_name); + std::string const profiler_name = "__profiler__"; // Insert special entry for the profiler overhead time. - query_insert(profiler_name, profiler_hash_, profiler_index_); + query_insert(profiler_name, tid, profiler_hash_, profiler_index_); + +} + +/** + * @brief Decorates a region name with the thread ID and computes the + * corresponding hash. + * + * @param [in] region_name The code region name. + * @param [in] tid The thread ID + * + * @note The integer thread ID is not converted to a string before it is + * appended to the hash string. This is a performance measure. + * Conversions to strings are expensive and not strictly necessary. + * Since the physical bit pattern is unique for each integer, that + * will do the job. The hash function input does not need to be + * human-readable. + * + */ + +size_t HashTable::compute_hash(std::string_view region_name, int tid) +{ + + // Get the bit-pattern of the thread ID. + std::array tid_bytes; + std::memcpy(tid_bytes.data(), &tid, sizeof(tid)); + + [[maybe_unused]] int const* tid_back = reinterpret_cast(tid_bytes.data()); + assert (*tid_back == tid); + + // Store special characters in an array container, so that we have STL syntax + // available to us later. + std::array constexpr delimiter = {'@'}; + + // Extra bytes to accommodate the thread ID. + int constexpr num_extra_bytes = sizeof(tid) + + sizeof(delimiter); + + // Avoid dynamic memory allocation for performance reasons. Instead, fix the + // size of the buffer and perform a runtime check that we're not exceeding it. + std::array new_chars; + new_chars.fill('\0'); + + if (region_name.length() + num_extra_bytes > new_chars.size()) { + std::string error_msg = "Internal error: character buffer exhausted."; + throw std::runtime_error(error_msg); + } + + // Get iterator to the start of the string buffer. + auto new_chars_iterator = new_chars.begin(); + + // Copy the region name into the char buffer. + std::copy(region_name.begin(), region_name.end(), new_chars_iterator); + std::advance(new_chars_iterator, region_name.size()); + + // Add delimiter + std::copy(delimiter.begin(), delimiter.end(), new_chars_iterator); + std::advance(new_chars_iterator, delimiter.size()); + + // Add thread ID (physical representation) + std::memcpy(&(*new_chars_iterator), tid_bytes.data(), tid_bytes.size()); + std::advance(new_chars_iterator, tid_bytes.size()); + + [[maybe_unused]] int const expected_size = region_name.length() + num_extra_bytes; + int new_chars_size = std::distance(new_chars.begin(), new_chars_iterator); + assert (new_chars_size == expected_size); + + return hash_function_(std::string_view(new_chars.data(), new_chars_size)); } /** @@ -41,10 +111,13 @@ HashTable::HashTable(int const tid) */ void HashTable::query_insert(std::string_view const region_name, + int tid, size_t& hash, record_index_t& record_index) noexcept { - hash = hash_function_(region_name); + + // Compute the hash + hash = compute_hash(region_name, tid); // Does the entry exist already? if (auto search = lookup_table_.find(hash); search != lookup_table_.end()) @@ -55,7 +128,8 @@ void HashTable::query_insert(std::string_view const region_name, // If not, create new entry. else { - hashvec_.emplace_back(hash, region_name); + // Insert this region into the thread's hash table. + hashvec_.emplace_back(hash, region_name, tid); record_index = hashvec_.size()-1; lookup_table_.emplace(hash, record_index); assert (lookup_table_.count(hash) > 0); @@ -68,7 +142,8 @@ void HashTable::query_insert(std::string_view const region_name, * @param [in] time_delta The time increment to add. */ -void HashTable::update(record_index_t const record_index, time_duration_t const time_delta) +void HashTable::update(record_index_t const record_index, + time_duration_t const time_delta) { auto& record = hashvec_[record_index]; @@ -318,10 +393,10 @@ double HashTable::get_child_walltime(size_t const hash) const * @param [in] hash The hash corresponding to the region. */ -std::string HashTable::get_region_name(size_t const hash) const +std::string HashTable::get_decorated_region_name(size_t const hash) const { auto& record = hash2record_const(hash); - return record.region_name_; + return record.decorated_region_name_; } /** diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index c94c5980..7652ac8e 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -84,7 +84,8 @@ class HashTable{ HashTable(int); // Prototypes - void query_insert(std::string_view const, size_t&, record_index_t&) noexcept; + size_t compute_hash(std::string_view, int); + void query_insert(std::string_view const, int, size_t&, record_index_t&) noexcept; void update(record_index_t const, time_duration_t const); // Member functions @@ -101,7 +102,7 @@ class HashTable{ double get_overhead_walltime(size_t const hash) const; double get_self_walltime(size_t const hash); double get_child_walltime(size_t const hash) const; - std::string get_region_name(size_t const hash) const; + std::string get_decorated_region_name(size_t const hash) const; unsigned long long int get_call_count(size_t const hash) const; unsigned long long int get_prof_call_count() const; }; diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 75ed858b..e3793321 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -14,7 +14,8 @@ */ RegionRecord::RegionRecord(size_t const region_hash, - std::string_view const region_name) + std::string_view const region_name, + int tid) : region_hash_(region_hash) , region_name_(region_name) , total_walltime_ (time_duration_t::zero()) @@ -23,5 +24,9 @@ RegionRecord::RegionRecord(size_t const region_hash, , child_walltime_ (time_duration_t::zero()) , overhead_walltime_ (time_duration_t::zero()) , call_count_(0) -{} +{ + decorated_region_name_ = region_name_; + decorated_region_name_ += '@'; + decorated_region_name_ += std::to_string(tid); +} diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 486632a8..6e29acf2 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -31,11 +31,12 @@ struct RegionRecord{ // Constructor RegionRecord() = delete; - explicit RegionRecord(size_t const, std::string_view const); + explicit RegionRecord(size_t const, std::string_view const, int); // Data members size_t region_hash_; std::string region_name_; + std::string decorated_region_name_; time_duration_t total_walltime_; time_duration_t total_raw_walltime_; time_duration_t self_walltime_; diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index fc82115d..42ce8042 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -138,21 +138,14 @@ size_t Profiler::start_part2(std::string_view const region_name) #ifdef _OPENMP tid = static_cast(omp_get_thread_num()); #endif + int tid_int = static_cast(tid); assert (tid <= thread_hashtables_.size()); assert (tid <= thread_traceback_.size()); - // Insert this region into the thread's hash table. - std::string new_region_name; - new_region_name.reserve(region_name.size()+5); - new_region_name += region_name; - new_region_name += '@'; - new_region_name += std::to_string(tid); - size_t hash; record_index_t record_index; - thread_hashtables_[tid].query_insert(new_region_name, hash, record_index); - + thread_hashtables_[tid].query_insert(region_name, tid_int, hash, record_index); // Store the calliper and region start times. ++call_depth; if (call_depth < PROF_MAX_TRACEBACK_SIZE){ @@ -361,10 +354,11 @@ double Profiler::get_child_walltime(size_t const hash, int const input_tid) cons * */ -std::string Profiler::get_region_name(size_t const hash, int const input_tid) const +std::string Profiler::get_decorated_region_name(size_t const hash, + int const input_tid) const { auto tid = static_cast(input_tid); - return thread_hashtables_[tid].get_region_name(hash); + return thread_hashtables_[tid].get_decorated_region_name(hash); } /** diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 42bb7903..d1d37ee5 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -85,7 +85,7 @@ class Profiler double get_overhead_walltime (size_t const, int const); double get_self_walltime(size_t const hash, int const input_tid); double get_child_walltime(size_t const hash, int const input_tid) const; - std::string get_region_name(size_t const hash, int const input_tid) const; + std::string get_decorated_region_name(size_t const hash, int const input_tid) const; unsigned long long int get_call_count(size_t const hash, int const input_tid) const; unsigned long long int get_prof_call_count(int const input_tid) const; diff --git a/tests/unit_tests/c++/test_hashtable.cpp b/tests/unit_tests/c++/test_hashtable.cpp index 86f6556d..5ecb15aa 100644 --- a/tests/unit_tests/c++/test_hashtable.cpp +++ b/tests/unit_tests/c++/test_hashtable.cpp @@ -15,6 +15,10 @@ using ::testing::AllOf; using ::testing::An; using ::testing::Gt; +int const tid = 0; +std::string tid_str(std::to_string(tid)); +std::string tid_bytes(reinterpret_cast(&tid), sizeof(tid)); + // // Testing that the hashing function works as expected (we don't want // collisions), and that walltimes are being updated by profiler.stop(). @@ -38,8 +42,14 @@ TEST(HashTableTest,HashFunctionTest) { // - query_insert'ing Penne or Rigatoni just returns the hash // - the regions have different hashes // - the regions have the hashes returned by hash_function_ which uses std::hash - EXPECT_EQ(prof.start("Rigatoni"), std::hash{}("Rigatoni@0")); - EXPECT_EQ(prof.start("Penne"), std::hash{}("Penne@0")); + //std::string rigatoni_str = "Rigatoni@"; + //std::cout << "MJG ==> str len is: " << rigatoni_str.length() << "<<<===" << std::endl; + //rigatoni_str += tid_bytes; + //std::cout << "MJG ==> string is: " << rigatoni_str << "<<<===" << std::endl; + //std::cout << "MJG ==> length2 is: " << rigatoni_str.length() << "<<<===" << std::endl; + std::cout << "MJG ==> tid len is: " << tid_bytes.length() << "<<<===" << std::endl; + EXPECT_EQ(prof.start("Rigatoni"), std::hash{}("Rigatoni@" + tid_bytes)); + EXPECT_EQ(prof.start("Penne"), std::hash{}("Penne@" + tid_bytes)); } } @@ -53,7 +63,7 @@ TEST(HashTableTest,HashFunctionTest) { TEST(HashTableTest,UpdateTimesTest) { // Create new hash - size_t prof_pie = std::hash{}("Pie@0"); + size_t prof_pie = std::hash{}("Pie@" + tid_bytes); // Trying to find a time before .start() will throw an exception EXPECT_THROW(prof.get_total_walltime(prof_pie, 0), std::out_of_range); diff --git a/tests/unit_tests/c++/test_hashtiming.cpp b/tests/unit_tests/c++/test_hashtiming.cpp index 5ab2e70c..0c70eaa3 100644 --- a/tests/unit_tests/c++/test_hashtiming.cpp +++ b/tests/unit_tests/c++/test_hashtiming.cpp @@ -46,12 +46,12 @@ TEST(HashEntryTest, TimingsTest) { SCOPED_TRACE("Self walltime calculation failed"); // Grab the total, child and self wallclock times - const double& total_raw = prof.get_total_raw_walltime(prof_main,0); - const double& total = prof.get_total_walltime (prof_main,0); - const double& child = prof.get_child_walltime (prof_main,0); - const double& self = prof.get_self_walltime (prof_main,0); - const double& overhead = prof.get_overhead_walltime (prof_main,0); - std::string region = prof.get_region_name (prof_main,0); + const double& total_raw = prof.get_total_raw_walltime (prof_main,0); + const double& total = prof.get_total_walltime (prof_main,0); + const double& child = prof.get_child_walltime (prof_main,0); + const double& self = prof.get_self_walltime (prof_main,0); + const double& overhead = prof.get_overhead_walltime (prof_main,0); + std::string region = prof.get_decorated_region_name(prof_main,0); // Test that total EXPECT_EQ(total_raw,total-overhead) diff --git a/tests/unit_tests/c++/test_regionname.cpp b/tests/unit_tests/c++/test_regionname.cpp index 14571a87..1540f46c 100644 --- a/tests/unit_tests/c++/test_regionname.cpp +++ b/tests/unit_tests/c++/test_regionname.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include "profiler.h" @@ -18,6 +18,10 @@ // other funky region names will potentially be tested in the future. // +int const tid = 0; +std::string tid_str(std::to_string(tid)); +std::string tid_bytes(reinterpret_cast(&tid), sizeof(tid)); + TEST(RegionNameTest,NamesMatchTest) { //Start main region with name "Cappucino" @@ -31,8 +35,8 @@ TEST(RegionNameTest,NamesMatchTest) { const auto& prof_latte = prof.start(myString); // Get subregion name out from profiler and check it is what we expect - std::string subregionName = prof.get_region_name(prof_latte,0); - EXPECT_EQ("Latte@0", subregionName); + std::string subregionName = prof.get_decorated_region_name(prof_latte, tid); + EXPECT_EQ("Latte@" + tid_str, subregionName); prof.stop(prof_latte); } @@ -41,17 +45,17 @@ TEST(RegionNameTest,NamesMatchTest) { SCOPED_TRACE("Problem with main region name"); // Get main region name out from profiler and test - std::string regionName = prof.get_region_name(prof_cappucino,0); - EXPECT_EQ("Cappucino@0", regionName); + std::string regionName = prof.get_decorated_region_name(prof_cappucino, tid); + EXPECT_EQ("Cappucino@" + tid_str, regionName); } { SCOPED_TRACE("Problem with the profiler region name"); // Get profiler region name out from the profiler and test - auto const prof_self_handle = std::hash{}("__profiler__@0"); - std::string profilerRegionName = prof.get_region_name(prof_self_handle,0); - EXPECT_EQ("__profiler__@0", profilerRegionName); + auto const prof_self_handle = std::hash{}("__profiler__@" + tid_bytes); + std::string profilerRegionName = prof.get_decorated_region_name(prof_self_handle, tid); + EXPECT_EQ("__profiler__@" + tid_str, profilerRegionName); } prof.stop(prof_cappucino); From 9e3224008d6d5ed2087e7c0f35a1cc7b1d556083 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 5 May 2023 11:04:09 +0100 Subject: [PATCH 108/128] Tweaks to typecasting (remove build warning messages). --- src/c++/hashtable.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index cbbefdb9..ad9848a0 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -95,11 +95,12 @@ size_t HashTable::compute_hash(std::string_view region_name, int tid) std::memcpy(&(*new_chars_iterator), tid_bytes.data(), tid_bytes.size()); std::advance(new_chars_iterator, tid_bytes.size()); - [[maybe_unused]] int const expected_size = region_name.length() + num_extra_bytes; - int new_chars_size = std::distance(new_chars.begin(), new_chars_iterator); + [[maybe_unused]] auto const expected_size = region_name.length() + num_extra_bytes; + auto new_chars_size = std::distance(new_chars.begin(), new_chars_iterator); assert (new_chars_size == expected_size); - return hash_function_(std::string_view(new_chars.data(), new_chars_size)); + return hash_function_(std::string_view(new_chars.data(), + static_cast(new_chars_size))); } /** From 9e88a554e559e9ff906650aaca3dd3b60cc0cd7c Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 5 May 2023 20:41:31 +0100 Subject: [PATCH 109/128] Use PROF_STRING_BUFFER_LENGTH. --- src/c++/hashtable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index ad9848a0..93926d4d 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -72,7 +72,7 @@ size_t HashTable::compute_hash(std::string_view region_name, int tid) // Avoid dynamic memory allocation for performance reasons. Instead, fix the // size of the buffer and perform a runtime check that we're not exceeding it. - std::array new_chars; + std::array new_chars; new_chars.fill('\0'); if (region_name.length() + num_extra_bytes > new_chars.size()) { From 095e8dd7d1239f0a650ad4f99edb1dc438d9a447 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 11 May 2023 16:24:24 +0100 Subject: [PATCH 110/128] Return overhead time pointers through argument list, not returned from functions. --- src/c++/hashtable.cpp | 34 +++++++++++++++++++++------------- src/c++/hashtable.h | 5 ++--- src/c++/profiler.cpp | 20 ++++++++++++-------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 93926d4d..5d160c32 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -158,31 +158,39 @@ void HashTable::update(record_index_t const record_index, } /** - * @brief Add child region and overhead times to parent. - * @param [in] record_index The index corresponding to the region record. - * @param [in] time_delta The time spent in the child region. - * @returns Pointer to the overhead time for this region. + * @brief Add in time spent calling child regions. Also retuns a pointer + * to the overhead time so that it can be incremented downstream, + * outside this function, with minimal additional overhead. + * @param [in] record_index The index corresponding to the region record. + * @param [in] time_delta The time spent in the child region. + * @param [out] overhead_time_ptr Pointer to the profiling overhead time + * incurred by calling children of this region. */ -time_duration_t* HashTable::add_child_time( - record_index_t const record_index, - time_duration_t const child_walltime) +void HashTable::add_child_time( + record_index_t const record_index, + time_duration_t const child_walltime, + time_duration_t*& overhead_time_ptr) { auto& record = hashvec_[record_index]; record.child_walltime_ += child_walltime; - return &record.overhead_walltime_; + overhead_time_ptr = &record.overhead_walltime_; } /** - * @brief Add child region and overhead times to parent. - * @returns Reference to the total profiling overhead time. + * @brief Increment the number of calls to the profiler callipers. Also returns + * a pointer to the total profiling overhead time so that it can be + * incremented downstream, outside this function, with minimal + * additional overhead. + * @param [out] overhead_time_ptr Pointer to the total profiling overhead time + * incurred by calling every set of profiler + * calls. */ -time_duration_t& HashTable::increment_profiler_calls() -{ +void HashTable::add_profiler_call(time_duration_t*& overhead_time_ptr) { auto& record = hashvec_[profiler_index_]; ++record.call_count_; - return record.total_walltime_; + overhead_time_ptr = &record.total_walltime_; } /** diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 7652ac8e..c9f429ec 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -90,9 +90,8 @@ class HashTable{ // Member functions std::vector list_keys(); - time_duration_t* add_child_time(record_index_t const, time_duration_t const); - time_duration_t& increment_profiler_calls(); - void add_overhead_time(size_t const, time_duration_t); + void add_child_time(record_index_t const, time_duration_t const, time_duration_t*&); + void add_profiler_call(time_duration_t*&); void compute_self_times(); void append_to(HashVecHandler&); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 42ce8042..a2fea431 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -216,18 +216,20 @@ void Profiler::stop(size_t const hash) // The sequence of code that follows is aimed at leaving only minimal and // simple operations after the call to prof_gettime(). - time_duration_t* parent_overhead_time_ptr = nullptr; + time_duration_t* parent_overhead_time_ptr = nullptr; + time_duration_t* profiler_overhead_time_ptr = nullptr; // Acquire parent pointers if (call_depth > 0){ auto parent_depth = static_cast(call_depth-1); record_index_t parent_index = thread_traceback_[tid].at(parent_depth).record_index_; - parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( - parent_index, region_duration); + thread_hashtables_[tid].add_child_time( + parent_index, region_duration, + parent_overhead_time_ptr); } // Increment profiler calls, and get a reference to the total overhead time. - auto& total_overhead_time = thread_hashtables_[tid].increment_profiler_calls(); + thread_hashtables_[tid].add_profiler_call(profiler_overhead_time_ptr); // Decrement index to last entry in the traceback. --call_depth; @@ -236,10 +238,12 @@ void Profiler::stop(size_t const hash) auto calliper_stop_time = prof_gettime(); auto calliper_time = calliper_stop_time - temp_sum; - // Increment the overhead time both the parent and total overhead times, using - // previously-obtained pointers or references, as appropriate. - if(parent_overhead_time_ptr){ *parent_overhead_time_ptr += calliper_time; } - total_overhead_time += calliper_time; + // Increment the overhead time specific to this region, incurred when calling + // direct children, and also the overall profiling overhead time. + // Being outside the stop calliper, these operations need to be as cheap + // as possible. + if(parent_overhead_time_ptr) { *parent_overhead_time_ptr += calliper_time; } + *profiler_overhead_time_ptr += calliper_time; } /** From e8a40ac96b2aa7a81a9a12b26afd5cf042c253b8 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 12 May 2023 09:43:07 +0100 Subject: [PATCH 111/128] Forward declare HashVecHandler; use hashvec_t in one place; TracebackEntry() = default. --- src/c++/hashtable.h | 6 ++++-- src/c++/profiler.cpp | 8 -------- src/c++/profiler.h | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index c94c5980..79022de8 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -26,9 +26,11 @@ #include #include "hashvec.h" -#include "hashvec_handler.h" #include "prof_gettime.h" +// Forward declarations +class HashVecHandler; + /** * @brief Defines a null hash function. * @@ -66,7 +68,7 @@ class HashTable{ std::unordered_map lookup_table_; // Vector of region records. - std::vector hashvec_; + hashvec_t hashvec_; // Private member functions void prepare_computed_times(RegionRecord&); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index fc82115d..60fe647e 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -36,14 +36,6 @@ namespace{ int call_depth = -1; } -/** - * @brief Constructor for TracebackEntry struct. - * - */ - -Profiler::TracebackEntry::TracebackEntry() - {} - /** * @brief Constructor for TracebackEntry struct. * @param [in] record_hash The hash of the region name. diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 42bb7903..b5cd00d1 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -46,7 +46,7 @@ class Profiler public: // Constructors - TracebackEntry(); + TracebackEntry() = default; TracebackEntry(size_t, record_index_t, time_point_t, time_point_t); // Data members From ff402ad7aa0ffeeace14a3bbf87d9b4d5a43dac7 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 12 May 2023 09:55:07 +0100 Subject: [PATCH 112/128] Corrections to doxygen comments. --- src/c++/hashtable.cpp | 10 ++++++---- src/c++/hashvec.cpp | 1 - src/c++/profiler.cpp | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 09ca7182..653c6b95 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -64,7 +64,8 @@ void HashTable::query_insert(std::string_view const region_name, /** * @brief Updates the total walltime and call count for the specified region. - * @param [in] hash The hash corresponding to the profiled region. + * @param [in] record_index The index in hashvec_ corresponding to the + * profiled region. * @param [in] time_delta The time increment to add. */ @@ -83,8 +84,8 @@ void HashTable::update(record_index_t const record_index, time_duration_t const /** * @brief Add child region and overhead times to parent. - * @param [in] record_index The index corresponding to the region record. - * @param [in] time_delta The time spent in the child region. + * @param [in] record_index The index corresponding to the region record. + * @param [in] child_walltime The time spent in the child region. * @returns Pointer to the overhead time for this region. */ @@ -181,7 +182,8 @@ std::vector HashTable::list_keys() /** * @brief Appends table_ onto the end of an input hashvec. - * @param[inout] HashVecHandler containing the hashvec to amend. + * @param[inout] hashvec_handler HashVecHandler object containing the + * hashvec to amend. * */ diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 75ed858b..558fc885 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -10,7 +10,6 @@ * @brief Constructs a new region record. * @param [in] region_hash Hash of the region name. * @param [in] region_name The region name. - * @param [in] tid The thread ID. */ RegionRecord::RegionRecord(size_t const region_hash, diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 60fe647e..eaa4bbdf 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -93,8 +93,8 @@ Profiler::Profiler() } /** - * @brief Start timing a profiled code region. - * @detail Calls both part1 and part2 start routines in succession. + * @brief Start timing a profiled code region. + * @details Calls both part1 and part2 start routines in succession. * @param [in] region_name The code region name. * @returns Unique hash for the code region being started. */ From 4e2a8c235a050184498f3495d940b90337657277 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 12 May 2023 14:07:35 +0100 Subject: [PATCH 113/128] Make start_part1 and start_part2 methods private in Profiler class. --- src/c++/profiler.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/c++/profiler.h b/src/c++/profiler.h index b5cd00d1..88bc387d 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -25,6 +26,13 @@ #define PROF_MAX_TRACEBACK_SIZE 1000 +// Forward declarations. The definitions of these functions will require access +// to private methods. +extern "C" { + void c_profiler_start_part1(); + void c_profiler_start_part2(long int& hash_out, char const* name); +} + /** * @brief Top-level profiler class. * @@ -67,6 +75,10 @@ class Profiler typedef std::vector> ::size_type traceback_index_t; + // Private methods + void start_part1(); + size_t start_part2(std::string_view const); + public: // Constructors @@ -74,8 +86,6 @@ class Profiler // Member functions size_t start(std::string_view const); - void start_part1(); - size_t start_part2(std::string_view const); void stop (size_t const); void write(); @@ -89,6 +99,9 @@ class Profiler unsigned long long int get_call_count(size_t const hash, int const input_tid) const; unsigned long long int get_prof_call_count(int const input_tid) const; + // Grant these functions access to private methods. + void friend c_profiler_start_part1(); + void friend c_profiler_start_part2(long int& hash_out, char const* name); }; // Declare global profiler From d170cb4003a0245c8cd381c63bd3f8a797150b09 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 12 May 2023 15:25:53 +0100 Subject: [PATCH 114/128] Return overhead time pointers through argument list. --- src/c++/hashtable.cpp | 34 +++++++++++++++++++++------------- src/c++/hashtable.h | 7 ++++--- src/c++/profiler.cpp | 20 ++++++++++++-------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 653c6b95..27c507fb 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -83,31 +83,39 @@ void HashTable::update(record_index_t const record_index, time_duration_t const } /** - * @brief Add child region and overhead times to parent. - * @param [in] record_index The index corresponding to the region record. - * @param [in] child_walltime The time spent in the child region. - * @returns Pointer to the overhead time for this region. + * @brief Add in time spent calling child regions. Also retuns a pointer + * to the overhead time so that it can be incremented downstream, + * outside this function, with minimal additional overhead. + * @param [in] record_index The index corresponding to the region record. + * @param [in] time_delta The time spent in the child region. + * @param [out] overhead_time_ptr Pointer to the profiling overhead time + * incurred by calling children of this region. */ -time_duration_t* HashTable::add_child_time( - record_index_t const record_index, - time_duration_t const child_walltime) +void HashTable::add_child_time( + record_index_t const record_index, + time_duration_t const child_walltime, + time_duration_t*& overhead_time_ptr) { auto& record = hashvec_[record_index]; record.child_walltime_ += child_walltime; - return &record.overhead_walltime_; + overhead_time_ptr = &record.overhead_walltime_; } /** - * @brief Add child region and overhead times to parent. - * @returns Reference to the total profiling overhead time. + * @brief Increment the number of calls to the profiler callipers. Also returns + * a pointer to the total profiling overhead time so that it can be + * incremented downstream, outside this function, with minimal + * additional overhead. + * @param [out] overhead_time_ptr Pointer to the total profiling overhead time + * incurred by calling every set of profiler + * calls. */ -time_duration_t& HashTable::increment_profiler_calls() -{ +void HashTable::add_profiler_call(time_duration_t*& overhead_time_ptr) { auto& record = hashvec_[profiler_index_]; ++record.call_count_; - return record.total_walltime_; + overhead_time_ptr = &record.total_walltime_; } /** diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 79022de8..30489ec0 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -91,9 +91,10 @@ class HashTable{ // Member functions std::vector list_keys(); - time_duration_t* add_child_time(record_index_t const, time_duration_t const); - time_duration_t& increment_profiler_calls(); - void add_overhead_time(size_t const, time_duration_t); + + void add_child_time(record_index_t const, time_duration_t const, time_duration_t*&); + void add_profiler_call(time_duration_t*&); + void compute_self_times(); void append_to(HashVecHandler&); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index eaa4bbdf..f40b0617 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -215,18 +215,20 @@ void Profiler::stop(size_t const hash) // The sequence of code that follows is aimed at leaving only minimal and // simple operations after the call to prof_gettime(). - time_duration_t* parent_overhead_time_ptr = nullptr; + time_duration_t* parent_overhead_time_ptr = nullptr; + time_duration_t* profiler_overhead_time_ptr = nullptr; // Acquire parent pointers if (call_depth > 0){ auto parent_depth = static_cast(call_depth-1); record_index_t parent_index = thread_traceback_[tid].at(parent_depth).record_index_; - parent_overhead_time_ptr = thread_hashtables_[tid].add_child_time( - parent_index, region_duration); + thread_hashtables_[tid].add_child_time( + parent_index, region_duration, + parent_overhead_time_ptr); } // Increment profiler calls, and get a reference to the total overhead time. - auto& total_overhead_time = thread_hashtables_[tid].increment_profiler_calls(); + thread_hashtables_[tid].add_profiler_call(profiler_overhead_time_ptr); // Decrement index to last entry in the traceback. --call_depth; @@ -235,10 +237,12 @@ void Profiler::stop(size_t const hash) auto calliper_stop_time = prof_gettime(); auto calliper_time = calliper_stop_time - temp_sum; - // Increment the overhead time both the parent and total overhead times, using - // previously-obtained pointers or references, as appropriate. - if(parent_overhead_time_ptr){ *parent_overhead_time_ptr += calliper_time; } - total_overhead_time += calliper_time; + // Increment the overhead time specific to this region, incurred when calling + // direct children, and also the overall profiling overhead time. + // Being outside the stop calliper, these operations need to be as cheap + // as possible. + if(parent_overhead_time_ptr) { *parent_overhead_time_ptr += calliper_time; } + *profiler_overhead_time_ptr += calliper_time; } /** From b128a8d5516204d490243ad9921713e536211a2c Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Sat, 13 May 2023 08:59:37 +0100 Subject: [PATCH 115/128] Remove extraneous hash of __profiler__. --- src/c++/hashtable.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 27c507fb..79dde995 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -26,7 +26,6 @@ HashTable::HashTable(int const tid) // Set the name and hash of the profiler entry. std::string const profiler_name = "__profiler__@" + std::to_string(tid); - profiler_hash_ = hash_function_(profiler_name); // Insert special entry for the profiler overhead time. query_insert(profiler_name, profiler_hash_, profiler_index_); From 928c5deb933212dee98c5c45aebcd10d5d6fe189 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 17 May 2023 15:30:05 +0100 Subject: [PATCH 116/128] add_child_time -> add_child_time_to_parent. --- src/c++/hashtable.cpp | 6 +++--- src/c++/hashtable.h | 2 +- src/c++/profiler.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 79dde995..1df8ef93 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -91,12 +91,12 @@ void HashTable::update(record_index_t const record_index, time_duration_t const * incurred by calling children of this region. */ -void HashTable::add_child_time( - record_index_t const record_index, +void HashTable::add_child_time_to_parent( + record_index_t const parent_index, time_duration_t const child_walltime, time_duration_t*& overhead_time_ptr) { - auto& record = hashvec_[record_index]; + auto& record = hashvec_[parent_index]; record.child_walltime_ += child_walltime; overhead_time_ptr = &record.overhead_walltime_; } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 30489ec0..b2349bfc 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -92,7 +92,7 @@ class HashTable{ // Member functions std::vector list_keys(); - void add_child_time(record_index_t const, time_duration_t const, time_duration_t*&); + void add_child_time_to_parent(record_index_t const, time_duration_t const, time_duration_t*&); void add_profiler_call(time_duration_t*&); void compute_self_times(); diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index f40b0617..606596ee 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -222,7 +222,7 @@ void Profiler::stop(size_t const hash) if (call_depth > 0){ auto parent_depth = static_cast(call_depth-1); record_index_t parent_index = thread_traceback_[tid].at(parent_depth).record_index_; - thread_hashtables_[tid].add_child_time( + thread_hashtables_[tid].add_child_time_to_parent( parent_index, region_duration, parent_overhead_time_ptr); } From ece55a73579234bf4599bb16867959dc4f3c1bdd Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 17 May 2023 17:10:23 +0100 Subject: [PATCH 117/128] reference -> pointer in comments in one place. --- src/c++/profiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 606596ee..cd6b88c5 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -227,7 +227,7 @@ void Profiler::stop(size_t const hash) parent_overhead_time_ptr); } - // Increment profiler calls, and get a reference to the total overhead time. + // Increment profiler calls, and get a pointer to the total overhead time. thread_hashtables_[tid].add_profiler_call(profiler_overhead_time_ptr); // Decrement index to last entry in the traceback. From b4f019a493e8451d48dff3c78d73820bed36f9f7 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 26 May 2023 10:20:20 +0100 Subject: [PATCH 118/128] Correct handling of total times with recursions Accumulates double-counted time and subtracts that away before output. --- src/c++/formatter.cpp | 2 +- src/c++/hashtable.cpp | 16 ++++++++++++++++ src/c++/hashtable.h | 4 ++++ src/c++/hashvec.cpp | 12 +++++++----- src/c++/hashvec.h | 2 ++ src/c++/profiler.cpp | 3 +++ 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/c++/formatter.cpp b/src/c++/formatter.cpp index 24ee3a2a..2c0da929 100644 --- a/src/c++/formatter.cpp +++ b/src/c++/formatter.cpp @@ -184,7 +184,7 @@ void Formatter::drhook(std::ofstream& os, hashvec_t hashvec) << std::setw(7) << std::right << percent_time << std::setw(13) << std::right << cumul_walltime.count() << std::setw(13) << std::right << record.self_walltime_.count() - << std::setw(13) << std::right << record.total_walltime_.count() + << std::setw(13) << std::right << record.total_raw_walltime_.count() << std::setw(15) << std::right << record.call_count_ << std::setw(12) << std::right << self_per_call << std::setw(12) << std::right << total_per_call << " " diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 5be01560..ca1e017e 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -152,12 +152,27 @@ void HashTable::update(record_index_t const record_index, // Increment the walltime for this hash entry. record.total_walltime_ += time_delta; + if (record.recursion_level_ > 0){ + record.recursion_total_walltime_ += time_delta; + } // Update the number of times this region has been called ++record.call_count_; } +void HashTable::increment_recursion_level(record_index_t const record_index) +{ + auto& record = hashvec_[record_index]; + ++record.recursion_level_; +} + +void HashTable::decrement_recursion_level(record_index_t const record_index) +{ + auto& record = hashvec_[record_index]; + --record.recursion_level_; +} + /** * @brief Add in time spent calling child regions. Also retuns a pointer * to the overhead time so that it can be incremented downstream, @@ -231,6 +246,7 @@ void HashTable::prepare_computed_times(RegionRecord& record) // Total walltime with overheads attributed to the parent removed. record.total_raw_walltime_ = record.total_walltime_ + - record.recursion_total_walltime_ - record.overhead_walltime_; } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index 56ab658c..c362347f 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -108,6 +108,10 @@ class HashTable{ std::string get_decorated_region_name(size_t const hash) const; unsigned long long int get_call_count(size_t const hash) const; unsigned long long int get_prof_call_count() const; + + void increment_recursion_level(record_index_t const); + void decrement_recursion_level(record_index_t const); + }; #endif diff --git a/src/c++/hashvec.cpp b/src/c++/hashvec.cpp index 7b41cc65..5aebcbdd 100644 --- a/src/c++/hashvec.cpp +++ b/src/c++/hashvec.cpp @@ -17,12 +17,14 @@ RegionRecord::RegionRecord(size_t const region_hash, int tid) : region_hash_(region_hash) , region_name_(region_name) -, total_walltime_ (time_duration_t::zero()) -, total_raw_walltime_ (time_duration_t::zero()) -, self_walltime_ (time_duration_t::zero()) -, child_walltime_ (time_duration_t::zero()) -, overhead_walltime_ (time_duration_t::zero()) +, total_walltime_ (time_duration_t::zero()) +, recursion_total_walltime_ (time_duration_t::zero()) +, total_raw_walltime_ (time_duration_t::zero()) +, self_walltime_ (time_duration_t::zero()) +, child_walltime_ (time_duration_t::zero()) +, overhead_walltime_ (time_duration_t::zero()) , call_count_(0) +, recursion_level_(0) { decorated_region_name_ = region_name_; decorated_region_name_ += '@'; diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 6e29acf2..3f58e6fd 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -38,11 +38,13 @@ struct RegionRecord{ std::string region_name_; std::string decorated_region_name_; time_duration_t total_walltime_; + time_duration_t recursion_total_walltime_; time_duration_t total_raw_walltime_; time_duration_t self_walltime_; time_duration_t child_walltime_; time_duration_t overhead_walltime_; unsigned long long int call_count_; + int recursion_level_; }; diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index 52fc348e..e890c66a 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -138,6 +138,8 @@ size_t Profiler::start_part2(std::string_view const region_name) size_t hash; record_index_t record_index; thread_hashtables_[tid].query_insert(region_name, tid_int, hash, record_index); + thread_hashtables_[tid].increment_recursion_level(record_index); + // Store the calliper and region start times. ++call_depth; if (call_depth < PROF_MAX_TRACEBACK_SIZE){ @@ -197,6 +199,7 @@ void Profiler::stop(size_t const hash) auto region_duration = region_stop_time - traceback_entry.region_start_time_; // Do the hashtable update for the child region. + thread_hashtables_[tid].decrement_recursion_level(traceback_entry.record_index_); thread_hashtables_[tid].update(traceback_entry.record_index_, region_duration); // Precompute times as far as possible. We just need the calliper stop time From 4dcc912abb87411b5bbf841f2ee081d86dbe1973 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 31 May 2023 22:10:38 +0100 Subject: [PATCH 119/128] File-scope variables become static data members. --- src/c++/profiler.cpp | 46 +++++++++++++------------------------------- src/c++/profiler.h | 6 ++++++ src/c/CMakeLists.txt | 5 ++++- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp index cd6b88c5..0ec47138 100644 --- a/src/c++/profiler.cpp +++ b/src/c++/profiler.cpp @@ -12,29 +12,9 @@ #include #include -// Define threadprivate variables. -// `extern` keywords in the code below represent a workaround for a GNU compiler -// bug. Formally, the `threadprivate` pragma ought to be *after* the variable -// declaration. GNU does not allow this at present. The `extern` is merely a -// portable way of getting around this, with the unwanted side effect of -// introducing external linkage. The anonymous namespace removes that. -namespace{ - // `logged_calliper_start_time` is needed for the Fortran interface. - // Noting that: - // (i) the start region procedure must be separated into two parts, and - // (ii) the time point is an instance of a C++ class, - // we avoid passing time objects into other interface layers by declaring - // storage here. - extern time_point_t logged_calliper_start_time; -#pragma omp threadprivate(logged_calliper_start_time) - time_point_t logged_calliper_start_time; - - // The call depth must be stored separately for all threads. Important to - // initialise it here, so that it's initialised correctly on all threads. - extern int call_depth; -#pragma omp threadprivate(call_depth) - int call_depth = -1; -} +// Initialize static data members. +int Profiler::call_depth_ = -1; +time_point_t Profiler::logged_calliper_start_time_{}; /** * @brief Constructor for TracebackEntry struct. @@ -114,7 +94,7 @@ size_t Profiler::start(std::string_view const region_name) void Profiler::start_part1() { // Store the calliper start time, which is used in part2. - logged_calliper_start_time = prof_gettime(); + logged_calliper_start_time_ = prof_gettime(); } /** @@ -146,12 +126,12 @@ size_t Profiler::start_part2(std::string_view const region_name) thread_hashtables_[tid].query_insert(new_region_name, hash, record_index); // Store the calliper and region start times. - ++call_depth; - if (call_depth < PROF_MAX_TRACEBACK_SIZE){ - auto call_depth_index = static_cast(call_depth); + ++call_depth_; + if (call_depth_ < PROF_MAX_TRACEBACK_SIZE){ + auto call_depth_index = static_cast(call_depth_); auto region_start_time = prof_gettime(); thread_traceback_[tid].at(call_depth_index) - = TracebackEntry(hash, record_index, region_start_time, logged_calliper_start_time); + = TracebackEntry(hash, record_index, region_start_time, logged_calliper_start_time_); } else { std::cerr << "EMERGENCY STOP: Traceback array exhausted." << "\n"; @@ -184,13 +164,13 @@ void Profiler::stop(size_t const hash) // Check that we have called a start calliper before the stop calliper. // If not, then the call depth would be -1. - if (call_depth < 0) { + if (call_depth_ < 0) { std::cerr << "EMERGENCY STOP: stop called before start calliper." << "\n"; exit (101); } // Get reference to the traceback entry. - auto call_depth_index = static_cast(call_depth); + auto call_depth_index = static_cast(call_depth_); auto& traceback_entry = thread_traceback_[tid].at(call_depth_index); // Check: which hash is last on the traceback list? @@ -219,8 +199,8 @@ void Profiler::stop(size_t const hash) time_duration_t* profiler_overhead_time_ptr = nullptr; // Acquire parent pointers - if (call_depth > 0){ - auto parent_depth = static_cast(call_depth-1); + if (call_depth_ > 0){ + auto parent_depth = static_cast(call_depth_-1); record_index_t parent_index = thread_traceback_[tid].at(parent_depth).record_index_; thread_hashtables_[tid].add_child_time_to_parent( parent_index, region_duration, @@ -231,7 +211,7 @@ void Profiler::stop(size_t const hash) thread_hashtables_[tid].add_profiler_call(profiler_overhead_time_ptr); // Decrement index to last entry in the traceback. - --call_depth; + --call_depth_; // Account for time spent in the profiler itself. auto calliper_stop_time = prof_gettime(); diff --git a/src/c++/profiler.h b/src/c++/profiler.h index 88bc387d..c1ff9916 100644 --- a/src/c++/profiler.h +++ b/src/c++/profiler.h @@ -67,6 +67,12 @@ class Profiler // Data members int max_threads_; + // Static, threadprivate data members + static time_point_t logged_calliper_start_time_; + static int call_depth_; + #pragma omp threadprivate(call_depth_, logged_calliper_start_time_) + + // Hashtables and tracebacks std::vector thread_hashtables_; std::vector> thread_traceback_; diff --git a/src/c/CMakeLists.txt b/src/c/CMakeLists.txt index bc2fd43d..49396c87 100644 --- a/src/c/CMakeLists.txt +++ b/src/c/CMakeLists.txt @@ -11,7 +11,10 @@ add_library(${CMAKE_PROJECT_NAME}_c profiler_c.cpp) # Link library to and external libs (also use project warnings and options). -target_link_libraries(${CMAKE_PROJECT_NAME}_c PRIVATE profiler) +target_link_libraries(${CMAKE_PROJECT_NAME}_c PRIVATE + OpenMP::OpenMP_CXX + profiler + ) set_project_warnings(${CMAKE_PROJECT_NAME}_c) From 78ca0844519e5be85aaa66bde3f0ee0286a556aa Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 1 Jun 2023 09:54:13 +0100 Subject: [PATCH 120/128] Overloaded const and non-const hash2record. (Can have same name.) --- src/c++/hashtable.cpp | 14 +++++++------- src/c++/hashtable.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 1df8ef93..ecaa47dc 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -263,7 +263,7 @@ void HashTable::sync_lookup() double HashTable::get_total_walltime(size_t const hash) const { - auto& record = hash2record_const(hash); + auto& record = hash2record(hash); return record.total_walltime_.count(); } @@ -291,7 +291,7 @@ double HashTable::get_total_raw_walltime(size_t const hash) double HashTable::get_overhead_walltime(size_t const hash) const { - auto& record = hash2record_const(hash); + auto& record = hash2record(hash); return record.overhead_walltime_.count(); } @@ -318,7 +318,7 @@ double HashTable::get_self_walltime(size_t const hash) double HashTable::get_child_walltime(size_t const hash) const { - auto& record = hash2record_const(hash); + auto& record = hash2record(hash); return record.child_walltime_.count(); } @@ -329,7 +329,7 @@ double HashTable::get_child_walltime(size_t const hash) const std::string HashTable::get_region_name(size_t const hash) const { - auto& record = hash2record_const(hash); + auto& record = hash2record(hash); return record.region_name_; } @@ -345,7 +345,7 @@ std::string HashTable::get_region_name(size_t const hash) const unsigned long long int HashTable::get_call_count(size_t const hash) const { - auto& record = hash2record_const(hash); + auto& record = hash2record(hash); return record.call_count_; } @@ -359,7 +359,7 @@ unsigned long long int HashTable::get_call_count(size_t const hash) const unsigned long long int HashTable::get_prof_call_count() const { - auto& record = hash2record_const(profiler_hash_); + auto& record = hash2record(profiler_hash_); assert (lookup_table_.count(profiler_hash_) > 0); return record.call_count_; } @@ -384,7 +384,7 @@ RegionRecord& HashTable::hash2record(size_t const hash) * */ -RegionRecord const& HashTable::hash2record_const(size_t const hash) const +RegionRecord const& HashTable::hash2record(size_t const hash) const { return hashvec_[lookup_table_.at(hash)]; } diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h index b2349bfc..a71561ea 100644 --- a/src/c++/hashtable.h +++ b/src/c++/hashtable.h @@ -77,7 +77,7 @@ class HashTable{ void erase_record(size_t const); void sync_lookup(); RegionRecord& hash2record(size_t const); - RegionRecord const& hash2record_const(size_t const) const; + RegionRecord const& hash2record(size_t const) const; public: From 807b39a037df17aa233da83e385663e96248557d Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 2 Jun 2023 13:56:02 +0100 Subject: [PATCH 121/128] Add recursion test; handle recursion_total_time_ slightly differently. --- src/c++/hashtable.cpp | 11 ++- tests/unit_tests/c++/CMakeLists.txt | 1 + tests/unit_tests/c++/test_recursion.cpp | 119 ++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 tests/unit_tests/c++/test_recursion.cpp diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index bc348356..b09152ca 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -150,11 +150,14 @@ void HashTable::update(record_index_t const record_index, auto& record = hashvec_[record_index]; - // Increment the walltime for this hash entry. - record.total_walltime_ += time_delta; + // Increment the walltime for this hash entry. If this region has been called + // recursively, directly or indirectly, the time goes into a different bucket. if (record.recursion_level_ > 0){ record.recursion_total_walltime_ += time_delta; } + else{ + record.total_walltime_ += time_delta; + } // Update the number of times this region has been called ++record.call_count_; @@ -233,7 +236,7 @@ void HashTable::sort_records() * @details Times computed are: the region self time and the total time minus * directly incurred profiling overhead costs. * - * @param [in] record The region record to compute. + * @param [inout] record The region record to compute. */ void HashTable::prepare_computed_times(RegionRecord& record) @@ -241,12 +244,12 @@ void HashTable::prepare_computed_times(RegionRecord& record) // Self time record.self_walltime_ = record.total_walltime_ + + record.recursion_total_walltime_ - record.child_walltime_ - record.overhead_walltime_; // Total walltime with overheads attributed to the parent removed. record.total_raw_walltime_ = record.total_walltime_ - - record.recursion_total_walltime_ - record.overhead_walltime_; } diff --git a/tests/unit_tests/c++/CMakeLists.txt b/tests/unit_tests/c++/CMakeLists.txt index 9d48063d..2e87f74b 100644 --- a/tests/unit_tests/c++/CMakeLists.txt +++ b/tests/unit_tests/c++/CMakeLists.txt @@ -25,3 +25,4 @@ add_unit_test(test_regionname test_regionname.cpp) add_unit_test(test_hashtable test_hashtable.cpp) add_unit_test(test_proftests test_proftests.cpp) add_unit_test(test_callcount test_callcount.cpp) +add_unit_test(test_recursion test_recursion.cpp) diff --git a/tests/unit_tests/c++/test_recursion.cpp b/tests/unit_tests/c++/test_recursion.cpp new file mode 100644 index 00000000..96733f2e --- /dev/null +++ b/tests/unit_tests/c++/test_recursion.cpp @@ -0,0 +1,119 @@ +/*----------------------------------------------------------------------------*\ + (c) Crown copyright 2023 Met Office. All rights reserved. + The file LICENCE, distributed with this code, contains details of the terms + under which the code may be used. +\*----------------------------------------------------------------------------*/ + +#include +#include + +#include "profiler.h" + +int const max_depth = 3; +int const sleep_seconds = 1; + +// ------------------------------------------------------------------------------ +// File scope variables +// ------------------------------------------------------------------------------ + +struct Timings +{ + static double first_function_total_time ; + static double second_function_total_time; +}; + +double Timings::first_function_total_time = 0.0; +double Timings::second_function_total_time = 0.0; + +// ------------------------------------------------------------------------------ +// Forward declarations +// ------------------------------------------------------------------------------ + +void first_function(Timings&); +void second_function(Timings&); + +// ------------------------------------------------------------------------------ +// First test function +// ------------------------------------------------------------------------------ + +void first_function(Timings& timings) +{ + + static int recursion_depth = 1; +#pragma omp threadprivate(recursion_depth) + + auto prof_handle = prof.start("first_function"); + + sleep(sleep_seconds); + ++recursion_depth; + + if (recursion_depth <= max_depth) + { + second_function(timings); + } + + prof.stop(prof_handle); + + int const tid = omp_get_thread_num(); + timings.first_function_total_time = prof.get_total_walltime(prof_handle, tid); + +} + +// ------------------------------------------------------------------------------ +// Second test function +// ------------------------------------------------------------------------------ + +void second_function(Timings& timings) +{ + static int recursion_depth = 1; +#pragma omp threadprivate(recursion_depth) + + auto prof_handle = prof.start("second_function"); + + sleep(sleep_seconds); + ++recursion_depth; + + if (recursion_depth <= max_depth) + { + first_function(timings); + } + + prof.stop(prof_handle); + + int const tid = omp_get_thread_num(); + timings.second_function_total_time = prof.get_total_walltime(prof_handle, tid); +} + +// ------------------------------------------------------------------------------- +// Main test +// ------------------------------------------------------------------------------- + +TEST(RecursionTest,IndirectRecursion) +{ + auto prof_handle = prof.start("test_recursion"); + + // Test indepedently on each thread. +#pragma omp parallel + { + Timings timings; + double t1 = omp_get_wtime(); + + auto prof_handle_threaded = prof.start("test_recursion:threads"); + first_function(timings); + + double t2 = omp_get_wtime(); + double overall_time = t2-t1; + double constexpr time_tolerance = 0.0005; + + EXPECT_LE (timings.first_function_total_time, overall_time); + EXPECT_NEAR(timings.first_function_total_time, overall_time, time_tolerance); + EXPECT_NEAR(timings.second_function_total_time, timings.first_function_total_time - sleep_seconds, time_tolerance); + + prof.stop(prof_handle_threaded); + } + + prof.stop(prof_handle); +} + + + From a1e1170b32d8b2c3780026ed89d10d71eee022e3 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 23 Aug 2023 15:12:32 +0100 Subject: [PATCH 122/128] Swap recursion level to unsigned int. --- src/c++/hashvec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c++/hashvec.h b/src/c++/hashvec.h index 02d75a52..bc67f57d 100644 --- a/src/c++/hashvec.h +++ b/src/c++/hashvec.h @@ -47,7 +47,7 @@ struct RegionRecord { time_duration_t child_walltime_; time_duration_t overhead_walltime_; unsigned long long int call_count_; - int recursion_level_; + unsigned int recursion_level_; }; From 339e75d3cfcf8859b65d450f44e5418d98499a1a Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:54:44 +0100 Subject: [PATCH 123/128] Add direct recursion test. Slacken time tolerance. --- tests/unit_tests/c++/test_recursion.cpp | 92 ++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/tests/unit_tests/c++/test_recursion.cpp b/tests/unit_tests/c++/test_recursion.cpp index 314be5f3..ddec7ebc 100644 --- a/tests/unit_tests/c++/test_recursion.cpp +++ b/tests/unit_tests/c++/test_recursion.cpp @@ -9,19 +9,27 @@ #include "vernier.h" -int const max_depth = 3; -int const sleep_seconds = 1; +int const max_depth = 3; +int const sleep_seconds = 1; + +// The time tolerance can be reasonably loose. If the total times were incorrect +// as a result of mis-handled recursion, they would be too large by multiples of +// sleep_seconds. +double const time_tolerance = 0.01; // ------------------------------------------------------------------------------ // File scope variables // ------------------------------------------------------------------------------ +// Structure To hold timings from the various recursive functions. struct Timings { - static double first_function_total_time ; + static double zeroth_function_total_time; + static double first_function_total_time; static double second_function_total_time; }; +double Timings::zeroth_function_total_time = 0.0; double Timings::first_function_total_time = 0.0; double Timings::second_function_total_time = 0.0; @@ -29,11 +37,40 @@ double Timings::second_function_total_time = 0.0; // Forward declarations // ------------------------------------------------------------------------------ -void first_function(Timings&); +void zeroth_function(Timings&); +void first_function (Timings&); void second_function(Timings&); // ------------------------------------------------------------------------------ -// First test function +// Zeroth function - calls itself +// ------------------------------------------------------------------------------ + +void zeroth_function(Timings& timings) +{ + + static int recursion_depth = 1; +#pragma omp threadprivate(recursion_depth) + + auto prof_handle = meto::vernier.start("first_function"); + + sleep(sleep_seconds); + ++recursion_depth; + + if (recursion_depth <= max_depth) + { + zeroth_function(timings); + } + + meto::vernier.stop(prof_handle); + + // Update the total walltime so far spent in this function. + int const tid = omp_get_thread_num(); + timings.zeroth_function_total_time = meto::vernier.get_total_walltime(prof_handle, tid); + +} + +// ------------------------------------------------------------------------------ +// First function - calls the second function. // ------------------------------------------------------------------------------ void first_function(Timings& timings) @@ -54,13 +91,14 @@ void first_function(Timings& timings) meto::vernier.stop(prof_handle); + // Update the total walltime so far spent in this function. int const tid = omp_get_thread_num(); timings.first_function_total_time = meto::vernier.get_total_walltime(prof_handle, tid); } // ------------------------------------------------------------------------------ -// Second test function +// Second function - calls the first function. // ------------------------------------------------------------------------------ void second_function(Timings& timings) @@ -85,25 +123,61 @@ void second_function(Timings& timings) } // ------------------------------------------------------------------------------- -// Main test +// Main tests // ------------------------------------------------------------------------------- +// +// Direct recursion +// + +TEST(RecursionTest,DirectRecursion) +{ + auto prof_handle = meto::vernier.start("test_recursion"); + + // Test independently on each thread. +#pragma omp parallel + { + Timings timings; + double t1 = omp_get_wtime(); + + auto prof_handle_threaded = meto::vernier.start("test_recursion:threads"); + + // Function calls itself + zeroth_function(timings); + + double t2 = omp_get_wtime(); + double overall_time = t2-t1; + + EXPECT_LE (timings.zeroth_function_total_time, overall_time); + EXPECT_NEAR(timings.zeroth_function_total_time, overall_time, time_tolerance); + + meto::vernier.stop(prof_handle_threaded); + } + + meto::vernier.stop(prof_handle); +} + +// +// Indirect recursion +// + TEST(RecursionTest,IndirectRecursion) { auto prof_handle = meto::vernier.start("test_recursion"); - // Test indepedently on each thread. + // Test independently on each thread. #pragma omp parallel { Timings timings; double t1 = omp_get_wtime(); auto prof_handle_threaded = meto::vernier.start("test_recursion:threads"); + + // Function calls a second function first_function(timings); double t2 = omp_get_wtime(); double overall_time = t2-t1; - double constexpr time_tolerance = 0.0005; EXPECT_LE (timings.first_function_total_time, overall_time); EXPECT_NEAR(timings.first_function_total_time, overall_time, time_tolerance); From d85d26a3a17765cef4abaff7b68314b649d7e3cb Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Wed, 23 Aug 2023 17:03:21 +0100 Subject: [PATCH 124/128] Remove extraneous spaces. --- tests/unit_tests/c++/test_recursion.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/c++/test_recursion.cpp b/tests/unit_tests/c++/test_recursion.cpp index ddec7ebc..4deb8d63 100644 --- a/tests/unit_tests/c++/test_recursion.cpp +++ b/tests/unit_tests/c++/test_recursion.cpp @@ -9,8 +9,8 @@ #include "vernier.h" -int const max_depth = 3; -int const sleep_seconds = 1; +int const max_depth = 3; +int const sleep_seconds = 1; // The time tolerance can be reasonably loose. If the total times were incorrect // as a result of mis-handled recursion, they would be too large by multiples of From c5046c4592111b4422cf6c68719d969dfa46e630 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:37:15 +0100 Subject: [PATCH 125/128] Make test_recursion threadsafe. --- tests/unit_tests/c++/test_recursion.cpp | 45 ++++++++++++------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/unit_tests/c++/test_recursion.cpp b/tests/unit_tests/c++/test_recursion.cpp index 4deb8d63..e85ac0f7 100644 --- a/tests/unit_tests/c++/test_recursion.cpp +++ b/tests/unit_tests/c++/test_recursion.cpp @@ -15,24 +15,20 @@ int const sleep_seconds = 1; // The time tolerance can be reasonably loose. If the total times were incorrect // as a result of mis-handled recursion, they would be too large by multiples of // sleep_seconds. -double const time_tolerance = 0.01; +double const time_tolerance = 0.001; // ------------------------------------------------------------------------------ -// File scope variables +// Structs // ------------------------------------------------------------------------------ // Structure To hold timings from the various recursive functions. struct Timings { - static double zeroth_function_total_time; - static double first_function_total_time; - static double second_function_total_time; + double zeroth_function_total_time_ = 0.0; + double first_function_total_time_ = 0.0; + double second_function_total_time_ = 0.0; }; -double Timings::zeroth_function_total_time = 0.0; -double Timings::first_function_total_time = 0.0; -double Timings::second_function_total_time = 0.0; - // ------------------------------------------------------------------------------ // Forward declarations // ------------------------------------------------------------------------------ @@ -65,7 +61,7 @@ void zeroth_function(Timings& timings) // Update the total walltime so far spent in this function. int const tid = omp_get_thread_num(); - timings.zeroth_function_total_time = meto::vernier.get_total_walltime(prof_handle, tid); + timings.zeroth_function_total_time_ = meto::vernier.get_total_walltime(prof_handle, tid); } @@ -91,9 +87,10 @@ void first_function(Timings& timings) meto::vernier.stop(prof_handle); - // Update the total walltime so far spent in this function. + // Update the total walltime so far spent in this function. Do here while we + // have access to the prof_handle. int const tid = omp_get_thread_num(); - timings.first_function_total_time = meto::vernier.get_total_walltime(prof_handle, tid); + timings.first_function_total_time_ = meto::vernier.get_total_walltime(prof_handle, tid); } @@ -118,8 +115,10 @@ void second_function(Timings& timings) meto::vernier.stop(prof_handle); + // Update the total walltime so far spent in this function. Do here while we + // have access to the prof_handle. int const tid = omp_get_thread_num(); - timings.second_function_total_time = meto::vernier.get_total_walltime(prof_handle, tid); + timings.second_function_total_time_ = meto::vernier.get_total_walltime(prof_handle, tid); } // ------------------------------------------------------------------------------- @@ -137,19 +136,19 @@ TEST(RecursionTest,DirectRecursion) // Test independently on each thread. #pragma omp parallel { + auto prof_handle_threaded = meto::vernier.start("test_recursion:threads"); + Timings timings; double t1 = omp_get_wtime(); - auto prof_handle_threaded = meto::vernier.start("test_recursion:threads"); - // Function calls itself zeroth_function(timings); double t2 = omp_get_wtime(); double overall_time = t2-t1; - EXPECT_LE (timings.zeroth_function_total_time, overall_time); - EXPECT_NEAR(timings.zeroth_function_total_time, overall_time, time_tolerance); + EXPECT_LE (timings.zeroth_function_total_time_, overall_time); + EXPECT_NEAR(timings.zeroth_function_total_time_, overall_time, time_tolerance); meto::vernier.stop(prof_handle_threaded); } @@ -168,20 +167,20 @@ TEST(RecursionTest,IndirectRecursion) // Test independently on each thread. #pragma omp parallel { + auto prof_handle_threaded = meto::vernier.start("test_recursion:threads"); + Timings timings; double t1 = omp_get_wtime(); - auto prof_handle_threaded = meto::vernier.start("test_recursion:threads"); - // Function calls a second function first_function(timings); double t2 = omp_get_wtime(); double overall_time = t2-t1; - - EXPECT_LE (timings.first_function_total_time, overall_time); - EXPECT_NEAR(timings.first_function_total_time, overall_time, time_tolerance); - EXPECT_NEAR(timings.second_function_total_time, timings.first_function_total_time - sleep_seconds, time_tolerance); + + EXPECT_LE (timings.first_function_total_time_, overall_time); + EXPECT_NEAR(timings.first_function_total_time_, overall_time, time_tolerance); + EXPECT_NEAR(timings.second_function_total_time_, timings.first_function_total_time_ - sleep_seconds, time_tolerance); meto::vernier.stop(prof_handle_threaded); } From 15aa1bda3c85eeb7d1b4a9158c6677341c08226c Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 25 Aug 2023 11:46:16 +0100 Subject: [PATCH 126/128] Add doxygen comments for recursion increment / decrement. --- src/c++/hashtable.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp index 868c9593..22b60bcd 100644 --- a/src/c++/hashtable.cpp +++ b/src/c++/hashtable.cpp @@ -163,12 +163,22 @@ void meto::HashTable::update(record_index_t const record_index, } +/** + * @brief Increments by 1 the recursion level in a region record. + * @param [in] record_index The index corresponding to the region record. + */ + void meto::HashTable::increment_recursion_level(record_index_t const record_index) { auto& record = hashvec_[record_index]; ++record.recursion_level_; } +/** + * @brief Decrements by 1 the recursion level in a region record. + * @param [in] record_index The index corresponding to the region record. + */ + void meto::HashTable::decrement_recursion_level(record_index_t const record_index) { auto& record = hashvec_[record_index]; From 341b88a3dca765ee2f3e86bdafd08fcd5b3f482f Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 25 Aug 2023 11:50:57 +0100 Subject: [PATCH 127/128] Remove forward declarations in test_recursion. --- tests/unit_tests/c++/test_recursion.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/unit_tests/c++/test_recursion.cpp b/tests/unit_tests/c++/test_recursion.cpp index e85ac0f7..18e7262c 100644 --- a/tests/unit_tests/c++/test_recursion.cpp +++ b/tests/unit_tests/c++/test_recursion.cpp @@ -29,14 +29,6 @@ struct Timings double second_function_total_time_ = 0.0; }; -// ------------------------------------------------------------------------------ -// Forward declarations -// ------------------------------------------------------------------------------ - -void zeroth_function(Timings&); -void first_function (Timings&); -void second_function(Timings&); - // ------------------------------------------------------------------------------ // Zeroth function - calls itself // ------------------------------------------------------------------------------ From b767e34f8eaf5f9eb2100dc82ec57aa68a36ee12 Mon Sep 17 00:00:00 2001 From: Maff Glover <78152252+mo-mglover@users.noreply.github.com> Date: Fri, 25 Aug 2023 12:02:22 +0100 Subject: [PATCH 128/128] Reinstate one forward declaration. --- tests/unit_tests/c++/test_recursion.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit_tests/c++/test_recursion.cpp b/tests/unit_tests/c++/test_recursion.cpp index 18e7262c..0f0702e0 100644 --- a/tests/unit_tests/c++/test_recursion.cpp +++ b/tests/unit_tests/c++/test_recursion.cpp @@ -29,6 +29,13 @@ struct Timings double second_function_total_time_ = 0.0; }; +// ------------------------------------------------------------------------------ +// Forward declarations +// ------------------------------------------------------------------------------ + +// Needed to escape circular dependence on function declarations. +void second_function(Timings&); + // ------------------------------------------------------------------------------ // Zeroth function - calls itself // ------------------------------------------------------------------------------