diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp index f0c608309c67d258fecce2f84e44c061bb2a474a..be9bcd1dc5b2bca9a657aeeb21d2514affdd5f05 100644 --- a/miniapp/miniapp.cpp +++ b/miniapp/miniapp.cpp @@ -3,6 +3,7 @@ #include <iostream> #include <fstream> #include <memory> +#include <vector> #include <json/src/json.hpp> @@ -16,6 +17,7 @@ #include <profiling/profiler.hpp> #include <communication/communicator.hpp> #include <communication/global_policy.hpp> +#include <communication/exporter_spike_file.hpp> #include <util/ioutil.hpp> #include <util/optional.hpp> @@ -29,12 +31,15 @@ using global_policy = communication::global_policy; using lowered_cell = fvm::fvm_cell<double, cell_local_size_type>; using model_type = model<lowered_cell>; -using sample_trace_type = sample_trace<model_type::time_type, model_type::value_type>; - +using time_type = model_type::time_type; +using sample_trace_type = sample_trace<time_type, model_type::value_type>; +using file_export_type = communication::exporter_spike_file<time_type, global_policy>; void banner(); std::unique_ptr<recipe> make_recipe(const io::cl_options&, const probe_distribution&); std::unique_ptr<sample_trace_type> make_trace(cell_member_type probe_id, probe_spec probe); std::pair<cell_gid_type, cell_gid_type> distribute_cells(cell_size_type ncells); +using communicator_type = communication::communicator<time_type, communication::global_policy>; +using spike_type = typename communicator_type::spike_type; void write_trace_json(const sample_trace_type& trace, const std::string& prefix = "trace_"); @@ -63,15 +68,35 @@ int main(int argc, char** argv) { // build model from recipe // TODO: I would rather just forward the options object. - model_type::file_output_parameters model_params( - options.spike_file_output, - options.single_file_per_rank, - options.over_write, - options.output_path, - options.file_name, - options.file_extention - ); - model_type m(*recipe, cell_range.first, cell_range.second, model_params); + + model_type m(*recipe, cell_range.first, cell_range.second); + std::unique_ptr<file_export_type> file_exporter; + if (!options.spike_file_output) { + m.set_global_spike_callback( + file_export_type::do_nothing); + m.set_local_spike_callback( + file_export_type::do_nothing); + } + else { + file_exporter = nest::mc::util::make_unique<file_export_type>( + options.file_name, options.output_path, + options.file_extention, true); + + if (options.single_file_per_rank) { + m.set_global_spike_callback( + file_export_type::do_nothing); + m.set_local_spike_callback( + [&](const std::vector<spike_type>& spikes) { file_exporter->do_export(spikes); }); + + } + else { + m.set_global_spike_callback( + [&](const std::vector<spike_type>& spikes) { file_exporter->do_export(spikes); }); + m.set_local_spike_callback( + file_export_type::do_nothing); + } + } + // inject some artificial spikes, 1 per 20 neurons. cell_gid_type spike_cell = 20*((cell_range.first+19)/20); diff --git a/src/communication/export_manager.hpp b/src/communication/export_manager.hpp deleted file mode 100644 index 99354ab6bff8004b3cc6c2a85a3e5fa57571d8a1..0000000000000000000000000000000000000000 --- a/src/communication/export_manager.hpp +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include <algorithm> -#include <iostream> - -#include <vector> -#include <memory> -#include <utility> -#include <string> - -#include <spike.hpp> -#include <util.hpp> -#include <common_types.hpp> -#include "exporter_interface.hpp" -#include "exporter_spike_file.hpp" -#include "exporter_spike_single_file.hpp" - -namespace nest { -namespace mc { -namespace communication { - - -// export_manager manages the export of simulation parameters to the outside -// world. Examples could be output to disk, but also more advanced communication -// via mpi can be possible. -// The parameters to be exported are buffered locally. -// There are two export methods implemented: rank local where an export is -// done on each rank and a single export per simulation -// The output file is constructed from constructor arguments and will always -// contain a index also when a single file is written. THis allows automated -// parsing with a simple regex -// -// TODO: The exporter currently only exports spikes to file. The constructor -// arguments reflect this. In future version a better way to configure this -// class is needed. -template <typename Time, typename CommunicationPolicy> -class export_manager { -public: - using time_type = Time; - using spike_type = spike<cell_member_type, time_type>; - - // Constructor - // spike_file_output initiates spike file output. If false the object is - // constructed in a valid state. No exporters are registed so no output - // is done - // single_file_per_rank if true only rank zero performs output to disk - // over_write if true will overwrite the specified output file - // output_path relative or absolute path - // file_name will be appended with "_x" with x the rank number - // file_extention a seperator will be added automatically - export_manager(bool spike_file_output, bool single_file_per_rank, bool over_write, - std::string output_path, std::string file_name, std::string file_extention) - : - spike_file_output_(spike_file_output) - { - // simple switch to turn of the export, object will still be in a valid - // state. - if (!spike_file_output_) { - return; - } - - // single file per rank exporters - if (single_file_per_rank) { - rank_exporters_.push_back( - nest::mc::util::make_unique< - nest::mc::communication::exporter_spike_file<Time, CommunicationPolicy> >( - file_name, output_path, file_extention, over_write)); - } - - // single file per simulation exporters - if (!single_file_per_rank) { // TODO: When we add more exporters and - //agreed on the parameter input this should be cleanup up. - if (communication_policy_.id() == 0) { - single_exporters_.push_back( - nest::mc::util::make_unique< - nest::mc::communication::exporter_spike_file<Time, CommunicationPolicy> >( - file_name, output_path, file_extention, over_write)); - - } - } - } - - // Perform a export of local spikes, typically used for exporting to multi- - // ple files from each rank individually. - // spikes are buffer before export - void local_export_callback(const std::vector<spike_type>& spikes) - { - // TODO: No export needed, so exit - if (!spike_file_output_) { - return; - } - - local_spikes_.insert(std::end(local_spikes_), - std::begin(spikes), std::end(spikes)); - - // TODO: Do each exporter in a parallel thread? - for (auto &exporter : rank_exporters_) { - exporter->do_export(spikes); - } - - local_spikes_.clear(); - } - - // Perform a export of global spikes, typically used for exporting spikes - // from a single rank in a simulation - // spikes are buffer before export - void global_export_callback(const std::vector<spike_type>& spikes) - { - if (!spike_file_output_) { - return; - } - - // We only output on a single rank - if (!communication_policy_.id() == 0) { - return; - } - - global_spikes_.insert(std::end(global_spikes_), - std::begin(spikes), std::end(spikes)); - - // TODO: Do each exporter in a parallel thread? - for (auto &exporter : single_exporters_) { - exporter->do_export(spikes); - } - - global_spikes_.clear(); - } - -private: - bool spike_file_output_; - - std::vector<std::unique_ptr<exporter_interface<Time, CommunicationPolicy> > > rank_exporters_; - std::vector<std::unique_ptr<exporter_interface<Time, CommunicationPolicy> > > single_exporters_; - - CommunicationPolicy communication_policy_; - - // local buffer for spikes - std::vector<spike_type> local_spikes_; - std::vector<spike_type> global_spikes_; -}; - -} //communication -} // namespace mc -} // namespace nest diff --git a/src/communication/exporter_interface.hpp b/src/communication/exporter_interface.hpp index d0370a92b609773b06cb8663563129a242385cb1..8f7a714c9a1a735d338950c130debab89c0e3123 100644 --- a/src/communication/exporter_interface.hpp +++ b/src/communication/exporter_interface.hpp @@ -26,6 +26,10 @@ public: // Returns the status of the exporter virtual bool good() const = 0; + + // Static NULL version of the do_export function for NOP callbacks + static void do_nothing(const std::vector<spike_type>&) + {} }; } //communication diff --git a/src/model.hpp b/src/model.hpp index 14a5f9d82a70b502f4e4c14553a45b28e0701160..f6d3f7922ae925a7bb6831d7f7b028277318b5ba 100644 --- a/src/model.hpp +++ b/src/model.hpp @@ -12,7 +12,6 @@ #include <thread_private_spike_store.hpp> #include <communication/communicator.hpp> #include <communication/global_policy.hpp> -#include <communication/export_manager.hpp> #include <profiling/profiler.hpp> #include "trace_sampler.hpp" @@ -23,47 +22,19 @@ namespace mc { template <typename Cell> class model { public: - - // TODO:We need to think how to transport parameters accros the different classes - // Move the io to src directory would make it allot easier!! - struct file_output_parameters - { - bool spike_file_output; - bool single_file_per_rank; - bool over_write; - std::string output_path; - std::string file_name; - std::string file_extention; - - file_output_parameters(bool spike_file_output_i, - bool single_file_per_rank_i, - bool over_write_i, - std::string output_path_i, - std::string file_name_i, - std::string file_extention_i) - : - spike_file_output(spike_file_output_i), - single_file_per_rank(single_file_per_rank_i), - over_write(over_write_i), - output_path(output_path_i), - file_name(file_name_i), - file_extention(file_extention_i) - {} - }; - using cell_group_type = cell_group<Cell>; - using time_type = typename cell_group_type::time_type; + using time_type = typename cell_group_type::time_type; using value_type = typename cell_group_type::value_type; using communicator_type = communication::communicator<time_type, communication::global_policy>; using sampler_function = typename cell_group_type::sampler_function; + using spike_type = typename communicator_type::spike_type; struct probe_record { cell_member_type id; probe_spec probe; }; - model(const recipe& rec, cell_gid_type cell_from, cell_gid_type cell_to, - file_output_parameters file_output_parameters_i): + model(const recipe& rec, cell_gid_type cell_from, cell_gid_type cell_to): cell_from_(cell_from), cell_to_(cell_to), communicator_(cell_from, cell_to) @@ -99,14 +70,6 @@ public: } communicator_.construct(); - exporter_ = nest::mc::util::make_unique<exporter_manager_type>( - file_output_parameters_i.spike_file_output, - file_output_parameters_i.single_file_per_rank, - file_output_parameters_i.over_write, - file_output_parameters_i.output_path, - file_output_parameters_i.file_name, - file_output_parameters_i.file_extention); - // Allocate an empty queue buffer for each cell group // These must be set initially to ensure that a queue is available for each // cell group for the first time step. @@ -144,7 +107,7 @@ public: auto update_cells = [&] () { threading::parallel_for::apply( 0u, cell_groups_.size(), - [&](unsigned i) { + [&](unsigned i) { auto &group = cell_groups_[i]; PE("stepping","events"); @@ -173,10 +136,10 @@ public: auto exchange = [&] () { PE("stepping", "exchange"); auto local_spikes = previous_spikes().gather(); - exporter_->local_export_callback(local_spikes); + local_export_callback_(local_spikes); future_events() = communicator_.exchange(local_spikes, - // send the exporter function as pointers to export - [&] (const std::vector<spike_type>& spikes){ exporter_->global_export_callback(spikes); }); + // send the exporter function as pointer + [&] (const std::vector<spike_type>& spikes){ global_export_callback_(spikes); }); PL(2); }; @@ -215,6 +178,19 @@ public: std::size_t num_spikes() const { return communicator_.num_spikes(); } std::size_t num_groups() const { return cell_groups_.size(); } + void set_global_spike_callback(std::function<void( + const std::vector<spike_type>&)> global_export_callback) + { + global_export_callback_ = global_export_callback; + } + void set_local_spike_callback(std::function<void( + const std::vector<spike_type>&)> local_export_callback) + { + local_export_callback_ = local_export_callback; + } + + + private: cell_gid_type cell_from_; cell_gid_type cell_to_; @@ -222,7 +198,7 @@ private: std::vector<cell_group_type> cell_groups_; communicator_type communicator_; std::vector<probe_record> probes_; - using spike_type = typename communicator_type::spike_type; + using event_queue_type = typename communicator_type::event_queue; util::double_buffer< std::vector<event_queue_type> > event_queues_; @@ -230,8 +206,8 @@ private: using local_spike_store_type = thread_private_spike_store<time_type>; util::double_buffer< local_spike_store_type > local_spikes_; - using exporter_manager_type = nest::mc::communication::export_manager<time_type, communication::global_policy>; - std::unique_ptr<exporter_manager_type> exporter_; + std::function<void(const std::vector<spike_type>&)> global_export_callback_; + std::function<void(const std::vector<spike_type>&)> local_export_callback_; // Convenience functions that map the spike buffers and event queues onto // the appropriate integration interval. // diff --git a/tests/performance/io/disk_io.cpp b/tests/performance/io/disk_io.cpp index 877896759cc5637c52f5a3a18a567f6e2e2e5606..fa9973939d49db62b6427d80f2f9198c69dee26c 100644 --- a/tests/performance/io/disk_io.cpp +++ b/tests/performance/io/disk_io.cpp @@ -13,7 +13,7 @@ #include <communication/communicator.hpp> #include <communication/global_policy.hpp> -#include <communication/export_manager.hpp> +#include <communication/exporter_spike_file.hpp> #include <profiling/profiler.hpp> using namespace nest::mc; @@ -74,8 +74,8 @@ int main(int argc, char** argv) } // Create the sut - communication::export_manager<time_type, global_policy> manager( - true, file_per_rank, true, "./", "spikes", "gdf"); + communication::exporter_spike_file<time_type, global_policy> exporter( + "spikes", "./", "gdf", true); // We need the nr of ranks to calculate the nr of spikes to produce per // rank @@ -106,7 +106,7 @@ int main(int argc, char** argv) for (auto idx = 0; idx < nr_repeats; ++idx) { auto time_start = timer::tic(); - manager.local_export_callback(spikes); + exporter.do_export(spikes); auto run_time = timer::toc(time_start); time_total += run_time; timings_arr[idx] = run_time;