BRL-CAD
bu_parallel.c
Go to the documentation of this file.
1 /* B U _ S E M A P H O R E . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2013-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file bu_semaphore.c
21  *
22  * Tests libbu parallel processing.
23  *
24  */
25 
26 #include "common.h"
27 
28 #include "bu.h"
29 
30 #include <string.h>
31 
32 
33 struct parallel_data {
34  size_t iterations;
35  void (*call)(int, void*);
36 };
37 
38 /* intentionally not in struct so we can test the data arg,
39  * intentionally per-cpu so we can avoid calling
40  * bu_semaphore_acquire().
41  */
42 size_t counter[MAX_PSW] = {0};
43 
44 
45 static void
46 callback(int cpu, void *d)
47 {
48  size_t i;
49  struct parallel_data *data = (struct parallel_data *)d;
50  size_t iterations = 0;
51 
52  /* bu_log("I'm child %d (id=%d)\n", cpu, bu_parallel_id()); */
53 
54  if (data)
55  iterations = data->iterations;
56 
57  for (i=0; i < iterations; i++) {
58  counter[cpu] += 1;
59  }
60 
61  return;
62 }
63 
64 
65 static void
66 recursive_callback(int UNUSED(cpu), void *d)
67 {
68  struct parallel_data *parent = (struct parallel_data *)d;
69  struct parallel_data data;
70  data.iterations = parent->iterations;
71  data.call = NULL;
72 
73  /* bu_log("I'm parent %d (id=%d)\n", cpu, bu_parallel_id()); */
74 
75  bu_parallel(parent->call, 0, &data);
76 }
77 
78 
79 static size_t
80 tally(size_t ncpu)
81 {
82  size_t total = 0;
83  size_t i;
84 
85  for (i = 0; i < ncpu; i++) {
86  total += counter[i];
87  }
88 
89  return total;
90 }
91 
92 
93 int
94 main(int argc, char *argv[])
95 {
96  const char * const USAGE = "Usage: %s [-P ncpu]\n";
97 
98  int c;
99  size_t ncpu = bu_avail_cpus();
100  unsigned long ncpu_opt;
101  struct parallel_data data;
102 
103  while ((c = bu_getopt(argc, argv, "P:!:")) != -1) {
104  switch (c) {
105  case 'P':
106  ncpu_opt = (size_t)strtoul(bu_optarg, NULL, 0);
107  if (ncpu_opt > 0 && ncpu_opt < MAX_PSW)
108  ncpu = ncpu_opt;
109  break;
110  case '!':
111  sscanf(bu_optarg, "%x", (unsigned int *)&bu_debug);
112  break;
113  default:
114  bu_exit(1, USAGE, argv[0]);
115  }
116  }
117 
118  /* test calling without a hook function */
119  bu_parallel(NULL, ncpu, NULL);
120  if (tally(ncpu) != 0) {
121  bu_log("bu_parallel zero callback [FAIL]\n");
122  return 1;
123  }
124  bu_log("bu_parallel zero callback [PASS]\n");
125 
126  /* test calling a simple hook function */
127  memset(counter, 0, sizeof(counter));
128  bu_parallel(callback, ncpu, NULL);
129  if (tally(ncpu) != 0) {
130  bu_log("bu_parallel simple callback [FAIL]\n");
131  return 1;
132  }
133  bu_log("bu_parallel simple callback [PASS]\n");
134 
135  /* test calling a simple hook function with data */
136  memset(counter, 0, sizeof(counter));
137  data.iterations = 0;
138  bu_parallel(callback, ncpu, &data);
139  if (tally(ncpu) != 0) {
140  bu_log("bu_parallel simple callback with data, no iterations [FAIL]\n");
141  return 1;
142  }
143  bu_log("bu_parallel simple callback with data, no iterations [PASS]\n");
144 
145  /* test calling a simple hook function with data, minimal potential for collisions */
146  memset(counter, 0, sizeof(counter));
147  data.iterations = 10;
148  bu_parallel(callback, ncpu, &data);
149  if (tally(MAX_PSW) != ncpu*data.iterations) {
150  bu_log("bu_parallel simple callback with data, few iterations [FAIL] (got %zd, expected %zd)\n", tally(ncpu), ncpu*data.iterations);
151  return 1;
152  }
153  bu_log("bu_parallel simple callback with data, few iterations [PASS]\n");
154 
155  /* test calling a simple hook function again with data, but lots of collision potential */
156  memset(counter, 0, sizeof(counter));
157  data.iterations = 1000000;
158  bu_parallel(callback, ncpu, &data);
159  if (tally(MAX_PSW) != ncpu*data.iterations) {
160  bu_log("bu_parallel simple callback with data, many iterations [FAIL] (got %zd, expected %zd)\n", tally(ncpu), ncpu*data.iterations);
161  return 1;
162  }
163  bu_log("bu_parallel simple callback with data, many iterations [PASS]\n");
164 
165  /* test calling a simple hook function with data, zero cpus, potential for collisions */
166  memset(counter, 0, sizeof(counter));
167  data.iterations = 1000000;
168  bu_parallel(callback, 0, &data);
169  if (tally(MAX_PSW) != bu_avail_cpus()*data.iterations) {
170  bu_log("bu_parallel simple callback with data, many iterations [FAIL] (got %zd, expected %zd)\n", tally(MAX_PSW), bu_avail_cpus()*data.iterations);
171  return 1;
172  }
173  bu_log("bu_parallel simple callback with data, many iterations [PASS]\n");
174 
175  /* test calling a recursive hook function without data */
176  memset(counter, 0, sizeof(counter));
177  data.iterations = 10;
178  data.call = &callback;
179  bu_parallel(recursive_callback, ncpu, &data);
180  if (tally(MAX_PSW) != ncpu*ncpu*data.iterations) {
181  bu_log("bu_parallel recursive callback, few iterations [FAIL] (got %zd, expected %zd)\n", tally(MAX_PSW), ncpu*ncpu*data.iterations);
182  return 1;
183  }
184  bu_log("bu_parallel recursive callback, few iterations [PASS]\n");
185 
186  /* test calling a recursive hook function without data, more collision potential */
187  memset(counter, 0, sizeof(counter));
188  data.iterations = 1000000;
189  data.call = &callback;
190  bu_parallel(recursive_callback, ncpu, &data);
191  if (tally(MAX_PSW) != ncpu*ncpu*data.iterations) {
192  bu_log("bu_parallel recursive callback, many iterations [FAIL] (got %zd, expected %zd)\n", tally(MAX_PSW), ncpu*ncpu*data.iterations);
193  return 1;
194  }
195  bu_log("bu_parallel recursive callback, many iterations [PASS]\n");
196 
197  return 0;
198 }
199 
200 
201 /*
202  * Local Variables:
203  * mode: C
204  * tab-width: 8
205  * indent-tabs-mode: t
206  * c-file-style: "stroustrup"
207  * End:
208  * ex: shiftwidth=4 tabstop=8
209  */
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
char * bu_optarg
Definition: globals.c:91
Header file for the BRL-CAD common definitions.
int bu_getopt(int nargc, char *const nargv[], const char *ostr)
Definition: getopt.c:43
COMPLEX data[64]
Definition: fftest.c:34
void * memset(void *s, int c, size_t n)
void bu_exit(int status, const char *fmt,...) _BU_ATTR_NORETURN _BU_ATTR_PRINTF23
Definition: bomb.c:195
size_t iterations
Definition: bu_parallel.c:34
#define UNUSED(parameter)
Definition: common.h:239
size_t bu_avail_cpus(void)
Definition: parallel.c:193
#define MAX_PSW
Definition: parallel.h:48
void bu_parallel(void(*func)(int func_ncpu, void *func_data), int ncpu, void *data)
int main(int argc, char *argv[])
Definition: bu_parallel.c:94
int bu_debug
Definition: globals.c:87
#define USAGE
Definition: bntester.c:38
void(* call)(int, void *)
Definition: bu_parallel.c:35