diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d569fb28de9bc055360137b9b8b0b3eef300907..b08a739f1ebcb31a960b3dc1386db3756a5a04c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,7 @@ set_property(CACHE NMC_SYSTEM_TYPE PROPERTY STRINGS Generic Cray BGQ ) # Cray specific flags if(${NMC_SYSTEM_TYPE} MATCHES "Cray") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -dynamic") + add_definitions(-DNMC_HAVE_CRAY) endif() #---------------------------------------------------------- diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp index 3582df425e74e1cd123c8837143fff57105af91e..c9fb6b7c577ceedf2b9afa3f2512866dec156814 100644 --- a/miniapp/miniapp.cpp +++ b/miniapp/miniapp.cpp @@ -51,7 +51,7 @@ int main(int argc, char** argv) { try { nest::mc::util::meter_manager meters; - meters.checkpoint("start"); + meters.start(); std::cout << util::mask_stream(global_policy::id()==0); // read parameters @@ -168,8 +168,6 @@ int main(int argc, char** argv) { write_trace_json(*trace.get(), options.trace_prefix); } - meters.checkpoint("output"); - util::save_to_file(meters, "meters.json"); } catch (io::usage_error& e) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bbbff66024ff0dac90a0cc42c9116e918e68bd89..c42d2d131d4f0ef3fa3db6ed0965f0fbc976d898 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,15 +5,16 @@ set(BASE_SOURCES morphology.cpp parameter_list.cpp profiling/memory_meter.cpp - profiling/meter.cpp profiling/meter_manager.cpp + profiling/power_meter.cpp profiling/profiler.cpp - profiling/time_meter.cpp swcio.cpp threading/affinity.cpp util/debug.cpp + util/hostname.cpp util/memory.cpp util/path.cpp + util/power.cpp util/unwind.cpp backends/multicore/fvm.cpp ) diff --git a/src/communication/mpi.hpp b/src/communication/mpi.hpp index 34b08864b8b329e0d3a774318e8bf51484ec1c8e..ad74b8bcb42d8d03479fe39541b67538a2ae860e 100644 --- a/src/communication/mpi.hpp +++ b/src/communication/mpi.hpp @@ -65,10 +65,6 @@ namespace mpi { // T must be trivially copyable template<typename T> std::vector<T> gather(T value, int root) { - static_assert( - true,//std::is_trivially_copyable<T>::value, - "gather can only be performed on trivally copyable types"); - using traits = mpi_traits<T>; auto buffer_size = (rank()==root) ? size() : 0; std::vector<T> buffer(buffer_size); @@ -87,9 +83,6 @@ namespace mpi { // T must be trivially copyable template <typename T> std::vector<T> gather_all(T value) { - static_assert( - true,//std::is_trivially_copyable<T>::value, - "gather_all can only be performed on trivally copyable types"); using traits = mpi_traits<T>; std::vector<T> buffer(size()); @@ -103,11 +96,33 @@ namespace mpi { return buffer; } + // Specialize gather for std::string. + inline std::vector<std::string> gather(std::string str, int root) { + using traits = mpi_traits<char>; + + auto counts = gather_all(int(str.size())); + auto displs = algorithms::make_index(counts); + + std::vector<char> buffer(displs.back()); + + PE("MPI", "Gather"); + MPI_Gatherv(str.data(), counts[rank()], traits::mpi_type(), // send + buffer.data(), counts.data(), displs.data(), traits::mpi_type(), // receive + root, MPI_COMM_WORLD); + PL(2); + + // Unpack the raw string data into a vector of strings. + std::vector<std::string> result; + result.reserve(size()); + for (auto i=0; i<size(); ++i) { + result.push_back(std::string(buffer.data()+displs[i], counts[i])); + } + return result; + } + + template <typename T> std::vector<T> gather_all(const std::vector<T>& values) { - static_assert( - true,//std::is_trivially_copyable<T>::value, - "gather_all can only be performed on trivally copyable types"); using traits = mpi_traits<T>; auto counts = gather_all(int(values.size())); diff --git a/src/communication/serial_global_policy.hpp b/src/communication/serial_global_policy.hpp index 4fb88908111cf05b3edbd60183115c0f860d56eb..81d2f3bc58d3cb1e7dedf14a856e056b4a90b21f 100644 --- a/src/communication/serial_global_policy.hpp +++ b/src/communication/serial_global_policy.hpp @@ -54,7 +54,7 @@ struct serial_global_policy { template <typename T> static std::vector<T> gather(T value, int) { - return {value}; + return {std::move(value)}; } static void barrier() {} diff --git a/src/profiling/memory_meter.cpp b/src/profiling/memory_meter.cpp index afdc42a6607b375e84f495bb53653562339c8601..c18c8f26e223a7574d7f8887c5dabf8378fbd2aa 100644 --- a/src/profiling/memory_meter.cpp +++ b/src/profiling/memory_meter.cpp @@ -1,62 +1,75 @@ #include <string> #include <vector> +#include <util/config.hpp> + #include "memory_meter.hpp" -#include <communication/global_policy.hpp> namespace nest { namespace mc { namespace util { -namespace { - measurement collate(const std::vector<memory_size_type>& readings, std::string name) { - using gcom = communication::global_policy; +// +// memory_meter +// - // Calculate the local change in allocated memory for each interval. - std::vector<memory_size_type> allocated; - allocated.push_back(0); - for (auto i=1u; i<readings.size(); ++i) { - allocated.push_back(readings[i] - readings[i-1]); - } +class memory_meter: public meter { +protected: + std::vector<memory_size_type> readings_; - // Assert that the same number of readings were taken on every domain. - const auto num_readings = allocated.size(); - if (gcom::min(num_readings)!=gcom::max(num_readings)) { - throw std::out_of_range( - "the number of checkpoints in the \"memory\" meter do not match across domains"); - } +public: + std::string name() override { + return "memory-allocated"; + } + + std::string units() override { + return "B"; + } + + void take_reading() override { + readings_.push_back(allocated_memory()); + } - // Gather allocations from across all of the domains onto the root domain. - // Note: results are only valid on the root domain on completion. - measurement results; - results.name = std::move(name); - results.units = "kB"; - for (auto m: allocated) { - results.measurements.push_back(gcom::gather(std::round(m/1e3), 0)); + std::vector<double> measurements() override { + std::vector<double> diffs; + + for (auto i=1ul; i<readings_.size(); ++i) { + diffs.push_back(readings_[i]-readings_[i-1]); } - return results; + return diffs; } -} // anonymous namespace +}; -std::string memory_meter::name() { - return "memory"; +meter_ptr make_memory_meter() { + if (not config::has_memory_measurement) { + return nullptr; + } + return meter_ptr(new memory_meter()); } -void memory_meter::take_reading() { - readings_.push_back(allocated_memory()); - #ifdef NMC_HAVE_GPU - readings_gpu_.push_back(gpu_allocated_memory()); - #endif -} +// +// gpu_memory_meter +// + +// The gpu memory meter specializes the reading and name methods of the basic +// memory_meter. +class gpu_memory_meter: public memory_meter { +public: + std::string name() override { + return "gpu-memory-allocated"; + } + + void take_reading() override { + readings_.push_back(gpu_allocated_memory()); + } +}; -std::vector<measurement> memory_meter::measurements() { - std::vector<measurement> results; - results.push_back(collate(readings_, "memory-allocated")); - if (readings_gpu_.size()) { - results.push_back(collate(readings_gpu_, "memory-allocated-gpu")); +meter_ptr make_gpu_memory_meter() { + if (not config::has_cuda) { + return nullptr; } - return results; + return meter_ptr(new gpu_memory_meter()); } } // namespace util diff --git a/src/profiling/memory_meter.hpp b/src/profiling/memory_meter.hpp index ee4e074e9afa016b7d2cf77c4d14a01769c65fdf..d19e1b84becd1451ee95efaa96915aca9c7a17e7 100644 --- a/src/profiling/memory_meter.hpp +++ b/src/profiling/memory_meter.hpp @@ -11,17 +11,8 @@ namespace nest { namespace mc { namespace util { -class memory_meter : public meter { - std::vector<memory_size_type> readings_; - - // only used if running on the GPU - std::vector<memory_size_type> readings_gpu_; - -public: - std::string name() override; - void take_reading() override; - virtual std::vector<measurement> measurements() override; -}; +meter_ptr make_memory_meter(); +meter_ptr make_gpu_memory_meter(); } // namespace util } // namespace mc diff --git a/src/profiling/meter.cpp b/src/profiling/meter.cpp deleted file mode 100644 index be55bda27d64df31bddec1f58d0d0808777d8361..0000000000000000000000000000000000000000 --- a/src/profiling/meter.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "meter.hpp" - -namespace nest { -namespace mc { -namespace util { - -nlohmann::json to_json(const measurement& mnt) { - nlohmann::json measurements; - for (const auto& m: mnt.measurements) { - measurements.push_back(m); - } - - return { - {"name", mnt.name}, - {"units", mnt.units}, - {"measurements", measurements} - }; -} - -} // namespace util -} // namespace mc -} // namespace nest - diff --git a/src/profiling/meter.hpp b/src/profiling/meter.hpp index 69874ba97c83a2c3498be20bf08c6c0cbb1b1aee..11afc7a616d627417685c346bc9b8cdeba0d135f 100644 --- a/src/profiling/meter.hpp +++ b/src/profiling/meter.hpp @@ -1,43 +1,25 @@ #pragma once +#include <memory> #include <string> -#include <json/json.hpp> +#include <vector> namespace nest { namespace mc { namespace util { -// A measurement from a meter has the following: -// * name -// * e.g. walltime or allocated-memory -// * units -// * use SI units -// * e.g. s or MiB -// * measurements -// * a vector with one entry for each checkpoint -// * each entry is a std::vector<double> of measurements gathered across -// domains at one checkpoint. -// -struct measurement { - std::string name; - std::string units; - std::vector<std::vector<double>> measurements; -}; - -// Converts a measurement to a json type for serialization to file. -// See src/profiling/meters.md for more information about the json formating. -nlohmann::json to_json(const measurement& m); - // A meter can be used to take a measurement of resource consumption, for // example wall time, memory or energy consumption. // Each specialization must: // 1) Record the resource consumption on calling meter::take_reading. // * How and which information is recorded is implementation dependent. -// 2) Return a std::vector containing the measurements that are derived -// from the information recorded on calls to meter::take_reading. -// * The return value is a vector of measurements, because a meter -// may derive multiple measurements from the recorded checkpoint -// information. +// 2) Provide the name of the resource being measured via name() +// e.g. : energy +// 3) Provide the units of the resource being measured via units() +// e.g. : J +// 4) Return the resources consumed between each pair of readings as a +// std::vector<double> from measurements(). So, for n readings, there will +// be n-1 differences. class meter { public: meter() = default; @@ -48,15 +30,16 @@ public: // Take a reading/measurement of the resource virtual void take_reading() = 0; - // Return a summary of the recordings. - // May perform expensive operations to process and analyse the readings. - // Full output is expected only on the root domain, i.e. when - // global_policy::id()==0 - virtual std::vector<measurement> measurements() = 0; + // The units of the values returned in from the measurements method. + virtual std::string units() = 0; + + virtual std::vector<double> measurements() = 0; virtual ~meter() = default; }; +using meter_ptr = std::unique_ptr<meter>; + } // namespace util } // namespace mc } // namespace nest diff --git a/src/profiling/meter_manager.cpp b/src/profiling/meter_manager.cpp index f6c2efab2e7250de6d9a84c2589ae792780374aa..54b5bd90a29f1701b609f0d400c2be81aad21c20 100644 --- a/src/profiling/meter_manager.cpp +++ b/src/profiling/meter_manager.cpp @@ -1,34 +1,79 @@ +#include <communication/global_policy.hpp> +#include <util/hostname.hpp> +#include <json/json.hpp> + #include "meter_manager.hpp" +#include "memory_meter.hpp" +#include "power_meter.hpp" namespace nest { namespace mc { namespace util { +measurement::measurement( + std::string n, std::string u, const std::vector<double>& readings): + name(std::move(n)), units(std::move(u)) +{ + using gcom = communication::global_policy; + + // Assert that the same number of readings were taken on every domain. + const auto num_readings = readings.size(); + if (gcom::min(num_readings)!=gcom::max(num_readings)) { + throw std::out_of_range( + "the number of checkpoints in the \""+name+"\" meter do not match across domains"); + } + + // Gather across all of the domains onto the root domain. + for (auto r: readings) { + measurements.push_back(gcom::gather(r, 0)); + } +} + meter_manager::meter_manager() { - // add time-measurement meter - meters_.emplace_back(new time_meter()); + if (auto m = make_memory_meter()) { + meters_.push_back(std::move(m)); + } + if (auto m = make_gpu_memory_meter()) { + meters_.push_back(std::move(m)); + } + if (auto m = make_power_meter()) { + meters_.push_back(std::move(m)); + } +}; + +void meter_manager::start() { + EXPECTS(!started_); + + started_ = true; - // add memory consumption meter - if (has_memory_metering) { - meters_.emplace_back(new memory_meter()); + // take readings for the start point + for (auto& m: meters_) { + m->take_reading(); } - // add energy consumption meter - // TODO + // Enforce a global barrier after taking the time stamp + communication::global_policy::barrier(); + + start_time_ = timer_type::tic(); }; + void meter_manager::checkpoint(std::string name) { - // Enforce a global synchronization point the first time that the meters - // are used, to ensure that times measured across all domains are - // synchronised. - if (checkpoint_names_.size()==0) { - communication::global_policy::barrier(); - } + EXPECTS(started_); + + // Record the time taken on this domain since the last checkpoint + auto end_time = timer_type::tic(); + times_.push_back(timer_type::difference(start_time_, end_time)); + // Update meters checkpoint_names_.push_back(std::move(name)); for (auto& m: meters_) { m->take_reading(); } + + // Synchronize all domains before setting start time for the next interval + communication::global_policy::barrier(); + start_time_ = timer_type::tic(); } const std::vector<std::unique_ptr<meter>>& meter_manager::meters() const { @@ -39,15 +84,39 @@ const std::vector<std::string>& meter_manager::checkpoint_names() const { return checkpoint_names_; } +const std::vector<double>& meter_manager::times() const { + return times_; +} + +nlohmann::json to_json(const measurement& mnt) { + nlohmann::json measurements; + for (const auto& m: mnt.measurements) { + measurements.push_back(m); + } + + return { + {"name", mnt.name}, + {"units", mnt.units}, + {"measurements", measurements} + }; +} + nlohmann::json to_json(const meter_manager& manager) { using gcom = communication::global_policy; + // Gather the meter outputs into a json Array nlohmann::json meter_out; for (auto& m: manager.meters()) { - for (auto& measure: m->measurements()) { - meter_out.push_back(to_json(measure)); - } + meter_out.push_back( + to_json(measurement(m->name(), m->units(), m->measurements())) + ); } + // Add the times to the meter outputs + meter_out.push_back(to_json(measurement("time", "s", manager.times()))); + + // Gather a vector with the names of the node that each rank is running on. + auto host = hostname(); + auto hosts = gcom::gather(host? *host: "unknown", 0); // Only the "root" process returns meter information if (gcom::id()==0) { @@ -56,9 +125,7 @@ nlohmann::json to_json(const meter_manager& manager) { {"num_domains", gcom::size()}, {"global_model", std::to_string(gcom::kind())}, {"meters", meter_out}, - // TODO mapping of domains to nodes, which will be required to - // calculate the total memory and energy consumption of a - // distributed simulation. + {"hosts", hosts}, }; } diff --git a/src/profiling/meter_manager.hpp b/src/profiling/meter_manager.hpp index 323665799dd3c4f47121ce0f51ba7a4f77deed48..c3164b8106b5a24335f9b498d5023ca08012ddb7 100644 --- a/src/profiling/meter_manager.hpp +++ b/src/profiling/meter_manager.hpp @@ -3,28 +3,54 @@ #include <memory> #include <vector> -#include <util/make_unique.hpp> -#include <communication/global_policy.hpp> #include <json/json.hpp> #include "meter.hpp" -#include "memory_meter.hpp" -#include "time_meter.hpp" +#include "profiler.hpp" namespace nest { namespace mc { namespace util { +// A measurement has the following: +// * name +// * e.g. walltime or allocated-memory +// * units +// * use SI units +// * e.g. s or MiB +// * measurements +// * a vector with one entry for each checkpoint +// * each entry is a std::vector<double> of measurements gathered across +// domains at one checkpoint. +struct measurement { + std::string name; + std::string units; + std::vector<std::vector<double>> measurements; + measurement(std::string, std::string, const std::vector<double>&); +}; + +// Converts a measurement to a json type for serialization to file. +// See src/profiling/meters.md for more information about the json formating. +nlohmann::json to_json(const measurement& m); + class meter_manager { +private: + bool started_ = false; + + timer_type::time_point start_time_; + std::vector<double> times_; + std::vector<std::unique_ptr<meter>> meters_; std::vector<std::string> checkpoint_names_; public: meter_manager(); + void start(); void checkpoint(std::string name); const std::vector<std::unique_ptr<meter>>& meters() const; const std::vector<std::string>& checkpoint_names() const; + const std::vector<double>& times() const; }; nlohmann::json to_json(const meter_manager&); diff --git a/src/profiling/meters.md b/src/profiling/meters.md index afe11da26f333805cd8094058e947f36e07c09ed..72cdc8e13ef63cf6dfce327c3bc7679b9e7b1e0e 100644 --- a/src/profiling/meters.md +++ b/src/profiling/meters.md @@ -3,13 +3,14 @@ A json record for a meter measurement is a json object. Each Object corresponds to a derived measurement: * `name`: a string describing the measurement * `units`: a string with SI units for measurements - * `measurements`: a json Array of measurements, with one - entry per checkpoint (corresponding to a call to - meter::take_reading) - * each measurement is itself a numeric array, with one - recording for each domain in the global communicator + * `measurements`: a json Array of measurements, with one entry for the + each checkpoint. The first enry is the measure of resources consumed + between the call to `meter_manager::start()` and the first checkpoint, the + second entry measure between the first and second checkpoints, and son on. + * each measurement is itself a numeric array, with one recording for each + domain in the global communicator -For example, the output of a meter for measuring wall time where 5 readings +For example, the output of a meter for measuring wall time where 4 checkpoints were taken on 4 MPI ranks could be represented as follows: ```json @@ -17,7 +18,6 @@ were taken on 4 MPI ranks could be represented as follows: "name": "walltime", "units": "s", "measurements": [ - [ 0, 0, 0, 0, ], [ 0.001265837, 0.001344004, 0.001299362, 0.001195762, ], [ 0.014114013, 0.015045662, 0.015071675, 0.014209514, ], [ 1.491986631, 1.491121134, 1.490957219, 1.492064233, ], diff --git a/src/profiling/power_meter.cpp b/src/profiling/power_meter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2601f114e0ba1037ecd140d00fd8d2575af858f8 --- /dev/null +++ b/src/profiling/power_meter.cpp @@ -0,0 +1,48 @@ +#include <string> +#include <vector> + +#include <util/config.hpp> + +#include "power_meter.hpp" + +namespace nest { +namespace mc { +namespace util { + +class power_meter: public meter { + std::vector<energy_size_type> readings_; + +public: + std::string name() override { + return "energy"; + } + + std::string units() override { + return "J"; + } + + std::vector<double> measurements() override { + std::vector<double> diffs; + + for (auto i=1ul; i<readings_.size(); ++i) { + diffs.push_back(readings_[i]-readings_[i-1]); + } + + return diffs; + } + + void take_reading() override { + readings_.push_back(energy()); + } +}; + +meter_ptr make_power_meter() { + if (not config::has_power_measurement) { + return nullptr; + } + return meter_ptr(new power_meter()); +} + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/profiling/power_meter.hpp b/src/profiling/power_meter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..40682acb5fd4816b6cdf54abaa0213fd4b19c482 --- /dev/null +++ b/src/profiling/power_meter.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <string> +#include <vector> + +#include <util/power.hpp> + +#include "meter.hpp" + +namespace nest { +namespace mc { +namespace util { + +meter_ptr make_power_meter(); + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/profiling/time_meter.cpp b/src/profiling/time_meter.cpp deleted file mode 100644 index c29bf12b157dd2bcd3730be307a4a6cab830bf95..0000000000000000000000000000000000000000 --- a/src/profiling/time_meter.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include <string> -#include <vector> - -#ifdef NMC_HAVE_GPU - #include <cuda_runtime.h> -#endif - -#include "time_meter.hpp" -#include <communication/global_policy.hpp> - -namespace nest { -namespace mc { -namespace util { - -std::string time_meter::name() { - return "time"; -} - -void time_meter::take_reading() { - // Wait for execution on this global domain to finish before recording the - // time stamp. For now this means waiting for all work to finish executing - // on the GPU (if GPU support is enabled) -#ifdef NMC_HAVE_GPU - cudaDeviceSynchronize(); -#endif - - // Record the time stamp - readings_.push_back(timer_type::tic()); - - // Enforce a global barrier after taking the time stamp - communication::global_policy::barrier(); -} - -std::vector<measurement> time_meter::measurements() { - using gcom = communication::global_policy; - - // Calculate the elapsed time on the local domain for each interval, - // and store them in the times vector. - std::vector<double> times; - times.push_back(0); - for (auto i=1u; i<readings_.size(); ++i) { - double t = timer_type::difference(readings_[i-1], readings_[i]); - times.push_back(t); - } - - // Assert that the same number of readings were taken on every domain. - const auto num_readings = times.size(); - if (gcom::min(num_readings)!=gcom::max(num_readings)) { - throw std::out_of_range( - "the number of checkpoints in the \"time\" meter do not match across domains"); - } - - // Gather the timers from accross all of the domains onto the root domain. - // Note: results are only valid on the root domain on completion. - measurement results; - results.name = "walltime"; - results.units = "s"; - for (auto t: times) { - results.measurements.push_back(gcom::gather(t, 0)); - } - - return {results}; -} - -} // namespace util -} // namespace mc -} // namespace nest diff --git a/src/profiling/time_meter.hpp b/src/profiling/time_meter.hpp deleted file mode 100644 index 32709167cd7e965491c9d738f8fec783117c8ea8..0000000000000000000000000000000000000000 --- a/src/profiling/time_meter.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include <string> -#include <vector> - -#include "meter.hpp" -#include "profiler.hpp" - -namespace nest { -namespace mc { -namespace util { - -class time_meter : public meter { - std::vector<timer_type::time_point> readings_; - -public: - std::string name() override; - void take_reading() override; - virtual std::vector<measurement> measurements() override; -}; - -} // namespace util -} // namespace mc -} // namespace nest - diff --git a/src/util/config.hpp b/src/util/config.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4bd3adf3bbd20b7626e5422e27df99b45a9eb3bf --- /dev/null +++ b/src/util/config.hpp @@ -0,0 +1,41 @@ +#pragma once + +namespace nest { +namespace mc { +namespace config { + +// has_memory_measurement +// Support for measuring total allocated memory. +// * true: calls to util::allocated_memory() will return valid results +// * false: calls to util::allocated_memory() will return -1 +// +// has_power_measurement +// Support for measuring energy consumption. +// Currently only on Cray XC30/40/50 systems. +// * true: calls to util::energy() will return valid results +// * false: calls to util::energy() will return -1 +// +// has_cuda +// Has been compiled with CUDA back end support + +#ifdef __linux__ +constexpr bool has_memory_measurement = true; +#else +constexpr bool has_memory_measurement = false; +#endif + +#ifdef NMC_HAVE_CRAY +constexpr bool has_power_measurement = true; +#else +constexpr bool has_power_measurement = false; +#endif + +#ifdef NMC_HAVE_CUDA +constexpr bool has_cuda = true; +#else +constexpr bool has_cuda = false; +#endif + +} // namespace config +} // namespace mc +} // namespace nest diff --git a/src/util/hostname.cpp b/src/util/hostname.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c39f3084ff265ca1306e90f9ccd65f223be64b7d --- /dev/null +++ b/src/util/hostname.cpp @@ -0,0 +1,37 @@ +#include <string> + +#include <util/optional.hpp> + +#include "hostname.hpp" + +#ifdef __linux__ +extern "C" { + #include <unistd.h> +} +#endif + +namespace nest { +namespace mc { +namespace util { + +#ifdef __linux__ +util::optional<std::string> hostname() { + // Hostnames can be up to 256 characters in length, however on many systems + // it is limitted to 64. + char name[256]; + auto result = gethostname(name, sizeof(name)); + if (result) { + return util::nothing; + } + return std::string(name); +} +#else +util::optional<std::string> hostname() { + return util::nothing; +} +#endif + +} // namespace util +} // namespace mc +} // namespace nest + diff --git a/src/util/hostname.hpp b/src/util/hostname.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ae735ef5a77144954f71c2532f89bdc98bd75d88 --- /dev/null +++ b/src/util/hostname.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <string> + +#include <util/optional.hpp> + +namespace nest { +namespace mc { +namespace util { + +// Get the name of the host on which this process is running. +util::optional<std::string> hostname(); + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/util/memory.hpp b/src/util/memory.hpp index f231f8aeb3c574eb5c0e6330725f202c03fae8d7..7dab29b80a67f3a5604a04c3a0c936cb97b43d3b 100644 --- a/src/util/memory.hpp +++ b/src/util/memory.hpp @@ -6,12 +6,6 @@ namespace nest { namespace mc { namespace util { -#ifdef __linux__ - constexpr bool has_memory_metering = true; -#else - constexpr bool has_memory_metering = false; -#endif - // Use a signed type to store memory sizes because it can be used to store // the difference between two readings, which may be negative. // A 64 bit type is large enough to store any amount of memory that will diff --git a/src/util/power.cpp b/src/util/power.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07037ea8051844cad00d92b8fa4cae954beb3060 --- /dev/null +++ b/src/util/power.cpp @@ -0,0 +1,32 @@ +#include <fstream> + +#include "power.hpp" + +namespace nest { +namespace mc { +namespace util { + +#ifdef NMC_HAVE_CRAY + +energy_size_type energy() { + energy_size_type result = -1; + + std::ifstream fid("/sys/cray/pm_counters/energy"); + if (fid) { + fid >> result; + } + + return result; +} + +#else + +energy_size_type energy() { + return -1; +} + +#endif + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/util/power.hpp b/src/util/power.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7c13624795a00d40d4c5743101e46ce957f8a270 --- /dev/null +++ b/src/util/power.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <cstdint> + +namespace nest { +namespace mc { +namespace util { + +// Energy in Joules (J) +using energy_size_type = std::uint64_t; + +// Returns negative value if unable to read energy +energy_size_type energy(); + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/tests/global_communication/test_mpi_gather_all.cpp b/tests/global_communication/test_mpi_gather_all.cpp index aa62b0d58f45adc7ece97f3e7751a049d0909f52..8d676a17d253a423797ba535241347284e703162 100644 --- a/tests/global_communication/test_mpi_gather_all.cpp +++ b/tests/global_communication/test_mpi_gather_all.cpp @@ -97,4 +97,52 @@ TEST(mpi, gather_all_with_partition) { EXPECT_EQ(expected_divisions, gathered.partition()); } +TEST(mpi, gather_string) { + using policy = mpi_global_policy; + + int id = policy::id(); + + // Make a string of variable length, with the character + // in the string distrubuted as follows + // rank string + // 0 a + // 1 bb + // 2 ccc + // 3 dddd + // ... + // 25 zzzz...zzz (26 times z) + // 26 aaaa...aaaa (27 times a) + auto make_string = [](int id) { + return std::string(id+1, 'a'+char(id%26));}; + + auto s = make_string(id); + + auto gathered = mpi::gather(s, 0); + + if (!id) { + ASSERT_TRUE(policy::size()==(int)gathered.size()); + for (std::size_t i=0; i<gathered.size(); ++i) { + EXPECT_EQ(make_string(i), gathered[i]); + } + } +} + +TEST(mpi, gather) { + using policy = mpi_global_policy; + + int id = policy::id(); + + auto gathered = mpi::gather(id, 0); + + if (!id) { + ASSERT_TRUE(policy::size()==(int)gathered.size()); + for (std::size_t i=0; i<gathered.size(); ++i) { + EXPECT_EQ(int(i), gathered[i]); + } + } + else { + EXPECT_EQ(0u, gathered.size()); + } +} + #endif // NMC_HAVE_MPI