diff --git a/miniapp/io.cpp b/miniapp/io.cpp index 8c72f46375456e9994022f434d6f43b11ccbd9dc..0c9acd016c50ff5cacfe2180533eef9ba3150592 100644 --- a/miniapp/io.cpp +++ b/miniapp/io.cpp @@ -43,8 +43,9 @@ namespace io { cl_options read_options(int argc, char** argv) { // set default options - // TODO: the declaration of this defopts is realistic if we have allot - // more options. We should use a name scheme. + // TODO: the declaration of this defopts is not realistic if we have allot + // more options. We should use a named scheme or import the defopts from + // a valid json file const cl_options defopts{"", 1000, 500, "expsyn", 100, 100., 0.025, false, false, 1.0, "trace_", util::nothing, diff --git a/miniapp/miniapp.cpp b/miniapp/miniapp.cpp index 9433ffadf1de34e23a1adc52e72b5437f2a21beb..f0c608309c67d258fecce2f84e44c061bb2a474a 100644 --- a/miniapp/miniapp.cpp +++ b/miniapp/miniapp.cpp @@ -62,7 +62,7 @@ int main(int argc, char** argv) { auto cell_range = distribute_cells(recipe->num_cells()); // 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, diff --git a/src/communication/communicator.hpp b/src/communication/communicator.hpp index 0289fc3192e268bd5e647d659747635509914ef8..37b6d44cc4222d7314c1bc0d2ae0915be8f64f55 100644 --- a/src/communication/communicator.hpp +++ b/src/communication/communicator.hpp @@ -94,15 +94,15 @@ public: std::vector<event_queue> exchange(const std::vector<spike_type>& local_spikes, std::function<void (const std::vector<spike_type>&)> do_export_rank, std::function<void(const std::vector<spike_type>&)> do_export_single) - { - // global all-to-all to gather a local copy of the global spike list on each node. - - + { + // Export of (rank) local spikes do_export_rank(local_spikes); + // global all-to-all to gather a local copy of the global spike list on each node. auto global_spikes = communication_policy_.gather_spikes( local_spikes ); num_spikes_ += global_spikes.size(); + // Export of global spikes do_export_single(global_spikes); // check each global spike in turn to see it generates local events. diff --git a/src/communication/export_manager.hpp b/src/communication/export_manager.hpp index 52e73071ba4ad9734a99023778add93773f2472b..492be63bca57f0cb0af218f386d9ea47a8b08652 100644 --- a/src/communication/export_manager.hpp +++ b/src/communication/export_manager.hpp @@ -31,43 +31,54 @@ public: { return; } - if (single_file_per_rank) { // single file per rank + // single file per rank + 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)); } - - if (!single_file_per_rank) { // single file per simulation + // single file per simulation + if (!single_file_per_rank) { single_exporters_.push_back( nest::mc::util::make_unique< - nest::mc::communication::exporter_spike_single_file<Time, CommunicationPolicy> >( + nest::mc::communication::exporter_spike_file<Time, CommunicationPolicy> >( file_name, output_path, file_extention, over_write)); } } - void do_export_rank(const std::vector<spike_type>& spikes) + void do_export_local(const std::vector<spike_type>& spikes) { - // TODO: do the buffering of the spikes here and not in the - // exporters itself!!! + local_spikes_.insert(std::end(local_spikes_), + std::begin(spikes), std::end(spikes)); + for (auto &exporter : rank_exporters_) { - exporter->add_and_export(spikes); + exporter->do_export(spikes); } + + local_spikes_.clear(); } - void do_export_single(const std::vector<spike_type>& spikes) + void do_export_global(const std::vector<spike_type>& spikes) { + // 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)); + + for (auto &exporter : single_exporters_) { - exporter->add_and_export(spikes); + exporter->do_export(spikes); } + + global_spikes_.clear(); } private: @@ -76,12 +87,15 @@ private: std::vector<std::unique_ptr<exporter_interface<Time, CommunicationPolicy> > > single_exporters_; - CommunicationPolicy communication_policy_; + + // local storage for sending spikes + std::vector<spike_type> local_spikes_; + std::vector<spike_type> global_spikes_; }; } //communication } // namespace mc -} // namespace nest \ No newline at end of file +} // namespace nest diff --git a/src/communication/exporter_interface.hpp b/src/communication/exporter_interface.hpp index 3fb101a7dc4eeef06acc2f8f6de5a99f8fc8b638..8093ec3fb60b2fd406cf3d061864fa6b30ac57f0 100644 --- a/src/communication/exporter_interface.hpp +++ b/src/communication/exporter_interface.hpp @@ -18,15 +18,7 @@ public: using spike_type = spike<cell_member_type, time_type>; // Performs the export of the data, thread safe buffered - virtual void do_export() = 0; - - // Add data to the internal storage to be exported - // Does not do the actual export - virtual void add_data(std::vector<spike_type>) = 0; - - - virtual void add_and_export(const std::vector<spike_type>&) = 0; - + virtual void do_export(const std::vector<spike_type>&) = 0; // Internal state is ok // Export might encounter problems in a separate thread. diff --git a/src/communication/exporter_spike_file.hpp b/src/communication/exporter_spike_file.hpp index 033fa97d3ff5749b4107c444bde9f5e357bca476..41e83f16aac61c142955c14e2817bf77f4bf7981 100644 --- a/src/communication/exporter_spike_file.hpp +++ b/src/communication/exporter_spike_file.hpp @@ -31,7 +31,8 @@ public: // exporter_spike_file(std::string file_name, std::string path, - std::string file_extention, bool over_write=true) + std::string file_extention, bool over_write=true, + bool single_file_per_rank=true) : ok_(false) { @@ -52,33 +53,18 @@ public: } buffer = new char[length]; - /* - // Manually setting buffer did not help (version 1) - //http://stackoverflow.com/questions/12997131/stdfstream-buffering-vs-manual-buffering-why-10x-gain-with-manual-buffering - - - file_handle_ = nest::mc::util::make_unique<std::ofstream>(); - file_handle_->rdbuf()->pubsetbuf(buffer, length); - file_handle_ ->open(file_path, std::fstream::app); - */ - file_handle_ = nest::mc::util::make_unique<std::ofstream>(file_path, std::fstream::app); if (file_handle_->good()) { ok_ = true; } - - // Force output of the spike times with precision - // TODO: We need to make this selectable - file_handle_->precision(4); - file_handle_->setf(std::ios::fixed, std::ios::floatfield); } // Performs the export of the data, // Does not throw - void do_export() override + void do_export(const std::vector<spike_type>& spikes) override { unsigned current_loc_in_buffer = 0; unsigned nr_chars_written = 0; @@ -88,7 +74,7 @@ public: const char * space = " "; const char * endline = "\n"; - for (auto spike : spikes_) + for (auto spike : spikes) { // First the id as output nr_chars_written = std::snprintf(single_value_buffer, 20, "%u", @@ -119,6 +105,7 @@ public: current_loc_in_buffer = 0; } } + // also write to buffer at end of the spikes processing if (current_loc_in_buffer != 0) { @@ -126,6 +113,7 @@ public: current_loc_in_buffer = 0; // not needed } + file_handle_->flush(); @@ -133,23 +121,6 @@ public: ok_ = false; } - spikes_.clear(); - } - - // Add data to the internal storage to be exported - // Does not do the actual export - void add_data(std::vector<spike_type>spikes) override - { - spikes_.insert(std::end(spikes_), - std::begin(spikes), std::end(spikes)); - } - - // Add and export data to file in a single function - void add_and_export(const std::vector<spike_type>& spikes) override //std::vector<spike_type>spikes - { - - add_data(spikes); - do_export(); } // Internal state is ok @@ -179,13 +150,10 @@ private: // Handle to our owned opened file handle std::unique_ptr<std::ofstream> file_handle_; - // local storage for sending spikes - std::vector<spike_type> spikes_; - communication_policy_type communication_policy_; + // Buffer (and size) for raw output of spikes char *buffer; - const unsigned int length = 4096; }; diff --git a/src/communication/exporter_spike_single_file.hpp b/src/communication/exporter_spike_single_file.hpp deleted file mode 100644 index 249e7583b28b6043e20b719ba8d1d9facfb8c339..0000000000000000000000000000000000000000 --- a/src/communication/exporter_spike_single_file.hpp +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include <cstdio> -#include <fstream> -#include <iomanip> -#include <memory> -#include <random> -#include <stdexcept> -#include <vector> - -#include <common_types.hpp> -#include <util.hpp> -#include <spike.hpp> - - -#include "exporter_interface.hpp" - -namespace nest { -namespace mc { -namespace communication { - -template <typename Time, typename CommunicationPolicy> // TODO: Templating on data type, for now only spike_type -class exporter_spike_single_file : public exporter_interface<Time, CommunicationPolicy> { - -public: - using time_type = Time; - using spike_type = spike<cell_member_type, time_type>; - using communication_policy_type = CommunicationPolicy; - - // - exporter_spike_single_file(std::string file_name, std::string path, - std::string file_extention, bool over_write=true) - : - ok_(false) - { - if (!communication_policy_.id() == 0) { - ok_ = true; - return; - } - - std::string file_path(create_output_file_path( - file_name, path, file_extention, communication_policy_.id())); - - //test if the file exist and depending on over_write throw or delete - std::ifstream f(file_path); - if (f.good()) { - if (!over_write) { - std::string error_string("Tried opening file for writing but it exists and over_write is false:\n" + - file_path); - - throw std::runtime_error(error_string); - } - - std::remove(file_path.c_str()); - } - - file_handle_ = nest::mc::util::make_unique<std::ofstream>(file_path, - std::fstream::app); - - if (file_handle_->good()) { - ok_ = true; - } - - // Force output of the spike times with precision - // TODO: We need to make this selectable - file_handle_->precision(4); - file_handle_->setf(std::ios::fixed, std::ios::floatfield); - } - - - // Performs the export of the data, - // Does not throw - void do_export() override - { - for (auto spike : spikes_) { - *file_handle_ << spike.source.gid << " " << spike.time << std::endl; - } - if (!file_handle_->good()){ - ok_ = false; - } - - spikes_.clear(); - } - - // Add data to the internal storage to be exported - // Does not do the actual export - void add_data(std::vector<spike_type>spikes) override - { - - spikes_.insert(std::end(spikes_), - std::begin(spikes), std::end(spikes)); - } - - // Add and export data to file in a single function - void add_and_export(const std::vector<spike_type>& spikes) override - { - add_data(spikes); - do_export(); - } - - // Internal state is ok - // We are working with fstreams possibly on a seperate thread - // We need a way to assertain the status of the stream - bool ok() const override - { - - return ok_ && file_handle_->good(); - } - - // Creates an indexed filename - static std::string create_output_file_path(std::string file_name, std::string path, - std::string file_extention, unsigned index) - { - std::string file_path = path + file_name + "_" + std::to_string(index) + - "." + file_extention; - // TODO: Nest does not produce the indexing for nrank == 0 - // I have the feeling this disrupts consistent output. Id rather - // always put the zero in. - return file_path; - } - -private: - // Are we in a valid state? - bool ok_; - - // Handle to our owned opened file handle - std::unique_ptr<std::ofstream> file_handle_; - - // local storage for sending spikes - std::vector<spike_type> spikes_; - - communication_policy_type communication_policy_; -}; - -} //communication -} // namespace mc -} // namespace nest diff --git a/src/model.hpp b/src/model.hpp index 816b32611ae3565df7fafe360929b813e92b054c..5cf4a71fde0dab9d2f45cef0dcc657f3002d09d7 100644 --- a/src/model.hpp +++ b/src/model.hpp @@ -172,8 +172,8 @@ public: PE("stepping", "exchange"); auto local_spikes = previous_spikes().gather(); future_events() = communicator_.exchange(local_spikes, - [&](const std::vector<spike_type>& spikes) { exporter_->do_export_rank(spikes); }, - [&] (const std::vector<spike_type>& spikes){ exporter_->do_export_single(spikes); }); + [&](const std::vector<spike_type>& spikes) { exporter_->do_export_local(spikes); }, + [&] (const std::vector<spike_type>& spikes){ exporter_->do_export_global(spikes); }); PL(2); }; diff --git a/tests/global_communication/CMakeLists.txt b/tests/global_communication/CMakeLists.txt index dff31ad71c6ac5c30a840073fb012405f652a584..82bb6583ea1310db1657e2a5110143fc484abd86 100644 --- a/tests/global_communication/CMakeLists.txt +++ b/tests/global_communication/CMakeLists.txt @@ -3,7 +3,6 @@ set(HEADERS ) set(COMMUNICATION_SOURCES test_exporter_spike_file.cpp - test_exporter_spike_single_file.cpp # unit test driver test.cpp ) diff --git a/tests/global_communication/test_exporter_spike_file.cpp b/tests/global_communication/test_exporter_spike_file.cpp index 12f3300131166e97c498bb8f0a8e6384fc68a933..6201c73103dda2bcce10e68c638798ac78bab384 100644 --- a/tests/global_communication/test_exporter_spike_file.cpp +++ b/tests/global_communication/test_exporter_spike_file.cpp @@ -97,11 +97,9 @@ TEST_F(exporter_spike_file_fixture, do_export) spikes.push_back({ { 1, 0 }, 1.0 }); spikes.push_back({ { 1, 0 }, 1.1 }); - // add to the exporter - exporter.add_data(spikes); // now do the export - exporter.do_export(); + exporter.do_export(spikes); // Test if we have spikes in the file std::ifstream f(get_standard_file_name()); diff --git a/tests/global_communication/test_exporter_spike_single_file.cpp b/tests/global_communication/test_exporter_spike_single_file.cpp deleted file mode 100644 index 8f78aa7c45882c6da26c715286523b37b3d4f5c3..0000000000000000000000000000000000000000 --- a/tests/global_communication/test_exporter_spike_single_file.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "gtest.h" - -#include <cstdio> -#include <fstream> -#include <iostream> -#include <string> -#include <vector> - -#include <communication/communicator.hpp> -#include <communication/global_policy.hpp> -#include <communication/exporter_spike_single_file.hpp> - -class exporter_spike_single_file_fixture : public ::testing::Test { - -protected: - using time_type = float; - using communicator_type = nest::mc::communication::global_policy; - - using spike_type = nest::mc::communication::exporter_spike_single_file<time_type, - communicator_type>::spike_type; - - using exporter_type = nest::mc::communication::exporter_spike_single_file<time_type, communicator_type>; - std::string file_name; - std::string path; - std::string extention; - unsigned index; - - exporter_spike_single_file_fixture() - : - file_name("spikes_exporter_spike_single_file_fixture"), - path("./"), - extention("gdf"), - index(0) - {} - - std::string get_standard_file_name() - { - return exporter_type::create_output_file_path( - file_name, path, extention, index); - } - - void SetUp() { - // code here will execute just before the test ensues - } - - void TearDown() { - // delete the start create file - std::remove(get_standard_file_name().c_str()); - } - - ~exporter_spike_single_file_fixture() { - - } -}; - -TEST_F(exporter_spike_single_file_fixture, constructor) -{ - exporter_type exporter(file_name, - path, extention); - - // after construction the state of the exporter should be valid - EXPECT_TRUE(exporter.ok()); - - - //test if the file exist and depending on over_write throw or delete - std::ifstream f(get_standard_file_name()); - EXPECT_TRUE(f.good()); -} - -TEST_F(exporter_spike_single_file_fixture, create_output_file_path) -{ - // Create some random paths, no need for fancy tests here - std::string produced_filename = - exporter_type::create_output_file_path( - "spikes", "./", "gdf", 0); - EXPECT_STREQ(produced_filename.c_str(), "./spikes_0.gdf"); - - produced_filename = - exporter_type::create_output_file_path( - "a_name", "../../", "txt", 5); - EXPECT_STREQ(produced_filename.c_str(), "../../a_name_5.txt"); -} - -TEST_F(exporter_spike_single_file_fixture, do_export) -{ - - - exporter_type exporter(file_name, - path, extention); - - // Create some spikes - std::vector<spike_type> spikes; - spikes.push_back({ { 0, 0 }, 0.0}); - spikes.push_back({ { 0, 0 }, 0.1 }); - spikes.push_back({ { 1, 0 }, 1.0 }); - spikes.push_back({ { 1, 0 }, 1.1 }); - - // add to the exporter - exporter.add_data(spikes); - - // now do the export - exporter.do_export(); - - // Test if we have spikes in the file - std::ifstream f(get_standard_file_name()); - EXPECT_TRUE(f.good()); - - std::string line; - - EXPECT_TRUE(std::getline(f, line)); - EXPECT_STREQ(line.c_str(), "0 0.0000"); - EXPECT_TRUE(std::getline(f, line)); - EXPECT_STREQ(line.c_str(), "0 0.1000"); - EXPECT_TRUE(std::getline(f, line)); - EXPECT_STREQ(line.c_str(), "1 1.0000"); - EXPECT_TRUE(std::getline(f, line)); - EXPECT_STREQ(line.c_str(), "1 1.1000"); -} diff --git a/tests/performance/io/disk_io.cpp b/tests/performance/io/disk_io.cpp index 98868f2bf1ee6e8d4dcd56ae31b0758751bf4ca7..e0a2c7d7e6a5889d4028819ee853198ac319741f 100644 --- a/tests/performance/io/disk_io.cpp +++ b/tests/performance/io/disk_io.cpp @@ -113,7 +113,7 @@ int main(int argc, char** argv) { { int time_start = clock(); - manager.do_export_rank(spikes); + manager.do_export_local(spikes); int time_stop = clock();