MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
test_mpsort.c
Go to the documentation of this file.
1 #include <stdarg.h>
2 #include <stddef.h>
3 #include <setjmp.h>
4 #include <cmocka.h>
5 #include <stdio.h>
6 
7 #include "stub.h"
8 
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <time.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include <mpi.h>
16 #include "../utils/endrun.h"
17 #include "../utils/mymalloc.h"
18 #include "../utils/mpsort.h"
19 
20 struct BaseGroup {
21  int OriginalTask;
22  int Length;
23  uint64_t MinID;
24  int MinIDTask;
25 };
26 
27 static void radix_int(const void * ptr, void * radix, void * arg) {
28  *(uint64_t*)radix = *(const int64_t*) ptr + INT64_MIN;
29 }
30 
31 static int64_t
32 checksum(int64_t * data, size_t localsize, MPI_Comm comm)
33 {
34  int64_t sum = 0;
35  size_t i;
36  for(i = 0; i < localsize; i ++) {
37  sum += data[i];
38  }
39 
40  MPI_Allreduce(MPI_IN_PLACE, &sum, 1, MPI_LONG, MPI_SUM, comm);
41  return sum;
42 }
43 static void
44 generate(int64_t * data, size_t localsize, int bits, int seed)
45 {
46  /* only keep bits of precision. */
47  srandom(seed);
48 
49  size_t i;
50  unsigned shift = 64u - bits;
51  for(i = 0; i < localsize; i ++) {
52  uint64_t value = (int64_t) random() * (int64_t) random() * random() * random();
53  data[i] = (signed) ((value << shift));
54  }
55 }
56 
57 static void
58 check_sorted(void * data, int elsize, size_t localsize, int compar(void * d1, void * d2), MPI_Comm comm)
59 {
60  size_t i;
61  int ThisTask, NTask;
62  MPI_Comm_rank(comm, &ThisTask);
63  MPI_Comm_size(comm, &NTask);
64 
65  const int TAG = 0xbeef;
66 
67  for(i = 1; i < localsize; i ++) {
68  if(compar(data + i*elsize, data + (i - 1)*elsize) < 0) {
69 // struct BaseGroup * i1 = ((struct BaseGroup *)data) + i;
70 // struct BaseGroup * i2= ((struct BaseGroup *)data) +(i-1);
71 // message(1, "cur=(%ld %ld %ld) prev= (%ld %ld %ld)\n", labs(i1->OriginalTask - i1->MinIDTask), i1->MinID, i1->Length, labs(i2->OriginalTask - i2->MinIDTask), i2->MinID, i2->Length);
72  endrun(12, "Ordering of local array is broken i=%ld, d=%ld d-1=%ld. \n", i, ((int64_t *)data)[i], ((int64_t *)data)[i-1]);
73  }
74  assert_true(compar(data + i*elsize, data + (i - 1)*elsize) >=0);
75  }
76 
77  if(NTask == 1) return;
78 
79  char * prev = ta_malloc("prev", char, elsize);
80  memset(prev, -1000000, elsize);
81 
82  while(1) {
83  if(ThisTask == 0) {
84  void * ptr = prev;
85  if(localsize > 0) {
86  ptr = data + elsize * (localsize - 1);
87  }
88  MPI_Send(ptr, elsize, MPI_BYTE, ThisTask + 1, TAG, comm);
89  break;
90  }
91  if(ThisTask == NTask - 1) {
92  MPI_Recv(prev, elsize, MPI_BYTE,
93  ThisTask - 1, TAG, comm, MPI_STATUS_IGNORE);
94  break;
95  }
96  /* else */
97  if(localsize == 0) {
98  /* simply pass through whatever we get */
99  MPI_Recv(prev, elsize, MPI_BYTE, ThisTask - 1, TAG, comm, MPI_STATUS_IGNORE);
100  MPI_Send(prev, elsize, MPI_BYTE, ThisTask + 1, TAG, comm);
101  break;
102  }
103  else
104  {
105  MPI_Sendrecv(
106  data+(localsize - 1)*elsize, elsize, MPI_BYTE,
107  ThisTask + 1, TAG,
108  prev, elsize, MPI_BYTE,
109  ThisTask - 1, TAG, comm, MPI_STATUS_IGNORE);
110  break;
111  }
112  }
113 
114  if(ThisTask > 1) {
115  if(localsize > 0) {
116 // printf("ThisTask = %d prev = %d\n", ThisTask, prev);
117  if(compar(prev, data) > 0) {
118  endrun(12, "Ordering of global array is broken prev=%ld d=%ld (comp: %d). \n", prev, *(int64_t *)data, compar(prev, data));
119  }
120  assert_true(compar(prev, data) <= 0);
121  }
122  }
123 }
124 
125 int compar_int(void * d1, void * d2)
126 {
127  int64_t * i1 = d1;
128  int64_t * i2 = d2;
129  if(*i1 < *i2)
130  return -1;
131  else if(*i1 == *i2)
132  return 0;
133  else
134  return 1;
135 }
136 
137 static void
138 do_mpsort_test(int64_t srcsize, int bits, int staggered, int gather)
139 {
140  int ThisTask;
141  int NTask;
142 
143  MPI_Comm_size(MPI_COMM_WORLD, &NTask);
144  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
145 
146  if(gather == 1)
148  if(gather == 0)
150 
151 // message(0, "NTask = %d\n", NTask);
152 // message(0, "src size = %ld\n", srcsize);
153 
154  if(staggered && (ThisTask % 2 == 0)) srcsize = 0;
155 
156  int64_t csize;
157 
158  MPI_Allreduce(&srcsize, &csize, 1, MPI_LONG, MPI_SUM, MPI_COMM_WORLD);
159 
160  int64_t destsize = csize * (ThisTask + 1) / NTask - csize * (ThisTask) / NTask;
161 
162  message(0, "dest size = %ld\n", destsize);
163 // message(0, "csize = %ld\n", csize);
164 
165  int64_t * src = mymalloc("src", srcsize * sizeof(int64_t));
166  int64_t * dest = mymalloc("dest", destsize * sizeof(int64_t));
167 
168  int seed = 9999 * ThisTask;
169  generate(src, srcsize, bits, seed);
170 
171  int64_t srcsum = checksum(src, srcsize, MPI_COMM_WORLD);
172 // if(ThisTask == 0)
173 // mpsort_setup_timers(512);
174  {
175  double start = MPI_Wtime();
176 
177  mpsort_setup_timers(512);
178  mpsort_mpi_newarray(src, srcsize,
179  dest, destsize,
180  sizeof(int64_t),
181  radix_int, sizeof(int64_t), NULL,
182  MPI_COMM_WORLD);
183 
184  MPI_Barrier(MPI_COMM_WORLD);
185  double end = MPI_Wtime();
186 
187  int64_t destsum = checksum(dest, destsize, MPI_COMM_WORLD);
188 
189  if(destsum != srcsum) {
190  endrun(5, "MPSort checksum is inconsistent.\n");
191  MPI_Abort(MPI_COMM_WORLD, -1);
192  }
193 
194  check_sorted(dest, sizeof(int64_t), destsize, compar_int, MPI_COMM_WORLD);
195 
196  message(0, "MPSort total time: %g\n", end - start);
197 // if(ThisTask == 0) {
198 // mpsort_mpi_report_last_run();
199 // mpsort_free_timers();
200 // }
201  }
202 
204  myfree(dest);
205  myfree(src);
206 }
207 
208 static void fof_radix_Group_TotalCountTaskDiffMinID(const void * a, void * radix, void * arg) {
209  uint64_t * u = (uint64_t *) radix;
210  struct BaseGroup * f = (struct BaseGroup *) a;
211  u[0] = labs(f->OriginalTask - f->MinIDTask);
212  u[1] = f->MinID;
213  u[2] = UINT64_MAX - (f->Length);
214 }
215 
216 int compar_bg(void * d1, void * d2)
217 {
218  struct BaseGroup * i1 = d1;
219  struct BaseGroup * i2 = d2;
220  int dist1 = labs(i1->OriginalTask - i1->MinIDTask);
221  int dist2 = labs(i2->OriginalTask - i2->MinIDTask);
222  /* Note reversed sign! We want the largest groups first.*/
223  if(i1->Length < i2->Length)
224  return 1;
225  else if(i1->Length > i2->Length)
226  return -1;
227  if(i1->MinID < i2->MinID)
228  return -1;
229  else if(i1->MinID > i2->MinID)
230  return 1;
231  else if(dist1 < dist2)
232  return -1;
233  else if(dist1 > dist2)
234  return 1;
235  return 0;
236 }
237 
238 static uint64_t
239 checksum_minid(struct BaseGroup * data, size_t localsize, MPI_Comm comm)
240 {
241  uint64_t sum = 0;
242  size_t i;
243  for(i = 0; i < localsize; i ++) {
244  sum += data[i].MinID;
245  }
246 
247  MPI_Allreduce(MPI_IN_PLACE, &sum, 1, MPI_LONG, MPI_SUM, comm);
248  return sum;
249 }
250 
251 /* Tests sorting with a very long radix, as is done in the FOF code*/
252 static void
253 do_long_radix_test(int srcsize)
254 {
255  int ThisTask;
256  int NTask;
257 
258  MPI_Comm_size(MPI_COMM_WORLD, &NTask);
259  MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
260 
261  struct BaseGroup * base = mymalloc("base", srcsize * sizeof(struct BaseGroup));
262 
263  int i;
264  for(i = 0; i < srcsize; i++)
265  {
266  base[i].OriginalTask = ThisTask; /* original task */
267  base[i].MinIDTask = random() % NTask;
268  base[i].MinID = random();
269  base[i].Length = 10;
270  }
271 
272  int64_t srcsum = checksum_minid(base, srcsize, MPI_COMM_WORLD);
273  {
274  double start = MPI_Wtime();
275 
276  mpsort_mpi_newarray(base, srcsize,
277  base, srcsize,
278  sizeof(struct BaseGroup),
280  MPI_COMM_WORLD);
281 
282  MPI_Barrier(MPI_COMM_WORLD);
283  double end = MPI_Wtime();
284 
285  int64_t destsum = checksum_minid(base, srcsize, MPI_COMM_WORLD);
286 
287  if(destsum != srcsum) {
288  endrun(5, "MPSort checksum is inconsistent.\n");
289  MPI_Abort(MPI_COMM_WORLD, -1);
290  }
291 
292  check_sorted(base, sizeof(struct BaseGroup), srcsize, compar_bg, MPI_COMM_WORLD);
293 
294  message(0, "MPSort total time: %g\n", end - start);
295  }
296  myfree(base);
297 }
298 
299 static void
300 test_basegroup(void ** state)
301 {
302  do_long_radix_test(50);
303  /* This is chosen because it fails in travis*/
305 }
306 
307 static void
308 test_mpsort_bits(void ** state)
309 {
310  message(0, "16 bits!\n");
311  /* With whatever gather we like*/
312  do_mpsort_test(2000, 16, 0, -1);
313  message(0, "32 bits!\n");
314  do_mpsort_test(2000, 32, 0, -1);
315  message(0, "64 bits!\n");
316  do_mpsort_test(2000, 64, 0, -1);
317 }
318 
319 static void
320 test_mpsort_stagger(void ** state)
321 {
322  /* With stagger*/
323  do_mpsort_test(2000, 32, 1, -1);
324  /* Use a number that doesn't divide evenly so we get a different destsize*/
325  do_mpsort_test(1999, 32, 0, -1);
326 }
327 
328 static void
329 test_mpsort_gather(void ** state)
330 {
331  /* With forced gather*/
332  do_mpsort_test(2000, 32, 0, 1);
333  /* Without forced gather*/
334  do_mpsort_test(2000, 32, 0, 0);
335 }
336 
337 int main(void)
338 {
339  const struct CMUnitTest tests[] = {
340  cmocka_unit_test(test_mpsort_bits),
341  cmocka_unit_test(test_mpsort_stagger),
342  cmocka_unit_test(test_basegroup),
343  cmocka_unit_test(test_mpsort_gather),
344  };
345  return cmocka_run_group_tests_mpi(tests, NULL, NULL);
346 }
void message(int where, const char *fmt,...)
Definition: endrun.c:175
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
void mpsort_mpi_unset_options(int options)
Definition: mpsort.c:1451
void mpsort_mpi_set_options(int options)
Definition: mpsort.c:1437
void mpsort_setup_timers(int ntimers)
Definition: mpsort.c:736
#define mpsort_mpi_newarray(base, nmemb, out, outnmemb, elsize, radix, rsize, arg, comm)
Definition: mpsort.h:30
#define MPSORT_REQUIRE_GATHER_SORT
Definition: mpsort.h:6
#define MPSORT_DISABLE_GATHER_SORT
Definition: mpsort.h:5
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define ta_malloc(name, type, nele)
Definition: mymalloc.h:25
#define myfree(x)
Definition: mymalloc.h:19
Definition: fof.h:13
MyIDType MinID
Definition: fof.h:18
int Length
Definition: fof.h:16
int OriginalTask
Definition: fof.h:14
uint64_t MinID
Definition: test_mpsort.c:23
int MinIDTask
Definition: fof.h:19
int ThisTask
Definition: test_exchange.c:23
int NTask
Definition: test_exchange.c:23
static uint64_t checksum_minid(struct BaseGroup *data, size_t localsize, MPI_Comm comm)
Definition: test_mpsort.c:239
static void do_long_radix_test(int srcsize)
Definition: test_mpsort.c:253
static void test_basegroup(void **state)
Definition: test_mpsort.c:300
int compar_bg(void *d1, void *d2)
Definition: test_mpsort.c:216
static void radix_int(const void *ptr, void *radix, void *arg)
Definition: test_mpsort.c:27
static void test_mpsort_gather(void **state)
Definition: test_mpsort.c:329
static int64_t checksum(int64_t *data, size_t localsize, MPI_Comm comm)
Definition: test_mpsort.c:32
static void test_mpsort_stagger(void **state)
Definition: test_mpsort.c:320
static void fof_radix_Group_TotalCountTaskDiffMinID(const void *a, void *radix, void *arg)
Definition: test_mpsort.c:208
static void do_mpsort_test(int64_t srcsize, int bits, int staggered, int gather)
Definition: test_mpsort.c:138
int main(void)
Definition: test_mpsort.c:337
static void generate(int64_t *data, size_t localsize, int bits, int seed)
Definition: test_mpsort.c:44
int compar_int(void *d1, void *d2)
Definition: test_mpsort.c:125
static void check_sorted(void *data, int elsize, size_t localsize, int compar(void *d1, void *d2), MPI_Comm comm)
Definition: test_mpsort.c:58
static void test_mpsort_bits(void **state)
Definition: test_mpsort.c:308