diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f92994c7..3bff2012 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,6 @@
name: Build and Test
on:
- workflow_dispatch:
push:
branches: [ main ]
pull_request:
@@ -58,12 +57,12 @@ jobs:
env:
OMP_NUM_THREADS: 1
working-directory: ${{github.workspace}}/build
- run: ctest -VV -C ${{env.BUILD_TYPE}}
+ run: ctest -C ${{env.BUILD_TYPE}}
# Run tests on 4 threads
- name: "Test (4 threads)"
env:
OMP_NUM_THREADS: 4
working-directory: ${{github.workspace}}/build
- run: ctest -VV -C ${{env.BUILD_TYPE}}
+ run: ctest -C ${{env.BUILD_TYPE}}
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 836a708a..4d3ca3d8 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -1,7 +1,6 @@
name: Deploy Docs
on:
- workflow_dispatch:
push:
branches: [ main ]
paths:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c80e042b..6a8d6619 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,8 +20,4 @@
15/07/2022 PR #30 for #29: Unit testing for Fortran interface. \
04/08/2022 PR #32 for #31: Functionality improvements (walltime, swap to unordered_map, sort entries in output. \
10/08/2022 PR #41 for #37 Add null terminated strings in Fortran interface. \
-16/08/2022 PR #48 Add hashtable.h to installed header files. \
-30/08/2022 PR #51 for #34: Including the number of times a region is called. \
-09/09/2022 PR #39 for #35: Initial profiler unit tests. \
-08/09/2022 PR #55 for #33: Replace omp_get_wtime(). (Chrono) \
-13/09/2022 PR #60 for #59: Allow manual triggering of workflows.
+16/08/2022 PR #48 Add hashtable.h to installed header files.
diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake
index 781f23ff..dcba7716 100644
--- a/cmake/CompilerWarnings.cmake
+++ b/cmake/CompilerWarnings.cmake
@@ -2,7 +2,7 @@
# different compilers and build settings.
function(set_project_warnings project_name)
# Create option for forcing errors for all warnings.
- option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
+ option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON)
# Create option to turn on sanitizers.
option(USE_SANITIZERS "Turn on sanitizers to help reporting of runtime errors" OFF)
diff --git a/cmake/Doxygen.cmake b/cmake/Doxygen.cmake
index 9f350a20..8e842043 100644
--- a/cmake/Doxygen.cmake
+++ b/cmake/Doxygen.cmake
@@ -21,8 +21,11 @@ function(enable_doxygen)
set(DOXYGEN_JAVADOC_BLOCK NO)
set(DOXYGEN_FULL_PATH_NAMES NO)
set(DOXYGEN_STRIP_CODE_COMMENTS NO)
- set(DOXYGEN_FILE_PATTERNS *.c *.cpp *.h *.f90 *.F90 *.md)
+ set(DOXYGEN_FILE_PATTERNS *.c *.cpp *.h *.f90 *.F90 *.md )
set(DOXYGEN_EXTENSION_MAPPING "F90=Fortran")
+ set(DOXYGEN_SORT_MEMBER_DOCS NO)
+ set(DOXYGEN_WARN_AS_ERROR NO)
+ set(DOXYGEN_LAYOUT_FILE ${PROJECT_SOURCE_DIR}/documentation/Doxygen/DoxygenLayout.xml)
set(DOXYGEN_HTML_HEADER ${PROJECT_SOURCE_DIR}/documentation/Doxygen/html/header.html)
set(DOXYGEN_HTML_FOOTER ${PROJECT_SOURCE_DIR}/documentation/Doxygen/html/footer.html)
diff --git a/documentation/Doxygen/DoxygenLayout.xml b/documentation/Doxygen/DoxygenLayout.xml
new file mode 100644
index 00000000..775b6d93
--- /dev/null
+++ b/documentation/Doxygen/DoxygenLayout.xml
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/documentation/Doxygen/Profiler.md b/documentation/Doxygen/Profiler.md
index 80c03010..922b3165 100644
--- a/documentation/Doxygen/Profiler.md
+++ b/documentation/Doxygen/Profiler.md
@@ -48,4 +48,4 @@ class A {
## Communication Efficiency {#comme}
-TBD
\ No newline at end of file
+TBD
diff --git a/src/c++/hashtable.cpp b/src/c++/hashtable.cpp
index a9f8262e..bf710ae2 100644
--- a/src/c++/hashtable.cpp
+++ b/src/c++/hashtable.cpp
@@ -13,21 +13,20 @@
#include
/**
- * @brief Constructs a new entry in the hash table.
+ * @brief Constructs a new entry in the hash table.
*
*/
HashEntry::HashEntry(std::string_view region_name)
: region_name_(region_name)
- , total_walltime_(time_duration_t::zero())
- , self_walltime_(time_duration_t::zero())
- , child_walltime_(time_duration_t::zero())
- , call_count_(0)
+ , total_walltime_(0.0)
+ , self_walltime_(0.0)
+ , child_walltime_(0.0)
{}
/**
* @brief Hashtable constructor
- *
+ *
*/
HashTable::HashTable(int const tid)
@@ -56,7 +55,7 @@ size_t HashTable::query_insert(std::string_view region_name) noexcept
*
*/
-void HashTable::update(size_t hash, time_duration_t time_delta)
+void HashTable::update(size_t hash, double time_delta)
{
// Assertions
assert (table_.size() > 0);
@@ -66,8 +65,6 @@ void HashTable::update(size_t hash, time_duration_t time_delta)
auto& entry = table_.at(hash);
entry.total_walltime_ += time_delta;
- // Update the number of times this region has been called
- entry.call_count_++;
}
/**
@@ -75,7 +72,7 @@ void HashTable::update(size_t hash, time_duration_t time_delta)
*
*/
-void HashTable::add_child_time(size_t hash, time_duration_t time_delta)
+void HashTable::add_child_time(size_t hash, double time_delta)
{
// Assertions
assert (table_.size() > 0);
@@ -103,32 +100,29 @@ void HashTable::write()
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::setw(15) << std::right << "Total (s)" << "\n";
+
std::cout << std::setfill('-');
std::cout
<< std::setw(40) << "-" << " "
<< std::setw(15) << "-" << " "
- << std::setw(15) << "-" << " "
- << std::setw(10) << "-" << "\n";
+ << std::setw(15) << "-" << "\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.
- hashvec = std::vector>(table_.cbegin(), table_.cend());
- std::sort(begin(hashvec), end(hashvec),
+ auto hashvec = std::vector>(begin(table_), end(table_));
+ 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
- << 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::cout
+ << 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";
}
}
@@ -161,78 +155,12 @@ 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) const
-{
- return table_.at(hash).total_walltime_.count();
-}
-
-/**
- * @brief Get the profiler self time corresponding to the input hash.
- *
- */
-
-double HashTable::get_self_walltime(size_t const hash)
-{
- this->compute_self_times();
- return table_.at(hash).self_walltime_.count();
-}
-
-/**
- * @brief Get the child time corresponding to the input hash.
- *
- */
-
-double HashTable::get_child_walltime(size_t const hash) const
-{
- return table_.at(hash).child_walltime_.count();
-}
-
-/**
- * @brief Get the region name corresponding to the input hash.
- *
+ *
*/
-std::string const& HashTable::get_region_name(size_t const hash) const
+double HashTable::get_total_walltime(size_t const hash)
{
- return table_.at(hash).region_name_;
+ return table_.at(hash).total_walltime_;
}
- /**
- * @brief Get the number of times the input hash region has been called.
- *
- * @param[in] hash The hash corresponding to the region of interest.
- *
- * @returns Returns an integer corresponding to the number of the times the
- * region of interest has been called within the code being profiled.
- *
- */
-
-unsigned long long int const& HashTable::get_region_call_count(size_t const hash) const
-{
- return table_.at(hash).call_count_;
-}
-/**
- * @brief Get the vector in profiler.write() which is used to sort entries in
- * the hashtable from high to low self walltime.
- *
- */
-
-std::vector> const& HashTable::get_hashvec() const
-{
- return hashvec;
-}
-
-/**
- * @brief Get the actual std::unordered_map hashtable, "table_" wherein hashes
- * and hash entries are stored.
- *
- */
-
-std::unordered_map const& HashTable::get_hashtable() const
-{
- return table_;
-}
diff --git a/src/c++/hashtable.h b/src/c++/hashtable.h
index f57c979e..2000cc80 100644
--- a/src/c++/hashtable.h
+++ b/src/c++/hashtable.h
@@ -28,12 +28,6 @@
#include
#include
#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;
-
/**
* @brief Structure to hold information for a particular routine.
@@ -50,11 +44,10 @@ struct HashEntry{
explicit HashEntry(std::string_view);
// Data members
- std::string region_name_;
- time_duration_t total_walltime_;
- time_duration_t self_walltime_;
- time_duration_t child_walltime_;
- unsigned long long int call_count_;
+ std::string region_name_;
+ double total_walltime_;
+ double self_walltime_;
+ double child_walltime_;
};
@@ -67,14 +60,13 @@ struct HashEntry{
*/
class HashTable{
-
+
private:
// Members
int tid_;
std::unordered_map table_;
std::hash hash_function_;
- std::vector> hashvec;
public:
@@ -84,22 +76,14 @@ class HashTable{
// Prototypes
size_t query_insert(std::string_view) noexcept;
- void update(size_t, time_duration_t);
+ void update(size_t, double);
void write();
// Member functions
std::vector list_keys();
- void add_child_time(size_t, time_duration_t);
+ void add_child_time(size_t, double);
void compute_self_times();
+ double get_total_walltime(size_t const);
- // Getters
- double get_total_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 const& get_region_name(size_t const hash) const;
- unsigned long long int const& get_region_call_count(size_t const hash) const;
-
- std::vector> const& get_hashvec() const;
- std::unordered_map const& get_hashtable() const;
};
#endif
diff --git a/src/c++/profiler.cpp b/src/c++/profiler.cpp
index 9ea7f317..bbe2b432 100644
--- a/src/c++/profiler.cpp
+++ b/src/c++/profiler.cpp
@@ -9,7 +9,6 @@
#include
#include
-#include
/**
* @brief Constructor
@@ -30,21 +29,16 @@ Profiler::Profiler(){
HashTable new_table(tid);
thread_hashtables_.push_back(new_table);
- std::vector> new_list;
+ std::vector> new_list;
thread_traceback_.push_back(new_list);
}
- // Assertions
+ // Assertions
assert ( static_cast (thread_hashtables_.size()) == max_threads_);
assert ( static_cast (thread_traceback_.size() ) == max_threads_);
}
-/**
- * @brief Start timing.
- *
- */
-
size_t Profiler::start(std::string_view region_name)
{
@@ -61,22 +55,17 @@ size_t Profiler::start(std::string_view region_name)
size_t const hash = thread_hashtables_[tid].query_insert(region_name);
// Add routine to the traceback.
- auto start_time = std::chrono::steady_clock::now();
+ double start_time = omp_get_wtime();
thread_traceback_[tid].push_back(std::make_pair(hash, start_time));
return hash;
}
-/**
- * @brief Stop timing.
- *
- */
-
void Profiler::stop(size_t const hash)
{
// First job: log the stop time.
- auto stop_time = std::chrono::steady_clock::now();
+ double stop_time = omp_get_wtime();
// Determine the thread number
auto tid = static_cast(0);
@@ -90,13 +79,13 @@ void Profiler::stop(size_t const hash)
// Check that the hash is the one we expect. If it isn't, there is an error in
// the instrumentation.
if (hash != last_hash_on_list){
- std::cerr << "EMERGENCY STOP: hashes don't match." << "\n";
+ std::cout << "EMERGENCY STOP: hashes don't match." << "\n";
exit (100);
}
// Increment the time for this
- auto start_time = thread_traceback_[tid].back().second;
- auto deltatime = stop_time - start_time;
+ double start_time = thread_traceback_[tid].back().second;
+ double deltatime = stop_time - start_time;
thread_hashtables_[tid].update(hash, deltatime);
// Remove from the end of the list.
@@ -110,11 +99,6 @@ void Profiler::stop(size_t const hash)
}
-/**
- * @brief Write profile information.
- *
- */
-
void Profiler::write()
{
// Write each one
@@ -125,115 +109,15 @@ void Profiler::write()
}
/**
- * @brief Get the total (inclusive) time of everything below the specified hash.
- *
- * @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
- * thread 0, the function does not take a thread ID argument and returns
- * the value for thread 0 only. Taking the hash argument avoids the need
- * to store the top-level hash inside the profiler itself.
- *
- */
-
-double Profiler::get_thread0_walltime(size_t const hash) const
+* @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
+* thread 0, the function does not take a thread ID argument and returns
+* the value for thread 0 only. Taking the hash argument avoids the need
+* to store the top-level hash inside the profiler itself.
+*/
+double Profiler::get_thread0_walltime(size_t const hash)
{
auto tid = static_cast(0);
return thread_hashtables_[tid].get_total_walltime(hash);
}
-/**
- * @brief Get the self walltime for the specified hash.
- *
- */
-
-double Profiler::get_self_walltime(size_t const hash, int const input_tid)
-{
- auto tid = static_cast(input_tid);
- return thread_hashtables_[tid].get_self_walltime(hash);
-}
-
-/**
- * @brief Get the child walltime for the specified hash.
- *
- */
-
-double Profiler::get_child_walltime(size_t const hash, int const input_tid) const
-{
- auto tid = static_cast(input_tid);
- return thread_hashtables_[tid].get_child_walltime(hash);
-}
-
-/**
- * @brief Get the region name corresponding to the input hash.
- *
- */
-
-std::string Profiler::get_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);
-}
-
-/**
- * @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.
- *
- * @returns Returns an integer corresponding to the number of times the
- * region of interest has been called on the specified thread.
- *
- */
-
-unsigned long long int Profiler::get_region_call_count(size_t const hash, int const input_tid) const
-{
- auto tid = static_cast(input_tid);
- return thread_hashtables_[tid].get_region_call_count(hash);
-}
-
-/**
- * @brief Gets the std::unordered_map "table_" hashtable.
- *
- */
-
-std::unordered_map const& Profiler::get_hashtable(int const input_tid) const
-{
- auto tid = static_cast(input_tid);
- return thread_hashtables_.at(tid).get_hashtable();
-}
-
-/**
- * @brief Gets the inner layer vector in thread_traceback_.
- *
- */
-
-std::vector> const& Profiler::get_inner_traceback_vector(int const input_tid) const
-{
- auto tid = static_cast(input_tid);
- return thread_traceback_.at(tid);
-}
-
-/**
- * @brief Gets the vector of (hash,HashEntry) pairs in Profiler.write() known as hashvec, the desired
- * behaviour of which is to sort the entries from high to low self walltime.
- *
- */
-
-std::vector> const& Profiler::get_hashvec(int const input_tid) const
-{
- auto tid = static_cast(input_tid);
- return thread_hashtables_.at(tid).get_hashvec();
-}
-
-/**
- * @brief Return the value of max_threads_
- *
- */
-
-int Profiler::get_max_threads() const
-{
- return max_threads_;
-}
diff --git a/src/c++/profiler.h b/src/c++/profiler.h
index dc4891a9..caec6353 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
@@ -25,6 +25,11 @@
#include "hashtable.h"
+/**
+ * @defgroup API C++
+ * @brief C++ API for the profiler
+ */
+
/**
* @brief Top-level profiler class.
*
@@ -35,40 +40,60 @@
class Profiler
{
- private:
+ private:
// 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_;
- typedef std::vector>>::size_type pair_iterator_t_;
+ typedef std::vector::size_type hashtable_iterator_t_;
+ typedef std::vector>>::size_type pair_iterator_t_;
public:
// Constructors
+/**
+ * @ingroup API
+ * @brief Constructor for profiler class
+ */
Profiler();
// Member functions
- size_t start(std::string_view);
- void stop (size_t const);
- void write();
- // HashEntry getters
- double get_thread0_walltime(size_t const hash) 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;
- unsigned long long int get_region_call_count(size_t const hash, int const input_tid) const;
+/**
+ * @ingroup API
+ * @brief Start timing.
+ *
+ * @param[in] region_name A unique name for the region being timed.
+ *
+ */
+ size_t start(std::string_view region_name);
- // Getters that return a constant, referenced instance of a private data member
- std::unordered_map const& get_hashtable(int const input_tid) const;
- std::vector> const& get_inner_traceback_vector(int const input_tid) const;
- std::vector> const& get_hashvec(int const input_tid) const;
- int get_max_threads() const;
+/**
+ * @ingroup API
+ * @brief Stop timing.
+ *
+ * @param[in] hash The hash corresponding to the region to be stopped.
+ */
+ void stop (size_t hash);
+/**
+ * @ingroup API
+ * @brief Write profile information.
+ *
+ */
+ void write();
+
+
+/**
+ * @ingroup API
+ * @brief Get the total (inclusive) time of everything below the specified hash.
+ *
+ * @param[in] hash The hash corresponding to the region of interest.
+ */
+ double get_thread0_walltime(size_t hash);
};
// Declare global profiler
diff --git a/src/c/profiler_c.cpp b/src/c/profiler_c.cpp
index f28e8fd6..50c63af9 100644
--- a/src/c/profiler_c.cpp
+++ b/src/c/profiler_c.cpp
@@ -13,7 +13,7 @@
* C-language interfaces are needed to call the profiler from C and Fortran.
*
* Since Fortran is pass by reference, arguments are received as references (&).
- *
+ *
*/
#include "profiler.h"
@@ -21,6 +21,11 @@
#include
#include
+
+/**
+ * @defgroup CAPI C
+ * @brief A simple C API for the profiler
+ */
extern "C" {
void c_profiler_start(long int&, char const*);
void c_profiler_stop (long int const&);
@@ -29,6 +34,7 @@ extern "C" {
}
/**
+ * @ingroup CAPI
* @brief Start timing a named region and return a unique handle.
*/
@@ -42,6 +48,7 @@ void c_profiler_start(long int& hash_out, char const* name)
}
/**
+ * @ingroup CAPI
* @brief Stop timing the region with the specified handle.
*/
@@ -57,6 +64,7 @@ void c_profiler_stop(long int const& hash_in)
}
/**
+ * @ingroup CAPI
* @brief Write the profile itself.
*/
@@ -66,7 +74,8 @@ void c_profiler_write()
}
/**
- * Get the total wallclock time for the specified region on thread 0.
+ * @ingroup CAPI
+ * @brief Get the total wallclock time for the specified region on thread 0.
*/
double c_get_thread0_walltime(long int const& hash_in)
diff --git a/src/f/profiler_mod.F90 b/src/f/profiler_mod.F90
index a7960474..66310a54 100644
--- a/src/f/profiler_mod.F90
+++ b/src/f/profiler_mod.F90
@@ -11,7 +11,6 @@
module profiler_mod
use, intrinsic :: iso_c_binding, only: c_char, c_long, c_double, c_null_char
implicit none
- private
!-----------------------------------------------------------------------------
! Public parameters
@@ -27,6 +26,8 @@ module profiler_mod
! Public interfaces / subroutines
!-----------------------------------------------------------------------------
+ !> @defgroup FortranAPI Fortran
+ !> @brief Fortran API for the profiler
public :: profiler_start
public :: profiler_stop
public :: profiler_write
@@ -38,22 +39,27 @@ module profiler_mod
interface
- subroutine interface_profiler_start(hash_out, region_name) &
- bind(C, name='c_profiler_start')
- 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
-
+ !> @ingroup FortranAPI
+ !> @fn profiler_mod::profiler_stop::profiler_stop(hash_in)
+ !> @brief Stop profiler
+ !> @param [in] hash_in The hash of the region to be stopped.
subroutine profiler_stop(hash_in) bind(C, name='c_profiler_stop')
import :: pik
integer(kind=pik), intent(in) :: hash_in
end subroutine profiler_stop
+ !> @ingroup FortranAPI
+ !> @fn profiler_mod::profiler_write::profiler_write()
+ !> @brief Write timings out for all profiling regions.
subroutine profiler_write() bind(C, name='c_profiler_write')
!No arguments to handle
end subroutine profiler_write
+ !> @ingroup FortranAPI
+ !> @fn profiler_mod::profiler_get_thread0_walltime::profiler_get_thread0_walltime(hash_in)
+ !> @brief Write profiling data out
+ !> @param [in] hash_in The hash of the region to return the time.
+ !> @returns The Walltime within the region.
function profiler_get_thread0_walltime(hash_in) result(walltime) &
bind(C, name='c_get_thread0_walltime')
import :: pik, prk
@@ -63,11 +69,22 @@ end function profiler_get_thread0_walltime
end interface
+ private
+ interface
+ subroutine interface_profiler_start(hash_out, region_name) &
+ bind(C, name='c_profiler_start')
+ 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 interface
+
!-----------------------------------------------------------------------------
! Contained functions / subroutines
!-----------------------------------------------------------------------------
contains
+ !> @ingroup FortranAPI
!> @brief Start profiling a code region.
!> @param [out] hash_out The unique hash for this region.
!> @param [in] region_name The region name.
@@ -82,7 +99,7 @@ subroutine profiler_start(hash_out, region_name)
!Local variables
character(len=len_trim(region_name)+1) :: local_region_name
-
+
local_region_name = trim(region_name) // c_null_char
call interface_profiler_start(hash_out, local_region_name)
diff --git a/tests/unit_tests/c++/CMakeLists.txt b/tests/unit_tests/c++/CMakeLists.txt
index bfe4b601..521a1b61 100644
--- a/tests/unit_tests/c++/CMakeLists.txt
+++ b/tests/unit_tests/c++/CMakeLists.txt
@@ -4,7 +4,6 @@ function(add_unit_test test_name cpp_file)
target_link_libraries(${test_name}
OpenMP::OpenMP_CXX
GTest::gtest_main
- GTest::gmock_main
${CMAKE_PROJECT_NAME})
set_project_warnings(${test_name})
target_include_directories(${test_name} PRIVATE
@@ -15,8 +14,4 @@ endfunction()
# List of unit-tests added to CTest. Add a line calling the 'add_unit_test'
# function to add an additional file of tests.
add_unit_test(test_profiler test_profiler.cpp)
-add_unit_test(test_hashtiming test_hashtiming.cpp)
-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)
+
diff --git a/tests/unit_tests/c++/test_callcount.cpp b/tests/unit_tests/c++/test_callcount.cpp
deleted file mode 100644
index 804e0a03..00000000
--- a/tests/unit_tests/c++/test_callcount.cpp
+++ /dev/null
@@ -1,67 +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 "omp.h"
-
-#include "profiler.h"
-
-TEST(HashEntryTest,CallCountTest)
-{
- // Start main region
- auto prof_main = prof.start("MainRegion");
-
- // 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;
- int num_threads = 1;
-
- // Start parallel region
-#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.
-#pragma omp single
- {
- num_threads = omp_get_num_threads();
- }
-
- // Current thread ID
- 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
- // warnings about it being potentially uninitialised when assigning to
- // prof_sub_shared.
- size_t prof_sub_private = 0;
-
- // Call a subregion a differing number of times depending on the thread ID.
- // The highest thread ID will have the fewest calls: just 1.
- for (int i = 0; i < num_threads-thread_id; ++i)
- {
- prof_sub_private = prof.start("SubRegion");
- prof.stop(prof_sub_private);
- }
-
- // Give prof_sub_shared a value for later use in EXPECT's
-#pragma omp single
- {
- prof_sub_shared = prof_sub_private;
- }
- }
-
- // Stop main region
- prof.stop(prof_main);
-
- // Check call_count_ is the number expected on all threads
- for (int j = 0; j < num_threads; ++j)
- {
- EXPECT_EQ(prof.get_region_call_count(prof_sub_shared,j),num_threads-j);
- }
-}
diff --git a/tests/unit_tests/c++/test_hashtable.cpp b/tests/unit_tests/c++/test_hashtable.cpp
deleted file mode 100644
index 81c72fa5..00000000
--- a/tests/unit_tests/c++/test_hashtable.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-#include
-#include
-#include
-#include
-
-using ::testing::AllOf;
-using ::testing::An;
-using ::testing::Gt;
-
-//
-// Testing some hashtable member variables and functions, such as query_insert()
-// and the std::vector thread_traceback_. The desired behaviour of calling
-// get_thread0_walltime before profiler.stop() is a bit fuzzy at the time of
-// writing, but currently a test is done to make sure it returns the MDI of 0.0
-//
-
-TEST(HashTableTest,QueryInsertTest) {
-
- // Hashtable instance to poke and prod without changing any private data
- const auto& htable = prof.get_hashtable(0);
-
- // Nothing has been done yet, hashtable should be empty
- EXPECT_TRUE(htable.empty());
-
- // Create new hashes via HashTable::query_insert, which is used in Profiler::start
- const auto& prof_rigatoni = prof.start("Rigatoni");
- const auto& prof_penne = prof.start("Penne");
- prof.stop(prof_penne);
- prof.stop(prof_rigatoni);
-
- {
- SCOPED_TRACE("HashTable still empty after start is called");
-
- // Table no longer empty
- EXPECT_FALSE(htable.empty());
-
- // .count() returns 1 if map already has an entry associated with input hash
- EXPECT_EQ(htable.count(prof_rigatoni), 1);
- EXPECT_EQ(htable.count(prof_penne), 1);
- }
-
- {
- SCOPED_TRACE("Hashing related fault");
-
- // Checking that:
- // - 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"));
- }
-
-}
-
-TEST(HashTableTest,ThreadsEqualsEntries) {
-
- // Trying to access an entry one higher than the value of max_threads_ should
- // throw an exception as it won't exist assuming
- // max_threads_ == thread_hashtables_.size(). This is just a different way of
- // testing the assertion that already exists in the code.
-
- EXPECT_THROW(prof.get_hashtable(prof.get_max_threads()+1), std::out_of_range);
-
-}
-
-/**
- * @TODO Decide how to handle the MDI stuff and update the following test
- * accordingly. See Issue #53.
- *
- */
-
-TEST(HashTableTest,UpdateAndMdiTest) {
-
- // Create new hash
- size_t prof_pie = std::hash{}("Pie");
-
- // Trying to find a time before .start() will throw an exception
- EXPECT_THROW(prof.get_thread0_walltime(prof_pie), std::out_of_range);
-
- // Start timing
- auto const& expected_hash = prof.start("Pie");
- EXPECT_EQ(expected_hash,prof_pie); // Make sure prof_pie has the hash we expect
-
- sleep(1);
-
- // Time t1 declared inbetween .start() and first .stop()
- double const t1 = prof.get_thread0_walltime(prof_pie);
-
- //Stop timing
- prof.stop(prof_pie);
-
- // Time t2 declared after first profiler.stop()
- double const t2 = prof.get_thread0_walltime(prof_pie);
-
- // Start and stop same region again
- prof.start("Pie");
- sleep(1);
- prof.stop(prof_pie);
-
- // Time t3 declared after second profiler.stop()
- double const t3 = prof.get_thread0_walltime(prof_pie);
-
- // Expected behaviour: t1 return the MDI and t3 > t2 > 0
- constexpr double MDI = 0.0; // Missing Data Indicator (MDI)
-
- {
- SCOPED_TRACE("MDI missing from time points expected to return it");
- EXPECT_EQ(t1, MDI);
- }
-
- {
- SCOPED_TRACE("Update potentially not incrementing times correctly");
- EXPECT_GT(t2, 0.0);
- EXPECT_GT(t3, t2 );
- }
-}
-
-TEST(HashTableTest,TracebackTest) {
-
- const auto& traceback_vec = prof.get_inner_traceback_vector(0);
-
- {
- SCOPED_TRACE("traceback.at() not throwing exception before profiler.start()");
-
- // .at() throws exception when trying to access entry which isn't there
- EXPECT_THROW( traceback_vec.at(0), std::out_of_range );
- }
-
- // Start profiler
- const auto& prof_main = prof.start("Main");
-
- {
- SCOPED_TRACE("Traceback vector setup incorrectly");
-
- // The traceback vector for this thread is a vector of pairs of (hash,StartTime)
- // Therefore...
- // - It should have a size of 1 in this example
- // - The first entry in the pair should be prof_main
- // - The second entry is some time, here a check done to make sure it has the type "time_point_t"
- EXPECT_EQ( traceback_vec.size(), 1);
- EXPECT_EQ( traceback_vec.back().first, prof_main );
- EXPECT_THAT( traceback_vec.back().second, An() );
- }
-
- // Stop profiler
- prof.stop(prof_main);
-
- {
- SCOPED_TRACE("Traceback vector not empty, pop_back() failed or still an unexpected entry left?");
-
- // Shouldn't be any elements left
- EXPECT_TRUE( traceback_vec.empty() );
- }
-
- {
- SCOPED_TRACE("traceback.at() not throwing exception after element is deleted by pop_back()");
-
- // .at() should throw exception again, only existing element was deleted by .pop_back() inside profiler.stop()
- EXPECT_ANY_THROW( traceback_vec.at(0) );
- }
-}
diff --git a/tests/unit_tests/c++/test_hashtiming.cpp b/tests/unit_tests/c++/test_hashtiming.cpp
deleted file mode 100644
index c50aba6a..00000000
--- a/tests/unit_tests/c++/test_hashtiming.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include
-#include
-#include
-#include
-
-//
-// A "timings" test that has expectations about the profiler walltime.
-// In particular, a test is done to make sure the final selfwalltime is
-// equal to the total walltime minus child walltime. Also, std::chrono
-// is used to time a main and sub-region, it should return durations equal
-// to the profiler total walltimes, within tolerance.
-//
-
-TEST(HashEntryTest, TimingsTest) {
-
- // Start main profiler region and chrono timing
- const auto& prof_main = prof.start("QuicheLorraine");
- const auto chrono_main_start = std::chrono::steady_clock::now();
-
- sleep(1);
-
- // Start a sub-region and chrono timing
- const auto& prof_sub = prof.start("SalmonQuiche");
- const auto chrono_sub_start = std::chrono::steady_clock::now();
-
- sleep(1);
-
- // Stop profiler sub-region and respective chrono time
- const auto chrono_sub_end = std::chrono::steady_clock::now();
- prof.stop(prof_sub);
-
- // Stop profiler main region and respective chrono time
- const auto chrono_main_end = std::chrono::steady_clock::now();
- prof.stop(prof_main);
-
- {
- SCOPED_TRACE("Self walltime calculation failed");
-
- // Grab the total, child and self wallclock times
- const double& total = prof.get_thread0_walltime(prof_main);
- const double& child = prof.get_child_walltime(prof_main,0);
- const double& self = prof.get_self_walltime(prof_main,0);
-
- // Test that self_walltime = total_walltime - child_walltime
- EXPECT_EQ(self,total-child);
- }
-
- // Work out chrono durations in seconds
- std::chrono::duration main_region_duration = chrono_main_end - chrono_main_start;
- double main_in_s = main_region_duration.count();
-
- std::chrono::duration sub_region_duration = chrono_sub_end - chrono_sub_start;
- double sub_in_s = sub_region_duration.count();
-
- {
- SCOPED_TRACE("Chrono and profiler times not within tolerance");
-
- // Specify a time tolerance
- const double time_tolerance = 0.0001;
-
- // Expect profiler & chrono times to be within tolerance
- EXPECT_NEAR( prof.get_thread0_walltime(prof_main), main_in_s, time_tolerance );
- EXPECT_NEAR( prof.get_thread0_walltime(prof_sub) , sub_in_s , time_tolerance );
- }
-}
diff --git a/tests/unit_tests/c++/test_proftests.cpp b/tests/unit_tests/c++/test_proftests.cpp
deleted file mode 100644
index 235a8a18..00000000
--- a/tests/unit_tests/c++/test_proftests.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-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.
-//
-
-TEST(ProfilerTest,WriteTest) {
-
- // Start 3 nested regions
- const auto& prof_shortbread = prof.start("Shortbread");
- const auto& prof_brownie = prof.start("Brownie");
- const auto& prof_rockyroad = prof.start("RockyRoad");
-
- // Stop them 1 by 1
- prof.stop(prof_rockyroad);
- prof.stop(prof_brownie);
- prof.stop(prof_shortbread);
-
- // Call write to ensure hashvec is filled & sorted
- prof.write();
-
- // Get the Hashvec
- const auto& local_hashvec = prof.get_hashvec(0);
-
- {
- SCOPED_TRACE("Hashvec has incorrect size!");
-
- // Since hashvec is a vector of (hash,HashEntry) pairs
- // it should be equal in size to the number of regions
- ASSERT_EQ(local_hashvec.size(), 3);
- }
-
- {
- SCOPED_TRACE("Entries in hashvec incorrectly sorted");
-
- // hashvec is ordered from high to low so... [lastEntry] < [firstEntry]
- const double& val1 = local_hashvec[0].second.self_walltime_.count();
- const double& val2 = local_hashvec[1].second.self_walltime_.count();
- const double& val3 = local_hashvec[2].second.self_walltime_.count();
-
- EXPECT_LT(val3, val2);
- EXPECT_LT(val2, val1);
- }
-}
-
-TEST(ProfilerDeathTest,WrongHashTest) {
-
- EXPECT_EXIT({
-
- // Start main
- const auto& prof_main = prof.start("Chocolate");
-
- // A subregion
- const auto& prof_sub = prof.start("Vanilla");
- prof.stop(prof_sub);
-
- // Wrong hash in profiler.stop()
- prof.stop(prof_sub);
-
- // Eventually stop prof_main to avoid Wunused telling me off...
- prof.stop(prof_main);
-
- }, ExitedWithCode(100), "EMERGENCY STOP: hashes don't match.");
-
-}
-
-TEST(ProfilerDeathTest,StopBeforeStartTest) {
-
- EXPECT_DEATH({
-
- const auto prof_main = std::hash{}("Main");
-
- // Stop the profiler before anything is done
- prof.stop(prof_main);
-
- }, "" );
-
-}
diff --git a/tests/unit_tests/c++/test_regionname.cpp b/tests/unit_tests/c++/test_regionname.cpp
deleted file mode 100644
index 38d1388a..00000000
--- a/tests/unit_tests/c++/test_regionname.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-#include
-#include
-#include
-#include
-
-
-//
-// Tests focused on the "region name" of a particular section.
-// All main and sub-regions should give the expected string. Various
-// other funky region names will potentially be tested in the future.
-//
-
-TEST(RegionNameTest,NamesMatchTest) {
-
- //Start main region with name "Cappucino"
- const auto& prof_cappucino = prof.start("Cappucino");
-
- {
- SCOPED_TRACE("Problem with sub-region name");
-
- // Start timing a sub-region with name "Latte"
- std::string myString = "Latte";
- 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", subregionName);
-
- prof.stop(prof_latte);
- }
-
- {
- 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", regionName);
- }
-
- prof.stop(prof_cappucino);
-
-}
-
-/**
- * @TODO Think about whether or not we want region name checks within code, i.e
- * rules for what is and isn't allowed. See Issue #54.
- *
- */
-/* Placeholder for testing different region names, which there are no checks for within the code yet...
-
-TEST(RegionNameTest,InvalidsTest) {
-
- // Long name
- const auto& A = prof.hashtable_query_insert("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz");
-
- // Nothing / blank space
-
- // Non-null-terminated string
-
- // Special characters
-
- // Initalise inside brackets
-
-}*/