Skip to content
This repository was archived by the owner on Mar 20, 2023. It is now read-only.

Commit 307016b

Browse files
authored
Write spike output to out.dat with parallel i/o when MPI is enabled (#71)
1 parent fd00a22 commit 307016b

File tree

5 files changed

+108
-14
lines changed

5 files changed

+108
-14
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,10 @@ Note that celsius and dt, if not specified, will get their values from the model
211211

212212
# Results
213213

214-
Currently CoreNEURON only outputs spike data. When running the simulation, each MPI rank writes spike information
215-
into a file `out.#mpi_rank`. These files should be combined and sorted to compare with NEURON spike output.
214+
Currently CoreNEURON only outputs spike data. Spike output file need to be sorted to compare with NEURON:
216215

217216
```
218-
cat out[0-9]*.dat | sort -k 1n,1n -k 2n,2n > out.spk
217+
sort -k 1n,1n -k 2n,2n out.dat > out.spk
219218
```
220219

221220
# Running tests

coreneuron/nrniv/output_spikes.cpp

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ THE POSSIBILITY OF SUCH DAMAGE.
2727
*/
2828

2929
#include <iostream>
30+
#include <sstream>
31+
#include <string.h>
3032
#include <stdexcept> // std::lenght_error
3133
#include <vector>
3234
#include "coreneuron/nrnconf.h"
3335
#include "coreneuron/nrniv/nrniv_decl.h"
3436
#include "coreneuron/nrniv/output_spikes.h"
3537
#include "coreneuron/nrnmpi/nrnmpi.h"
3638
#include "coreneuron/nrniv/nrnmutdec.h"
37-
#include "coreneuron/utils/sdprintf.h"
39+
#include "coreneuron/nrnmpi/nrnmpi_impl.h"
40+
#include "coreneuron/nrnmpi/nrnmpidec.h"
3841

3942
std::vector<double> spikevec_time;
4043
std::vector<int> spikevec_gid;
@@ -55,28 +58,113 @@ static MUTDEC
5558
void spikevec_lock() {
5659
MUTLOCK
5760
}
61+
5862
void spikevec_unlock() {
5963
MUTUNLOCK
6064
}
6165

62-
void output_spikes(const char* outpath) {
63-
char fnamebuf[100];
64-
sd_ptr fname = sdprintf(fnamebuf, sizeof(fnamebuf), "%s/out%d.dat", outpath, nrnmpi_myid);
65-
FILE* f = fopen(fname, "w");
66+
#if NRNMPI
67+
/** Write generated spikes to out.dat using mpi parallel i/o.
68+
* \todo : MPI related code should be factored into nrnmpi.c
69+
* Check spike record length which is set to 64 chars
70+
*/
71+
void output_spikes_parallel(const char* outpath) {
72+
std::stringstream ss;
73+
ss << outpath << "/out.dat";
74+
std::string fname = ss.str();
75+
76+
// remove if file already exist
77+
if(nrnmpi_myid == 0) {
78+
remove(fname.c_str());
79+
}
80+
nrnmpi_barrier();
81+
82+
// each spike record in the file is time + gid (64 chars sufficient)
83+
const int SPIKE_RECORD_LEN = 64;
84+
unsigned num_spikes = spikevec_gid.size();
85+
unsigned num_bytes = (sizeof(char) * num_spikes * SPIKE_RECORD_LEN);
86+
char *spike_data = (char*) malloc(num_bytes);
87+
88+
if(spike_data == NULL) {
89+
printf("Error while writing spikes due to memory allocation\n");
90+
return;
91+
}
92+
93+
// empty if no spikes
94+
strcpy(spike_data, "");
95+
96+
// populate buffer with all spike entries
97+
char spike_entry[SPIKE_RECORD_LEN];
98+
for(unsigned i = 0; i < num_spikes; i++) {
99+
snprintf(spike_entry, 64, "%.8g\t%d\n", spikevec_time[i], spikevec_gid[i]);
100+
strcat(spike_data, spike_entry);
101+
}
102+
103+
// calculate offset into global file. note that we don't write
104+
// all num_bytes but only "populated" buffer
105+
unsigned long num_chars = strlen(spike_data);
106+
unsigned long offset = 0;
107+
108+
// global offset into file
109+
MPI_Exscan(&num_chars, &offset, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
110+
111+
// write to file using parallel mpi i/o
112+
MPI_File fh;
113+
MPI_Status status;
114+
115+
// ibm mpi (bg-q) expects char* instead of const char* (even though it's standard)
116+
int op_status = MPI_File_open(MPI_COMM_WORLD, (char*) fname.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
117+
if(op_status != MPI_SUCCESS && nrnmpi_myid == 0) {
118+
std::cerr << "Error while opening spike output file " << fname << std::endl;
119+
abort();
120+
}
121+
122+
op_status = MPI_File_write_at_all(fh, offset, spike_data, num_chars, MPI_BYTE, &status);
123+
if(op_status != MPI_SUCCESS && nrnmpi_myid == 0) {
124+
std::cerr << "Error while writing spike output " << std::endl;
125+
abort();
126+
}
127+
128+
MPI_File_close(&fh);
129+
}
130+
#endif
131+
132+
void output_spikes_serial(const char* outpath) {
133+
std::stringstream ss;
134+
ss << outpath << "/out.dat";
135+
std::string fname = ss.str();
136+
137+
// remove if file already exist
138+
remove(fname.c_str());
139+
140+
FILE* f = fopen(fname.c_str(), "w");
66141
if (!f && nrnmpi_myid == 0) {
67142
std::cout << "WARNING: Could not open file for writing spikes." << std::endl;
68143
return;
69144
}
70145

71-
for (int i = 0; i < spikevec_gid.size(); ++i)
146+
for (unsigned i = 0; i < spikevec_gid.size(); ++i)
72147
if (spikevec_gid[i] > -1)
73148
fprintf(f, "%.8g\t%d\n", spikevec_time[i], spikevec_gid[i]);
74149

75150
fclose(f);
76151
}
77152

153+
void output_spikes(const char* outpath) {
154+
#if NRNMPI
155+
if(nrnmpi_initialized()) {
156+
output_spikes_parallel(outpath);
157+
} else {
158+
output_spikes_serial(outpath);
159+
}
160+
#else
161+
output_spikes_serial(outpath);
162+
#endif
163+
}
164+
165+
78166
void validation(std::vector<std::pair<double, int> >& res) {
79-
for (int i = 0; i < spikevec_gid.size(); ++i)
167+
for (unsigned i = 0; i < spikevec_gid.size(); ++i)
80168
if (spikevec_gid[i] > -1)
81169
res.push_back(std::make_pair(spikevec_time[i], spikevec_gid[i]));
82170
}

coreneuron/nrnmpi/nrnmpi.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,11 @@ void nrn_fatal_error(const char* msg) {
207207
}
208208
nrn_abort(-1);
209209
}
210+
211+
int nrnmpi_initialized() {
212+
int flag = 0;
213+
#if NRNMPI
214+
MPI_Initialized(&flag);
215+
#endif
216+
return flag;
217+
}

coreneuron/nrnmpi/nrnmpidec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ extern long nrnmpi_long_allreduce(long x, int type);
125125
extern void nrnmpi_dbl_allreduce_vec(double* src, double* dest, int cnt, int type);
126126
extern void nrnmpi_long_allreduce_vec(long* src, long* dest, int cnt, int type);
127127
extern void nrnmpi_dbl_allgather(double* s, double* r, int n);
128-
128+
extern int nrnmpi_initialized();
129129
#if NRN_MULTISEND
130130
extern void nrnmpi_multisend_comm();
131131
extern void nrnmpi_multisend(NRNMPI_Spike* spk, int n, int* hosts);

tests/integration/integration_test.sh.in

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@ fi
2424
# diff outputed files with reference
2525
cd @CMAKE_CURRENT_BINARY_DIR@/@SIM_NAME@
2626

27-
if [ ! -f out0.dat ]
27+
if [ ! -f out.dat ]
2828
then
2929
echo "No output files. Test failed!"
3030
exit 1
3131
fi
3232

33-
cat out[0-9]*.dat > out_cn.dat
34-
sort -k 1n,1n -k 2n,2n out_cn.dat > sort_out.dat
33+
sort -k 1n,1n -k 2n,2n out.dat > sort_out.dat
3534
diff -w sort_out.dat @CMAKE_CURRENT_SOURCE_DIR@/@SIM_NAME@/out.dat.ref > diff.dat 2>&1
3635

3736
if [ -s diff.dat ]

0 commit comments

Comments
 (0)