Rev 3 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | pj | 1 | /* |
2 | * Copyright (c) 1997-1999 Massachusetts Institute of Technology |
||
3 | * |
||
4 | * This program is free software; you can redistribute it and/or modify |
||
5 | * it under the terms of the GNU General Public License as published by |
||
6 | * the Free Software Foundation; either version 2 of the License, or |
||
7 | * (at your option) any later version. |
||
8 | * |
||
9 | * This program is distributed in the hope that it will be useful, |
||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
12 | * GNU General Public License for more details. |
||
13 | * |
||
14 | * You should have received a copy of the GNU General Public License |
||
15 | * along with this program; if not, write to the Free Software |
||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
17 | * |
||
18 | */ |
||
19 | |||
20 | /* |
||
21 | * test_main.c: driver for test programs (linked with fftw_test.c/rfftw_test.c) |
||
22 | */ |
||
23 | |||
24 | /* $Id: test_mai.c,v 1.1.1.1 2002-03-29 14:12:59 pj Exp $ */ |
||
25 | #include <fftw-int.h> |
||
26 | #include <stdlib.h> |
||
27 | #include <stdio.h> |
||
28 | #include <string.h> |
||
29 | #include <math.h> |
||
30 | #include <time.h> |
||
31 | #include <ctype.h> |
||
32 | |||
33 | #include "test_main.h" |
||
34 | |||
35 | #ifdef HAVE_GETOPT |
||
36 | # ifdef HAVE_GETOPT_H |
||
37 | # include <getopt.h> |
||
38 | # elif defined(HAVE_UNISTD_H) |
||
39 | # include <unistd.h> |
||
40 | # endif |
||
41 | #endif |
||
42 | |||
43 | double mydrand(void) |
||
44 | { |
||
45 | double d = rand(); |
||
46 | return (d / (double) RAND_MAX) - .5; |
||
47 | } |
||
48 | |||
49 | /* return random 0 or non-zero */ |
||
50 | int coinflip(void) |
||
51 | { |
||
52 | return (rand() & 8192); /* higher-order bits are often more random |
||
53 | * |
||
54 | */ |
||
55 | } |
||
56 | |||
57 | /* parse a string of the form N1xN2x... and return a size structure */ |
||
58 | struct size parse_size(char *s) |
||
59 | { |
||
60 | struct size sz; |
||
61 | int n; |
||
62 | |||
63 | sz.rank = 0; |
||
64 | sz.is_nd = 0; |
||
65 | |||
66 | if (*s == 'x' || *s == 'X' || *s == '*') { |
||
67 | ++s; |
||
68 | /* force ND transform of rank 1 */ |
||
69 | sz.is_nd = 1; |
||
70 | } |
||
71 | accept_digit: |
||
72 | n = 0; |
||
73 | |||
74 | CHECK(isdigit(*s), |
||
75 | "invalid digit"); |
||
76 | |||
77 | while (isdigit(*s)) { |
||
78 | n = n * 10 + (*s - '0'); |
||
79 | ++s; |
||
80 | } |
||
81 | |||
82 | sz.narray[sz.rank] = n; |
||
83 | ++sz.rank; |
||
84 | |||
85 | CHECK(sz.rank < MAX_CMDLINE_RANK, |
||
86 | "maximum rank exceeded"); |
||
87 | |||
88 | if (*s == 'x' || *s == 'X' || *s == '*') { |
||
89 | ++s; |
||
90 | goto accept_digit; |
||
91 | } |
||
92 | /* force ND transform if rank > 1 */ |
||
93 | if (sz.rank > 1) |
||
94 | sz.is_nd = 1; |
||
95 | return sz; |
||
96 | } |
||
97 | |||
98 | /******************* |
||
99 | * global variables |
||
100 | *******************/ |
||
101 | int verbose; |
||
102 | int wisdom_flag, measure_flag; |
||
103 | int speed_flag = FFTW_MEASURE; |
||
104 | int chk_mem_leak; |
||
105 | int paranoid; |
||
106 | int howmany_fields = 1; |
||
107 | int max_iterations = 0; /* |
||
108 | * maximum number of iterations to perform |
||
109 | * in "infinite" tests--default (0) means |
||
110 | * no limit |
||
111 | */ |
||
112 | |||
113 | /* When testing MPI stuff, only one process gets to do I/O: */ |
||
114 | int io_okay = 1; |
||
115 | #define my_printf if (io_okay) printf |
||
116 | #define my_fprintf if (io_okay) fprintf |
||
117 | #define my_fflush if (io_okay) fflush |
||
118 | |||
119 | /******************* |
||
120 | * procedures |
||
121 | *******************/ |
||
122 | |||
123 | /* smart time printer */ |
||
124 | char *smart_sprint_time(double x) |
||
125 | { |
||
126 | static char buf[128]; |
||
127 | |||
128 | if (x < 1.0E-6) |
||
129 | sprintf(buf, "%f ns", x * 1.0E9); |
||
130 | else if (x < 1.0E-3) |
||
131 | sprintf(buf, "%f us", x * 1.0E6); |
||
132 | else if (x < 1.0) |
||
133 | sprintf(buf, "%f ms", x * 1.0E3); |
||
134 | else |
||
135 | sprintf(buf, "%f s", x); |
||
136 | |||
137 | return buf; |
||
138 | } |
||
139 | |||
140 | /* greet the user */ |
||
141 | /* jokes stolen from http://whereis.mit.edu/bin/map */ |
||
142 | void please_wait(void) |
||
143 | { |
||
144 | int i; |
||
145 | const char *s[] = |
||
146 | { |
||
147 | "(while a large software vendor in Seattle takes over the world)", |
||
148 | "(and remember, this is faster than Java)", |
||
149 | "(and dream of faster computers)", |
||
150 | "(checking the gravitational constant in your locale)", |
||
151 | "(at least you are not on hold)", |
||
152 | "(while X11 grows by another kilobyte)", |
||
153 | "(while Windows NT reboots)", |
||
154 | "(correcting for the phase of the moon)", |
||
155 | "(your call is important to us)", |
||
156 | "(while the Linux user-base doubles)", |
||
157 | "(while you decide where you want to go tomorrow)", |
||
158 | "(exorcising evil spirits)", |
||
159 | }; |
||
160 | int choices = sizeof(s) / sizeof(*s); |
||
161 | |||
162 | i = rand() % choices; |
||
163 | my_printf("Please wait %s.\n", s[i < 0 ? -i : i]); |
||
164 | } |
||
165 | |||
166 | void please_wait_forever(void) |
||
167 | { |
||
168 | int i; |
||
169 | const char *s[] = |
||
170 | { |
||
171 | "(but it won't crash, either)", |
||
172 | "(at least in theory)", |
||
173 | "(please be patient)", |
||
174 | "(our next release will complete it more quickly)", |
||
175 | #if defined(__WIN32__) || defined(WIN32) || defined(_WINDOWS) |
||
176 | "(by the way, Linux executes infinite loops faster)", |
||
177 | #endif |
||
178 | }; |
||
179 | int choices = sizeof(s) / sizeof(*s); |
||
180 | |||
181 | if (!max_iterations) { |
||
182 | i = rand() % choices; |
||
183 | my_printf("This test does not terminate %s.\n", s[i < 0 ? -i : i]); |
||
184 | } else { |
||
185 | my_printf("This test will run for %d iterations.\n", max_iterations); |
||
186 | please_wait(); |
||
187 | } |
||
188 | } |
||
189 | |||
190 | /************************************************* |
||
191 | * Speed tests |
||
192 | *************************************************/ |
||
193 | |||
194 | double mflops(double t, int N) |
||
195 | { |
||
196 | return (5.0 * N * log((double) N) / (log(2.0) * t * 1.0e6)); |
||
197 | } |
||
198 | |||
199 | void print_dims(struct size sz) |
||
200 | { |
||
201 | int i; |
||
202 | |||
203 | my_printf("%d", sz.narray[0]); |
||
204 | for (i = 1; i < sz.rank; ++i) |
||
205 | my_printf("x%d", sz.narray[i]); |
||
206 | } |
||
207 | |||
208 | void test_speed(int n) |
||
209 | { |
||
210 | int specific; |
||
211 | |||
212 | please_wait(); |
||
213 | |||
214 | if (howmany_fields > 1) |
||
215 | WHEN_VERBOSE(1, my_printf("TIMING MULTIPLE-FIELD FFT: " |
||
216 | "howmany=%d, stride=%d, dist=%d\n\n", |
||
217 | howmany_fields, howmany_fields, 1)); |
||
218 | |||
219 | for (specific = 0; specific <= 1; ++specific) { |
||
220 | WHEN_VERBOSE(1, |
||
221 | my_printf("SPEED TEST: n = %d, FFTW_FORWARD, out of place, %s\n", |
||
222 | n, SPECIFICP(specific))); |
||
223 | test_speed_aux(n, FFTW_FORWARD, 0, specific); |
||
224 | |||
225 | WHEN_VERBOSE(1, |
||
226 | my_printf("SPEED TEST: n = %d, FFTW_FORWARD, in place, %s\n", |
||
227 | n, SPECIFICP(specific))); |
||
228 | test_speed_aux(n, FFTW_FORWARD, FFTW_IN_PLACE, specific); |
||
229 | |||
230 | WHEN_VERBOSE(1, |
||
231 | my_printf("SPEED TEST: n = %d, FFTW_BACKWARD, out of place, %s\n", |
||
232 | n, SPECIFICP(specific))); |
||
233 | test_speed_aux(n, FFTW_BACKWARD, 0, specific); |
||
234 | |||
235 | WHEN_VERBOSE(1, |
||
236 | my_printf("SPEED TEST: n = %d, FFTW_BACKWARD, in place, %s\n", |
||
237 | n, SPECIFICP(specific))); |
||
238 | test_speed_aux(n, FFTW_BACKWARD, FFTW_IN_PLACE, specific); |
||
239 | } |
||
240 | } |
||
241 | |||
242 | void test_speed_nd(struct size sz) |
||
243 | { |
||
244 | int specific; |
||
245 | |||
246 | please_wait(); |
||
247 | |||
248 | if (howmany_fields > 1) |
||
249 | WHEN_VERBOSE(1, my_printf("TIMING MULTIPLE-FIELD FFT: " |
||
250 | "howmany=%d, stride=%d, dist=%d\n\n", |
||
251 | howmany_fields, howmany_fields, 1)); |
||
252 | |||
253 | for (specific = 0; specific <= 1; ++specific) { |
||
254 | my_printf("SPEED TEST: "); |
||
255 | WHEN_VERBOSE(1, print_dims(sz)); |
||
256 | WHEN_VERBOSE(1, my_printf(", FFTW_FORWARD, in place, %s\n", |
||
257 | SPECIFICP(specific))); |
||
258 | test_speed_nd_aux(sz, FFTW_FORWARD, |
||
259 | FFTW_IN_PLACE, specific); |
||
260 | |||
261 | WHEN_VERBOSE(1, my_printf("SPEED TEST: ")); |
||
262 | print_dims(sz); |
||
263 | WHEN_VERBOSE(1, my_printf(", FFTW_BACKWARD, in place, %s\n", |
||
264 | SPECIFICP(specific))); |
||
265 | test_speed_nd_aux(sz, FFTW_BACKWARD, FFTW_IN_PLACE, specific); |
||
266 | } |
||
267 | } |
||
268 | |||
269 | /************************************************* |
||
270 | * correctness tests |
||
271 | *************************************************/ |
||
272 | |||
273 | double compute_error_complex(fftw_complex * A, int astride, |
||
274 | fftw_complex * B, int bstride, int n) |
||
275 | { |
||
276 | /* compute the relative error */ |
||
277 | double error = 0.0; |
||
278 | int i; |
||
279 | |||
280 | for (i = 0; i < n; ++i) { |
||
281 | double a; |
||
282 | double mag; |
||
283 | a = sqrt(SQR(c_re(A[i * astride]) - c_re(B[i * bstride])) + |
||
284 | SQR(c_im(A[i * astride]) - c_im(B[i * bstride]))); |
||
285 | mag = 0.5 * (sqrt(SQR(c_re(A[i * astride])) |
||
286 | + SQR(c_im(A[i * astride]))) + |
||
287 | sqrt(SQR(c_re(B[i * bstride])) |
||
288 | + SQR(c_im(B[i * bstride])))) + TOLERANCE; |
||
289 | |||
290 | a /= mag; |
||
291 | if (a > error) |
||
292 | error = a; |
||
293 | |||
294 | #ifdef HAVE_ISNAN |
||
295 | CHECK(!isnan(a), "NaN in answer"); |
||
296 | #endif |
||
297 | } |
||
298 | return error; |
||
299 | } |
||
300 | |||
301 | /* test forever */ |
||
302 | void test_all(void) |
||
303 | { |
||
304 | int n; |
||
305 | |||
306 | please_wait_forever(); |
||
307 | for (n = 1; !max_iterations || n <= max_iterations; ++n) { |
||
308 | test_correctness(n); |
||
309 | if (!(wisdom_flag & FFTW_USE_WISDOM) && chk_mem_leak) |
||
310 | fftw_check_memory_leaks(); |
||
311 | } |
||
312 | } |
||
313 | |||
314 | #define MAX_FACTOR 13 |
||
315 | |||
316 | int rand_small_factors(int N) |
||
317 | { |
||
318 | int f, n = 1; |
||
319 | |||
320 | f = rand() % MAX_FACTOR + 1; |
||
321 | |||
322 | while (n * f <= N) { |
||
323 | n *= f; |
||
324 | f = rand() % MAX_FACTOR + 1; |
||
325 | } |
||
326 | |||
327 | return n; |
||
328 | } |
||
329 | |||
330 | #define MAX_N 16384 |
||
331 | |||
332 | struct size random_dims(int rank) |
||
333 | { |
||
334 | int maxsize, dim; |
||
335 | double maxsize_d; |
||
336 | struct size sz; |
||
337 | |||
338 | /* workaround to weird gcc warning */ |
||
339 | maxsize_d = pow((double) (rank == 1 ? MAX_N / 4 : MAX_N), |
||
340 | 1.0 / (double) rank); |
||
341 | maxsize = (int) maxsize_d; |
||
342 | |||
343 | if (maxsize < 1) |
||
344 | maxsize = 1; |
||
345 | |||
346 | sz.rank = rank; |
||
347 | for (dim = 0; dim < rank; ++dim) |
||
348 | sz.narray[dim] = rand_small_factors(maxsize); |
||
349 | |||
350 | return sz; |
||
351 | } |
||
352 | |||
353 | void test_random(void) |
||
354 | { |
||
355 | static int counter = 0; |
||
356 | struct size sz; |
||
357 | |||
358 | if ((++counter) % 16 == 0) { |
||
359 | sz.rank = 1; |
||
360 | sz.narray[0] = rand() % (MAX_N / 16) + 1; |
||
361 | } else { |
||
362 | sz = random_dims(1); |
||
363 | } |
||
364 | |||
365 | test_correctness(sz.narray[0]); |
||
366 | } |
||
367 | |||
368 | /************************************************* |
||
369 | * multi-dimensional correctness tests |
||
370 | *************************************************/ |
||
371 | |||
372 | void testnd_correctness_both(struct size sz, |
||
373 | int alt_api, int specific, int force_buf) |
||
374 | { |
||
375 | WHEN_VERBOSE(1, |
||
376 | my_printf("Testing nd correctness for size = "); |
||
377 | print_dims(sz); |
||
378 | my_printf("..."); |
||
379 | my_fflush(stdout)); |
||
380 | |||
381 | if (alt_api) |
||
382 | WHEN_VERBOSE(1, my_printf("alt. api...")); |
||
383 | if (specific) |
||
384 | WHEN_VERBOSE(1, my_printf("specific...")); |
||
385 | if (force_buf) |
||
386 | WHEN_VERBOSE(1, my_printf("force buf...")); |
||
387 | |||
388 | testnd_correctness(sz, FFTW_FORWARD, alt_api, specific, force_buf); |
||
389 | testnd_correctness(sz, FFTW_BACKWARD, alt_api, specific, force_buf); |
||
390 | |||
391 | WHEN_VERBOSE(1, my_printf("OK\n")); |
||
392 | } |
||
393 | |||
394 | void testnd_correctness_aux(struct size sz) |
||
395 | { |
||
396 | int alt_api, specific, force_buf; |
||
397 | |||
398 | for (alt_api = 0; alt_api <= 1; ++alt_api) |
||
399 | for (specific = 0; specific <= 1; ++specific) |
||
400 | for (force_buf = 0; force_buf <= 1; ++force_buf) |
||
401 | testnd_correctness_both(sz, alt_api, specific, |
||
402 | force_buf); |
||
403 | } |
||
404 | |||
405 | void testnd_correctness_square(int rank, int size) |
||
406 | { |
||
407 | struct size sz; |
||
408 | int alt_api, specific, force_buf; |
||
409 | int i; |
||
410 | |||
411 | sz.rank = rank; |
||
412 | for (i = 0; i < rank; ++i) |
||
413 | sz.narray[i] = size; |
||
414 | |||
415 | for (alt_api = 0; alt_api <= 1; ++alt_api) |
||
416 | for (specific = 0; specific <= 1; ++specific) |
||
417 | for (force_buf = 0; force_buf <= 1; ++force_buf) |
||
418 | testnd_correctness_both(sz, alt_api, |
||
419 | specific, force_buf); |
||
420 | |||
421 | } |
||
422 | |||
423 | void testnd_random(int rank) |
||
424 | { |
||
425 | struct size sz; |
||
426 | |||
427 | sz = random_dims(rank); |
||
428 | testnd_correctness_both(sz, coinflip(), coinflip(), coinflip()); |
||
429 | } |
||
430 | |||
431 | /* loop forever */ |
||
432 | void test_all_random(int rank) |
||
433 | { |
||
434 | int counter; |
||
435 | please_wait_forever(); |
||
436 | |||
437 | for (counter = 0; !max_iterations || counter < max_iterations; ++counter) { |
||
438 | if (rank > 0) |
||
439 | testnd_random(rank); |
||
440 | else if ((counter) % 2 == 0) |
||
441 | test_random(); |
||
442 | else |
||
443 | testnd_random(rand() % MAX_RANK + 1); |
||
444 | } |
||
445 | |||
446 | } |
||
447 | |||
448 | int pow2sqrt(int n) |
||
449 | /* return greatest power of two <= sqrt(n) */ |
||
450 | { |
||
451 | int s = 1; |
||
452 | |||
453 | while (s * s * 4 <= n) |
||
454 | s *= 2; |
||
455 | return s; |
||
456 | } |
||
457 | |||
458 | /* test forever */ |
||
459 | void testnd_all(int rank) |
||
460 | { |
||
461 | int n; |
||
462 | |||
463 | please_wait_forever(); |
||
464 | |||
465 | for (n = 1; !max_iterations || n <= max_iterations; ++n) |
||
466 | testnd_correctness_square(rank, n); |
||
467 | } |
||
468 | |||
469 | fftw_direction random_dir(void) |
||
470 | { |
||
471 | if (coinflip()) |
||
472 | return FFTW_FORWARD; |
||
473 | else |
||
474 | return FFTW_BACKWARD; |
||
475 | } |
||
476 | |||
477 | /************************************************* |
||
478 | * timer tests |
||
479 | *************************************************/ |
||
480 | |||
481 | static int hack_sum_i; |
||
482 | |||
483 | void negative_time(void) |
||
484 | { |
||
485 | my_fprintf(stderr, |
||
486 | "* PROBLEM: I measured a negative time interval.\n" |
||
487 | "* Please make sure you defined the timer correctly\n" |
||
488 | "* or contact fftw@theory.lcs.mit.edu for help.\n"); |
||
489 | } |
||
490 | |||
491 | /* |
||
492 | * paranoid test to see if time is monotonic. If not, you are |
||
493 | * really in trouble |
||
494 | */ |
||
495 | void test_timer_paranoid(void) |
||
496 | { |
||
497 | fftw_time start_t, end_t; |
||
498 | double sec; |
||
499 | int i; |
||
500 | |||
501 | start_t = fftw_get_time(); |
||
502 | |||
503 | /* waste some time */ |
||
504 | for (i = 0; i < 10000; ++i) |
||
505 | hack_sum_i = i; |
||
506 | |||
507 | end_t = fftw_get_time(); |
||
508 | sec = fftw_time_to_sec(fftw_time_diff(end_t, start_t)); |
||
509 | if (sec < 0.0) |
||
510 | negative_time(); |
||
511 | } |
||
512 | |||
513 | /* compute useful numbers */ |
||
514 | static int fib(int n) |
||
515 | { |
||
516 | if (n < 2) |
||
517 | return n; |
||
518 | else { |
||
519 | int x, y; |
||
520 | x = fib(n - 1); |
||
521 | y = fib(n - 2); |
||
522 | return x + y; |
||
523 | } |
||
524 | } |
||
525 | |||
526 | static int hack_fib; |
||
527 | |||
528 | void test_timer(void) |
||
529 | { |
||
530 | double times[32], acc, min_time = 10000.00; |
||
531 | unsigned long iters, iter; |
||
532 | fftw_time begin, end, start; |
||
533 | double t, tmax, tmin; |
||
534 | int last = 0, i, repeat; |
||
535 | |||
536 | please_wait(); |
||
537 | test_timer_paranoid(); |
||
538 | |||
539 | start = fftw_get_time(); |
||
540 | |||
541 | for (i = 0; i < 32; i++) { |
||
542 | iters = 1 << i; |
||
543 | tmin = 1.0E10; |
||
544 | tmax = -1.0E10; |
||
545 | |||
546 | for (repeat = 0; repeat < FFTW_TIME_REPEAT; ++repeat) { |
||
547 | begin = fftw_get_time(); |
||
548 | for (iter = 0; iter < iters; ++iter) { |
||
549 | hack_fib = fib(10); |
||
550 | } |
||
551 | end = fftw_get_time(); |
||
552 | |||
553 | t = fftw_time_to_sec(fftw_time_diff(end, begin)); |
||
554 | if (t < tmin) |
||
555 | tmin = t; |
||
556 | if (t > tmax) |
||
557 | tmax = t; |
||
558 | |||
559 | /* do not run for too long */ |
||
560 | t = fftw_time_to_sec(fftw_time_diff(end, start)); |
||
561 | if (t > FFTW_TIME_LIMIT) |
||
562 | break; |
||
563 | } |
||
564 | |||
565 | if (tmin < 0.0) |
||
566 | negative_time(); |
||
567 | |||
568 | times[i] = tmin; |
||
569 | |||
570 | WHEN_VERBOSE(2, |
||
571 | my_printf("Number of iterations = 2^%d = %lu, time = %g, " |
||
572 | "time/iter = %g\n", |
||
573 | i, iters, times[i], |
||
574 | times[i] / iters)); |
||
575 | WHEN_VERBOSE(2, |
||
576 | my_printf(" (out of %d tries, tmin = %g, tmax = %g)\n", |
||
577 | FFTW_TIME_REPEAT, tmin, tmax)); |
||
578 | |||
579 | last = i; |
||
580 | if (times[i] > 10.0) |
||
581 | break; |
||
582 | } |
||
583 | |||
584 | /* |
||
585 | * at this point, `last' is the last valid element in the |
||
586 | * `times' array. |
||
587 | */ |
||
588 | |||
589 | for (i = 0; i <= last; ++i) |
||
590 | if (times[i] > 0.0 && times[i] < min_time) |
||
591 | min_time = times[i]; |
||
592 | |||
593 | WHEN_VERBOSE(1, my_printf("\nMinimum resolvable time interval = %g seconds.\n\n", |
||
594 | min_time)); |
||
595 | |||
596 | for (acc = 0.1; acc > 0.0005; acc *= 0.1) { |
||
597 | double t_final; |
||
598 | t_final = times[last] / (1 << last); |
||
599 | |||
600 | for (i = last; i >= 0; --i) { |
||
601 | double t_cur, error; |
||
602 | iters = 1 << i; |
||
603 | t_cur = times[i] / iters; |
||
604 | error = (t_cur - t_final) / t_final; |
||
605 | if (error < 0.0) |
||
606 | error = -error; |
||
607 | if (error > acc) |
||
608 | break; |
||
609 | } |
||
610 | |||
611 | ++i; |
||
612 | |||
613 | WHEN_VERBOSE(1, |
||
614 | my_printf("Minimum time for %g%% consistency = %g seconds.\n", |
||
615 | acc * 100.0, times[i])); |
||
616 | } |
||
617 | WHEN_VERBOSE(1, |
||
618 | my_printf("\nMinimum time used in FFTW timing (FFTW_TIME_MIN)" |
||
619 | " = %g seconds.\n", FFTW_TIME_MIN)); |
||
620 | } |
||
621 | |||
622 | /************************************************* |
||
623 | * help |
||
624 | *************************************************/ |
||
625 | |||
626 | #ifdef HAVE_GETOPT_LONG |
||
627 | # define WHEN_LONG_OPTIONS(x) x |
||
628 | #else |
||
629 | # define WHEN_LONG_OPTIONS(x) "" |
||
630 | #endif |
||
631 | |||
632 | static void usage(int exit_when_done) |
||
633 | { |
||
634 | my_printf("Usage: %s_test [options]\n", fftw_prefix); |
||
635 | my_printf(WHEN_LONG_OPTIONS(" --speed=<n>\n") |
||
636 | " -s <n> : test speed for size n\n"); |
||
637 | my_printf(WHEN_LONG_OPTIONS("\n --correctness=<n>\n") |
||
638 | " -c <n> : test correctness for size n\n"); |
||
639 | my_printf(WHEN_LONG_OPTIONS("\n --random=<rank>>\n") |
||
640 | " -r <rank> : test correctness for random sizes " |
||
641 | "(does not terminate)\n"); |
||
642 | my_printf(WHEN_LONG_OPTIONS("\n --all=<rank>\n") |
||
643 | " -a <rank> : test correctness for all sizes " |
||
644 | "(does not terminate)\n"); |
||
645 | my_printf(WHEN_LONG_OPTIONS("\n --fields=<n>\n") |
||
646 | " -f <n> : n fields ('howmany' param) in speed tests\n"); |
||
647 | my_printf(WHEN_LONG_OPTIONS("\n --planner=<rank>\n") |
||
648 | " -p <rank> : test planner\n"); |
||
649 | my_printf(WHEN_LONG_OPTIONS("\n --measure\n") |
||
650 | " -m : use FFTW_MEASURE in correctness tests\n"); |
||
651 | my_printf(WHEN_LONG_OPTIONS("\n --estimate\n") |
||
652 | " -e : use FFTW_ESTIMATE in speed tests\n"); |
||
653 | my_printf(WHEN_LONG_OPTIONS("\n --wisdom=<file>\n") |
||
654 | " -w <file> : use wisdom & read/write it from/to file\n"); |
||
655 | my_printf(WHEN_LONG_OPTIONS("\n --timer\n") |
||
656 | " -t : test timer resolution\n"); |
||
657 | my_printf(WHEN_LONG_OPTIONS("\n --x-repeat=<n>\n") |
||
658 | " -x <n> : run non-terminating tests (-r, -a) only n times\n"); |
||
659 | my_printf(WHEN_LONG_OPTIONS("\n --paranoid\n") |
||
660 | " -P : enable paranoid tests\n"); |
||
661 | my_printf(WHEN_LONG_OPTIONS("\n --verbose\n") |
||
662 | " -v : verbose output for subsequent options\n"); |
||
663 | my_printf(WHEN_LONG_OPTIONS("\n --version\n") |
||
664 | " -V : print FFTW version information\n"); |
||
665 | my_printf(WHEN_LONG_OPTIONS("\n --help\n") |
||
666 | " -h : this help\n"); |
||
667 | #ifndef HAVE_GETOPT |
||
668 | my_printf("(When run with no arguments, an interactive mode is used.)\n"); |
||
669 | #endif |
||
670 | if (exit_when_done) |
||
671 | exit(EXIT_FAILURE); |
||
672 | } |
||
673 | |||
674 | char wfname[128]; |
||
675 | |||
676 | void handle_option(char opt, char *optarg) |
||
677 | { |
||
678 | FILE *wf; |
||
679 | struct size sz; |
||
680 | int rank, n; |
||
681 | |||
682 | switch (opt) { |
||
683 | case 's': |
||
684 | sz = parse_size(optarg); |
||
685 | if (!sz.is_nd) |
||
686 | test_speed(sz.narray[0]); |
||
687 | else |
||
688 | test_speed_nd(sz); |
||
689 | break; |
||
690 | |||
691 | case 'c': |
||
692 | sz = parse_size(optarg); |
||
693 | if (!sz.is_nd) |
||
694 | test_correctness(sz.narray[0]); |
||
695 | else |
||
696 | testnd_correctness_aux(sz); |
||
697 | break; |
||
698 | |||
699 | case 'p': |
||
700 | rank = atoi(optarg); |
||
701 | test_planner(rank); |
||
702 | break; |
||
703 | |||
704 | case 'P': |
||
705 | paranoid = 1; |
||
706 | enter_paranoid_mode(); |
||
707 | break; |
||
708 | |||
709 | case 'r': |
||
710 | rank = atoi(optarg); |
||
711 | test_all_random(rank); |
||
712 | break; |
||
713 | |||
714 | case 'a': |
||
715 | rank = atoi(optarg); |
||
716 | if (rank == 0) |
||
717 | test_all(); |
||
718 | else |
||
719 | testnd_all(rank); |
||
720 | break; |
||
721 | |||
722 | case 't': |
||
723 | test_timer(); |
||
724 | break; |
||
725 | |||
726 | case 'f': |
||
727 | n = atoi(optarg); |
||
728 | CHECK(n > 0, "-f requires a positive integer argument"); |
||
729 | howmany_fields = n; |
||
730 | break; |
||
731 | |||
732 | case 'm': |
||
733 | measure_flag = FFTW_MEASURE; |
||
734 | break; |
||
735 | |||
736 | case 'e': |
||
737 | speed_flag = FFTW_ESTIMATE; |
||
738 | break; |
||
739 | |||
740 | case 'w': |
||
741 | wisdom_flag = FFTW_USE_WISDOM; |
||
742 | strcpy(wfname, optarg); |
||
743 | wf = fopen(wfname, "r"); |
||
744 | if (wf == 0) { |
||
745 | my_printf("Couldn't open wisdom file \"%s\".\n", wfname); |
||
746 | my_printf("This file will be created upon completion.\n"); |
||
747 | } else { |
||
748 | CHECK(FFTW_SUCCESS == fftw_import_wisdom_from_file(wf), |
||
749 | "invalid wisdom file format"); |
||
750 | fclose(wf); |
||
751 | } |
||
752 | break; |
||
753 | |||
754 | case 'v': |
||
755 | verbose++; |
||
756 | break; |
||
757 | |||
758 | case 'V': |
||
759 | my_printf("%s\n", fftw_version); |
||
760 | my_printf("%s test program, compiled in %s precision.\n", |
||
761 | fftw_prefix, |
||
762 | sizeof(fftw_real) == sizeof(double) ? "double" |
||
763 | : (sizeof(fftw_real) == sizeof(float) ? "single" |
||
764 | : "unknown")); |
||
765 | my_printf( |
||
766 | "\nCopyright (C) Massachusetts Institute of Technology.\n" |
||
767 | "FFTW comes with ABSOLUTELY NO WARRANTY. This is free software, and\n" |
||
768 | "you are welcome to redistribute it under the terms of the GNU\n" |
||
769 | "General Public License. For more information, see the file COPYING or\n" |
||
770 | "the GNU web site at http://www.gnu.org.\n" |
||
771 | "\nFor more information regarding FFTW, or to download the latest version,\n" |
||
772 | "see the FFTW home page at http://theory.lcs.mit.edu/~fftw.\n"); |
||
773 | |||
774 | break; |
||
775 | |||
776 | case 'x': |
||
777 | n = atoi(optarg); |
||
778 | CHECK(n > 0, "-x requires a positive integer argument"); |
||
779 | max_iterations = n; |
||
780 | break; |
||
781 | |||
782 | case 'h': |
||
783 | usage(FALSE); |
||
784 | break; |
||
785 | |||
786 | default: |
||
787 | usage(TRUE); |
||
788 | } |
||
789 | |||
790 | /* every test must free all the used FFTW memory */ |
||
791 | if (!(wisdom_flag & FFTW_USE_WISDOM) && chk_mem_leak) |
||
792 | fftw_check_memory_leaks(); |
||
793 | } |
||
794 | |||
795 | |||
796 | |||
797 | short askuser(const char *s) |
||
798 | { |
||
799 | char line[200] = "", c; |
||
800 | int i, count = 0; |
||
801 | |||
802 | do { |
||
803 | if (count++ > 0) |
||
804 | my_printf("Invalid response. Please enter \"y\" or \"n\".\n"); |
||
805 | my_printf("%s (y/n) ", s); |
||
806 | /* skip blank lines */ |
||
807 | while (line[0] == 0 || line[0] == '\n') |
||
808 | fgets(line, 200, stdin); |
||
809 | for (i = 0; line[i] && (line[i] == ' ' || line[i] == '\t'); ++i); |
||
810 | c = line[i]; |
||
811 | } while (c != 'n' && c != 'N' && c != 'y' && c != 'Y'); |
||
812 | |||
813 | return (c == 'y' || c == 'Y'); |
||
814 | } |
||
815 | |||
816 | /* Standard function to get the next command-line argument for the program. |
||
817 | Returns the option character (or -1 if there are no more options), |
||
818 | and the option argument (if any) in argval, which is an array of length |
||
819 | at least argval_maxlen. |
||
820 | |||
821 | The test programs need to implement a function get_option with the |
||
822 | same arguments as this one, which will typically just call |
||
823 | default_get_option. |
||
824 | |||
825 | The reason we need to put this in a separate function is that the MPI |
||
826 | test programs can't rely on all of the processes having direct access |
||
827 | to the program arguments--they need to pass them as explicit messages |
||
828 | from the master process. Sigh. */ |
||
829 | int default_get_option(int argc, char **argv, char *argval, int argval_maxlen) |
||
830 | { |
||
831 | int c = -1; |
||
832 | |||
833 | if (argc <= 1) |
||
834 | usage(TRUE); |
||
835 | |||
836 | #ifdef HAVE_GETOPT |
||
837 | { |
||
838 | const char short_options[] = "s:c:w:f:p:Pa:r:tvVmehx:"; |
||
839 | extern char *optarg; |
||
840 | extern int optind; |
||
841 | |||
842 | # if defined(HAVE_GETOPT_LONG) && defined(HAVE_GETOPT_H) |
||
843 | { |
||
844 | int option_index; |
||
845 | const struct option long_options[] = { |
||
846 | {"speed", 1, 0, 's'}, |
||
847 | {"correctness", 1, 0, 'c'}, |
||
848 | {"wisdom", 1, 0, 'w'}, |
||
849 | {"fields", 1, 0, 'f'}, |
||
850 | {"planner", 1, 0, 'p'}, |
||
851 | {"paranoid", 0, 0, 'P'}, |
||
852 | {"all", 1, 0, 'a'}, |
||
853 | {"random", 1, 0, 'r'}, |
||
854 | {"timer", 0, 0, 't'}, |
||
855 | {"verbose", 0, 0, 'v'}, |
||
856 | {"version", 0, 0, 'V'}, |
||
857 | {"measure", 0, 0, 'm'}, |
||
858 | {"estimate", 0, 0, 'e'}, |
||
859 | {"help", 0, 0, 'h'}, |
||
860 | {"x-repeat", 1, 0, 'x'}, |
||
861 | {0, 0, 0, 0} |
||
862 | }; |
||
863 | |||
864 | c = getopt_long(argc, argv, short_options, long_options, |
||
865 | &option_index); |
||
866 | } |
||
867 | # else /* not HAVE_GETOPT_LONG */ |
||
868 | c = getopt(argc, argv, short_options); |
||
869 | # endif /* not HAVE_GETOPT_LONG */ |
||
870 | |||
871 | if (c == -1 && argc != optind) |
||
872 | usage(TRUE); /* there were invalid args; print usage info */ |
||
873 | |||
874 | if (optarg) { |
||
875 | strncpy(argval, optarg, argval_maxlen - 1); |
||
876 | argval[argval_maxlen - 1] = 0; |
||
877 | } |
||
878 | else |
||
879 | argval[0] = 0; |
||
880 | } |
||
881 | #endif /* HAVE_GETOPT */ |
||
882 | |||
883 | return c; |
||
884 | } |
||
885 | |||
886 | int main(int argc, char *argv[]) |
||
887 | { |
||
888 | verbose = 1; |
||
889 | wisdom_flag = 0; |
||
890 | measure_flag = FFTW_ESTIMATE; |
||
891 | chk_mem_leak = 1; |
||
892 | paranoid = 0; |
||
893 | |||
894 | #ifdef DETERMINISTIC |
||
895 | srand(1123); |
||
896 | #else |
||
897 | srand((unsigned int) time(NULL)); |
||
898 | #endif |
||
899 | |||
900 | test_init(&argc, argv); |
||
901 | |||
902 | /* |
||
903 | * To parse the command line, we use getopt, but this does not seem |
||
904 | * to be in the ANSI standard (it is only available on UNIX, |
||
905 | * apparently). |
||
906 | */ |
||
907 | #ifndef HAVE_GETOPT |
||
908 | if (argc > 1) |
||
909 | my_printf("Sorry, command-line arguments are not available on\n" |
||
910 | "this system. Run fftw_test with no arguments to\n" |
||
911 | "use it in interactive mode.\n"); |
||
912 | |||
913 | if (argc <= 1) { |
||
914 | int n = 0; |
||
915 | char s[128] = ""; |
||
916 | |||
917 | usage(FALSE); |
||
918 | |||
919 | my_printf("\n"); |
||
920 | |||
921 | if (askuser("Perform random correctness tests (non-terminating)?")) |
||
922 | handle_option('r', "0"); |
||
923 | |||
924 | if (askuser("Verbose output?")) |
||
925 | handle_option('v', ""); |
||
926 | if (askuser("Paranoid test?")) |
||
927 | handle_option('P', ""); |
||
928 | |||
929 | if (askuser("Use/test wisdom?")) { |
||
930 | my_printf(" Enter wisdom file name to use: "); |
||
931 | fgets(s, 128, stdin); |
||
932 | handle_option('w', s); |
||
933 | } |
||
934 | if (askuser("Test correctness?")) { |
||
935 | if (askuser(" -- for all sizes?")) |
||
936 | handle_option('a', ""); |
||
937 | else { |
||
938 | my_printf(" Enter n: "); |
||
939 | fgets(s, 128, stdin); |
||
940 | handle_option('c', s); |
||
941 | } |
||
942 | } |
||
943 | if (askuser("Test speed?")) { |
||
944 | my_printf(" Enter n: "); |
||
945 | fgets(s, 128, stdin); |
||
946 | handle_option('s', s); |
||
947 | } |
||
948 | if (askuser("Test planner?")) |
||
949 | handle_option('p', ""); |
||
950 | if (askuser("Test timer?")) |
||
951 | handle_option('t', ""); |
||
952 | } |
||
953 | #else /* |
||
954 | * read command-line args using getopt |
||
955 | * facility |
||
956 | */ |
||
957 | { |
||
958 | char option_arg[128]; |
||
959 | int c; |
||
960 | |||
961 | while ((c = get_option(argc, argv, option_arg, 128)) != -1) |
||
962 | handle_option(c, option_arg); |
||
963 | } |
||
964 | #endif |
||
965 | |||
966 | if (wisdom_flag & FFTW_USE_WISDOM) { |
||
967 | char *ws; |
||
968 | FILE *wf; |
||
969 | |||
970 | ws = fftw_export_wisdom_to_string(); |
||
971 | CHECK(ws != 0, "error exporting wisdom to string"); |
||
972 | my_printf("\nAccumulated wisdom:\n %s\n", ws); |
||
973 | fftw_forget_wisdom(); |
||
974 | CHECK(FFTW_SUCCESS == fftw_import_wisdom_from_string(ws), |
||
975 | "unexpected error reading in wisdom from string"); |
||
976 | fftw_free(ws); |
||
977 | |||
978 | if (io_okay) { |
||
979 | wf = fopen(wfname, "w"); |
||
980 | CHECK(wf != 0, "error creating wisdom file"); |
||
981 | fftw_export_wisdom_to_file(wf); |
||
982 | fclose(wf); |
||
983 | } |
||
984 | } |
||
985 | /* make sure to dispose of wisdom before checking for memory leaks */ |
||
986 | fftw_forget_wisdom(); |
||
987 | |||
988 | fftw_check_memory_leaks(); |
||
989 | if (io_okay) |
||
990 | fftw_print_max_memory_usage(); |
||
991 | |||
992 | test_finish(); |
||
993 | |||
994 | return EXIT_SUCCESS; |
||
995 | } |