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

Commit 6efb972

Browse files
ohm314pramodk
authored andcommitted
Feat/sorted spike output (#89)
- Spikes are now sorted across MPI ranks according to time and gid before being written to file - Update build system to use c++11 by default and fix wrong directives - Use nrnmpi wrappers and extend wrappers by one function - Removed post-process sorting from integration tests - Updated endianness tests to conform with C++11 - Fix warnings with c++11/clang and update mod2c
1 parent 3b65992 commit 6efb972

File tree

11 files changed

+121
-35
lines changed

11 files changed

+121
-35
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ option(ENABLE_CUDA_MODULES "Enable modules like Random123 written in CUDA" ON)
6060
option(ENABLE_NET_RECEIVE_BUFFERING "Enable event buffering in net_receive function" ON)
6161
option(ENABLE_OMP_RUNTIME_SCHEDULE "Use runtime schedule for OpenMP" OFF)
6262

63+
## set C++11 standard to be default
64+
set(CMAKE_CXX_STANDARD 11)
65+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
66+
set(CMAKE_CXX_EXTENSIONS OFF)
6367

6468
set(FRONTEND_C_COMPILER gcc CACHE FILEPATH "C compiler for building mod2c [frontend executable]")
6569
set(FRONTEND_CXX_COMPILER g++ CACHE FILEPATH "C++ compiler for building mod2c [frontend executable]")

coreneuron/nrniv/nrn_stats.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@ void report_cell_stats(void) {
6969
stat_array[5] = (long)spikevec_gid.size(); // number of spikes
7070

7171
int spikevec_positive_gid_size = 0;
72-
for (int i = 0; i < spikevec_gid.size(); ++i)
73-
if (spikevec_gid[i] > -1)
72+
for (std::size_t i = 0; i < spikevec_gid.size(); ++i) {
73+
if (spikevec_gid[i] > -1) {
7474
spikevec_positive_gid_size++;
75+
}
76+
}
7577

7678
stat_array[6] = (long)spikevec_positive_gid_size; // number of non-negative gid spikes
7779

coreneuron/nrniv/output_spikes.cpp

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ THE POSSIBILITY OF SUCH DAMAGE.
3131
#include <string.h>
3232
#include <stdexcept> // std::lenght_error
3333
#include <vector>
34+
#include <algorithm>
35+
#include <numeric>
3436
#include "coreneuron/nrnconf.h"
3537
#include "coreneuron/nrniv/nrniv_decl.h"
3638
#include "coreneuron/nrniv/output_spikes.h"
@@ -65,6 +67,74 @@ void spikevec_unlock() {
6567
}
6668

6769
#if NRNMPI
70+
71+
void local_spikevec_sort(std::vector<double>& isvect,
72+
std::vector<int>& isvecg,
73+
std::vector<double>& osvect,
74+
std::vector<int>& osvecg) {
75+
osvect.resize(isvect.size());
76+
osvecg.resize(isvecg.size());
77+
// first build a permutation vector
78+
std::vector<std::size_t> perm(isvect.size());
79+
std::iota(perm.begin(), perm.end(), 0);
80+
// sort by gid (second predicate first)
81+
std::stable_sort(perm.begin(), perm.end(),
82+
[&](std::size_t i, std::size_t j) { return isvecg[i] < isvecg[j]; });
83+
// then sort by time
84+
std::stable_sort(perm.begin(), perm.end(),
85+
[&](std::size_t i, std::size_t j) { return isvect[i] < isvect[j]; });
86+
// now apply permutation to time and gid output vectors
87+
std::transform(perm.begin(), perm.end(), osvect.begin(),
88+
[&](std::size_t i) { return isvect[i]; });
89+
std::transform(perm.begin(), perm.end(), osvecg.begin(),
90+
[&](std::size_t i) { return isvecg[i]; });
91+
}
92+
93+
void sort_spikes(std::vector<double>& spikevec_time, std::vector<int>& spikevec_gid) {
94+
double lmin_time = *(std::min_element(spikevec_time.begin(), spikevec_time.end()));
95+
double lmax_time = *(std::max_element(spikevec_time.begin(), spikevec_time.end()));
96+
double min_time = nrnmpi_dbl_allmin(lmin_time);
97+
double max_time = nrnmpi_dbl_allmax(lmax_time);
98+
99+
// allocate send and receive counts and displacements for MPI_Alltoallv
100+
std::vector<int> snd_cnts(nrnmpi_numprocs);
101+
std::vector<int> rcv_cnts(nrnmpi_numprocs);
102+
std::vector<int> snd_dsps(nrnmpi_numprocs);
103+
std::vector<int> rcv_dsps(nrnmpi_numprocs);
104+
105+
double bin_t = (max_time - min_time) / nrnmpi_numprocs;
106+
// first find number of spikes in each time window
107+
for (const auto& st : spikevec_time) {
108+
int idx = (int)(st - min_time) / bin_t;
109+
snd_cnts[idx]++;
110+
}
111+
for (int i = 1; i < nrnmpi_numprocs; i++) {
112+
snd_dsps[i] = snd_dsps[i - 1] + snd_cnts[i - 1];
113+
}
114+
115+
// now let each rank know how many spikes they will receive
116+
// and get in turn all the buffer sizes to receive
117+
nrnmpi_int_alltoall(&snd_cnts[0], &rcv_cnts[0], 1);
118+
for (int i = 1; i < nrnmpi_numprocs; i++) {
119+
rcv_dsps[i] = rcv_dsps[i - 1] + rcv_cnts[i - 1];
120+
}
121+
std::size_t new_sz = 0;
122+
for (const auto& r : rcv_cnts) {
123+
new_sz += r;
124+
}
125+
// prepare new sorted vectors
126+
std::vector<double> svt_buf(new_sz, 0.0);
127+
std::vector<int> svg_buf(new_sz, 0);
128+
129+
// now exchange data
130+
nrnmpi_dbl_alltoallv(spikevec_time.data(), &snd_cnts[0], &snd_dsps[0], svt_buf.data(),
131+
&rcv_cnts[0], &rcv_dsps[0]);
132+
nrnmpi_int_alltoallv(spikevec_gid.data(), &snd_cnts[0], &snd_dsps[0], svg_buf.data(),
133+
&rcv_cnts[0], &rcv_dsps[0]);
134+
135+
local_spikevec_sort(svt_buf, svg_buf, spikevec_time, spikevec_gid);
136+
}
137+
68138
/** Write generated spikes to out.dat using mpi parallel i/o.
69139
* \todo : MPI related code should be factored into nrnmpi.c
70140
* Check spike record length which is set to 64 chars
@@ -78,6 +148,7 @@ void output_spikes_parallel(const char* outpath) {
78148
if (nrnmpi_myid == 0) {
79149
remove(fname.c_str());
80150
}
151+
sort_spikes(spikevec_time, spikevec_gid);
81152
nrnmpi_barrier();
82153

83154
// each spike record in the file is time + gid (64 chars sufficient)
@@ -136,6 +207,11 @@ void output_spikes_serial(const char* outpath) {
136207
ss << outpath << "/out.dat";
137208
std::string fname = ss.str();
138209

210+
// reserve some space for sorted spikevec buffers
211+
std::vector<double> sorted_spikevec_time(spikevec_time.size());
212+
std::vector<int> sorted_spikevec_gid(spikevec_gid.size());
213+
local_spikevec_sort(spikevec_time, spikevec_gid, sorted_spikevec_time, sorted_spikevec_gid);
214+
139215
// remove if file already exist
140216
remove(fname.c_str());
141217

@@ -145,9 +221,9 @@ void output_spikes_serial(const char* outpath) {
145221
return;
146222
}
147223

148-
for (unsigned i = 0; i < spikevec_gid.size(); ++i)
149-
if (spikevec_gid[i] > -1)
150-
fprintf(f, "%.8g\t%d\n", spikevec_time[i], spikevec_gid[i]);
224+
for (std::size_t i = 0; i < sorted_spikevec_gid.size(); ++i)
225+
if (sorted_spikevec_gid[i] > -1)
226+
fprintf(f, "%.8g\t%d\n", sorted_spikevec_time[i], sorted_spikevec_gid[i]);
151227

152228
fclose(f);
153229
}

coreneuron/nrnmpi/mpispike.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,13 +341,22 @@ void nrnmpi_assert_opstep(int opstep, double tt) {
341341

342342
double nrnmpi_dbl_allmin(double x) {
343343
double result;
344-
if (nrnmpi_numprocs < 2) {
344+
if (!nrnmpi_use || (nrnmpi_numprocs < 2)) {
345345
return x;
346346
}
347347
MPI_Allreduce(&x, &result, 1, MPI_DOUBLE, MPI_MIN, nrnmpi_comm);
348348
return result;
349349
}
350350

351+
double nrnmpi_dbl_allmax(double x) {
352+
double result;
353+
if (!nrnmpi_use || (nrnmpi_numprocs < 2)) {
354+
return x;
355+
}
356+
MPI_Allreduce(&x, &result, 1, MPI_DOUBLE, MPI_MAX, nrnmpi_comm);
357+
return result;
358+
}
359+
351360
static void pgvts_op(double* in, double* inout, int* len, MPI_Datatype* dptr) {
352361
int i, r = 0;
353362
if (*dptr != MPI_DOUBLE)

coreneuron/nrnmpi/nrnmpidec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ extern void nrnmpi_char_broadcast(char* buf, int cnt, int root);
110110
extern int nrnmpi_int_sum_reduce(int in);
111111
extern void nrnmpi_assert_opstep(int opstep, double t);
112112
extern double nrnmpi_dbl_allmin(double x);
113+
extern double nrnmpi_dbl_allmax(double x);
113114
extern int nrnmpi_pgvts_least(double* t, int* op, int* init);
114115
extern void nrnmpi_send_doubles(double* pd, int cnt, int dest, int tag);
115116
extern void nrnmpi_recv_doubles(double* pd, int cnt, int src, int tag);

coreneuron/nrnoc/eion.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ THE POSSIBILITY OF SUCH DAMAGE.
7171
namespace coreneuron {
7272

7373
#define nparm 5
74-
static char* mechanism[] = {/*just a template*/
74+
static const char* mechanism[] = {/*just a template*/
7575
"0", "na_ion", "ena", "nao", "nai", 0, "ina", "dina_dv_", 0, 0};
7676

7777
void nrn_init_ion(NrnThread*, Memb_list*, int);

coreneuron/scopmath_core/sparse_thread.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ int _cvode_sparse_thread(void** v, int n, int* x, SPFUN fun, _threadargsproto_)
224224
}
225225

226226
static int matsol(SparseObj* so, int _iml) {
227-
register Elm *pivot, *el;
227+
Elm *pivot, *el;
228228
unsigned i;
229229

230230
/* Upper triangularization */
@@ -244,7 +244,7 @@ static int matsol(SparseObj* so, int _iml) {
244244

245245
static void subrow(SparseObj* so, Elm* pivot, Elm* rowsub, int _iml) {
246246
double r;
247-
register Elm* el;
247+
Elm* el;
248248

249249
int _cntml_padded = so->_cntml_padded;
250250
r = rowsub->value[_iml] / pivot->value[_iml];
@@ -300,7 +300,7 @@ static void prmat(SparseObj* so) {
300300

301301
static void initeqn(SparseObj* so, unsigned maxeqn) /* reallocate space for matrix */
302302
{
303-
register unsigned i, nn;
303+
unsigned i, nn;
304304

305305
if (maxeqn == so->neqn)
306306
return;
@@ -361,7 +361,7 @@ saves much time allocating and freeing during the solve phase
361361
static Elm* getelm(SparseObj* so, unsigned row, unsigned col, Elm* new_elem)
362362
/* return pointer to row col element maintaining order in rows */
363363
{
364-
register Elm *el, *elnext;
364+
Elm *el, *elnext;
365365
unsigned vrow, vcol;
366366

367367
vrow = so->varord[row];

tests/integration/integration_test.sh.in

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ then
3030
exit 1
3131
fi
3232

33-
sort -k 1n,1n -k 2n,2n out.dat > sort_out.dat
34-
diff -w sort_out.dat @CMAKE_CURRENT_SOURCE_DIR@/@SIM_NAME@/out.dat.ref > diff.dat 2>&1
33+
diff -w out.dat @CMAKE_CURRENT_SOURCE_DIR@/@SIM_NAME@/out.dat.ref > diff.dat 2>&1
3534

3635
if [ -s diff.dat ]
3736
then

tests/unit/endian/endianness_test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ template <> const unsigned char check_data<T>::data[]
8686

8787
CHECK_DATA(char,'x','x')={'x'};
8888

89-
CHECK_DATA(float,(float)0x1.8a4782p+79,(float)-0x1.468acep+3)=
90-
{0xc1,0x23,0x45,0x67};
89+
CHECK_DATA(float,(float)1.1,(float)-428967904.0)=
90+
{0xcd,0xcc,0x8c,0x3f};
9191

92-
CHECK_DATA(double,-0x1.dab89674523c1p+45,-0x1.3456789abcdc2p+19)=
93-
{0xc1,0x23,0x45,0x67,0x89,0xab,0xcd,0xc2};
92+
CHECK_DATA(double,1.1,-1.5423487136706484e-180)=
93+
{0x9a,0x99,0x99,0x99,0x99,0x99,0xf1,0x3f};
9494

9595
CHECK_DATA(uint16_t,0xf12e,0x2ef1)=
9696
{0x2e,0xf1};

0 commit comments

Comments
 (0)