1 /*
2  * Copyright (c) 2013-2021, The OSKAR Developers.
3  * See the LICENSE file at the top-level directory of this distribution.
4  */
5 
6 #include "settings/oskar_option_parser.h"
7 #include "math/oskar_cmath.h"
8 #include "math/oskar_dftw.h"
9 #include "utility/oskar_get_error_string.h"
10 #include "utility/oskar_timer.h"
11 #include "utility/oskar_device.h"
12 #include "oskar_version.h"
13 
14 #include <cstdlib>
15 #include <cstdio>
16 
17 enum OpType { C2C, M2M, UNDEF };
18 
19 int benchmark(int num_elements, int num_directions, int num_element_types,
20         OpType op_type, int loc, int precision, bool evaluate_2d, int niter,
21         double& time_taken);
22 
23 
main(int argc,char ** argv)24 int main(int argc, char** argv)
25 {
26     oskar::OptionParser opt("oskar_array_pattern_benchmark", OSKAR_VERSION_STR);
27     opt.add_required("No. array elements");
28     opt.add_required("No. directions");
29     opt.add_flag("-sp", "Use single precision (default: double precision)");
30     opt.add_flag("-g", "Run on the GPU");
31     opt.add_flag("-c", "Run on the CPU");
32     opt.add_flag("-cl", "Run using OpenCL");
33     opt.add_flag("-c2c", "Beam pattern using complex inputs (complex to complex DFT)");
34     opt.add_flag("-m2m", "Beam pattern using complex, polarised inputs (complex matrix to matrix DFT)");
35     opt.add_flag("-2d", "Use a 2-dimensional phase term (default: 3D)");
36     opt.add_flag("-n", "Number of iterations", 1, "1");
37     opt.add_flag("-e", "Number of element types", 1, "1");
38     opt.add_flag("-v", "Display verbose output.");
39 
40     if (!opt.check_options(argc, argv)) return EXIT_FAILURE;
41 
42     int num_elements = atoi(opt.get_arg(0));
43     int num_directions = atoi(opt.get_arg(1));
44     OpType op_type = UNDEF;
45     int op_type_count = 0;
46     if (opt.is_set("-c2c"))
47     {
48         op_type = C2C;
49         op_type_count++;
50     }
51     if (opt.is_set("-m2m"))
52     {
53         op_type = M2M;
54         op_type_count++;
55     }
56 
57     int location = -1;
58     if (opt.is_set("-g")) location = OSKAR_GPU;
59     if (opt.is_set("-c")) location = OSKAR_CPU;
60     if (opt.is_set("-cl")) location = OSKAR_CL;
61     if (location < 0)
62     {
63         opt.error("Please select one of -g, -c or -cl");
64         return EXIT_FAILURE;
65     }
66 
67     int precision = opt.is_set("-sp") ? OSKAR_SINGLE : OSKAR_DOUBLE;
68     bool evaluate_2d = opt.is_set("-2d") ? true : false;
69     int niter = opt.get_int("-n");
70     int num_element_types = opt.get_int("-e");
71 
72     if (op_type == UNDEF || op_type_count != 1)
73     {
74         opt.error("Please select one of the following flags: -o2c, -c2c, -m2m");
75         return EXIT_FAILURE;
76     }
77 
78     if (opt.is_set("-v"))
79     {
80         printf("\n");
81         printf("- Number of elements: %i\n", num_elements);
82         printf("- Number of directions: %i\n", num_directions);
83         printf("- Precision: %s\n", (precision == OSKAR_SINGLE) ? "single" : "double");
84         printf("- %s\n", evaluate_2d ? "2D" : "3D");
85         printf("- Operation type: ");
86         if (op_type == C2C)
87         {
88             printf("c2c\n");
89         }
90         else if (op_type == M2M)
91         {
92             printf("m2m\n");
93         }
94         else
95         {
96             printf("Error undefined!\n");
97         }
98         printf("- Number of iterations: %i\n", niter);
99         printf("\n");
100     }
101 
102     double time_taken = 0.0;
103     oskar_device_set_require_double_precision(precision == OSKAR_DOUBLE);
104     int status = benchmark(num_elements, num_directions, num_element_types,
105             op_type, location, precision, evaluate_2d, niter, time_taken);
106 
107     if (status)
108     {
109         fprintf(stderr, "ERROR: array pattern evaluation failed with code %i: "
110                 "%s\n", status, oskar_get_error_string(status));
111         return EXIT_FAILURE;
112     }
113     if (opt.is_set("-v"))
114     {
115         printf("==> Total time taken: %f seconds.\n", time_taken);
116         printf("==> Time taken per iteration: %f seconds.\n", time_taken/niter);
117         printf("\n");
118     }
119     else
120     {
121         printf("%f\n", time_taken/niter);
122     }
123 
124     return EXIT_SUCCESS;
125 }
126 
127 
benchmark(int num_elements,int num_directions,int num_element_types,OpType op_type,int loc,int precision,bool evaluate_2d,int niter,double & time_taken)128 int benchmark(int num_elements, int num_directions, int num_element_types,
129         OpType op_type, int loc, int precision, bool evaluate_2d, int niter,
130         double& time_taken)
131 {
132     int status = 0;
133     int type = precision | OSKAR_COMPLEX;
134     oskar_Mem *beam = 0, *signal = 0, *z = 0, *z_i = 0;
135     oskar_Mem *x = oskar_mem_create(precision, loc, num_directions, &status);
136     oskar_Mem *y = oskar_mem_create(precision, loc, num_directions, &status);
137     oskar_Mem *x_i = oskar_mem_create(precision, loc, num_elements, &status);
138     oskar_Mem *y_i = oskar_mem_create(precision, loc, num_elements, &status);
139     oskar_Mem *weights = oskar_mem_create(type, loc, num_elements, &status);
140     oskar_Mem *element_types_cpu = oskar_mem_create(OSKAR_INT, OSKAR_CPU,
141             num_elements, &status);
142     int* el_type = oskar_mem_int(element_types_cpu, &status);
143     for (int j = 0; j < num_elements; j += num_element_types)
144     {
145         for (int i = 0; i < num_element_types; ++i)
146         {
147             if (i + j >= num_elements) break;
148             el_type[i + j] = i;
149         }
150     }
151     if (!evaluate_2d)
152     {
153         z = oskar_mem_create(precision, loc, num_directions, &status);
154         z_i = oskar_mem_create(precision, loc, num_elements, &status);
155     }
156     int num_signals = num_directions * num_elements;
157     if (op_type == M2M) type |= OSKAR_MATRIX;
158     beam = oskar_mem_create(type, loc, num_directions, &status);
159     signal = oskar_mem_create(type, loc, num_signals, &status);
160     oskar_Mem* element_types = oskar_mem_create_copy(
161             element_types_cpu, loc, &status);
162 
163     oskar_Timer *tmr = oskar_timer_create(loc);
164     if (!status)
165     {
166         char* device_name = oskar_device_name(loc, 0);
167         printf("Using device '%s'\n", device_name);
168         free(device_name);
169         oskar_timer_start(tmr);
170         for (int i = 0; i < niter; ++i)
171         {
172             oskar_dftw(0, num_elements, 2.0 * M_PI, weights, x_i, y_i, z_i,
173                     0, num_directions, x, y, z,
174                     element_types, signal, 1, 1, 0, beam, &status);
175         }
176         time_taken = oskar_timer_elapsed(tmr);
177     }
178 
179     // Free memory.
180     oskar_timer_free(tmr);
181     oskar_mem_free(x, &status);
182     oskar_mem_free(y, &status);
183     oskar_mem_free(z, &status);
184     oskar_mem_free(x_i, &status);
185     oskar_mem_free(y_i, &status);
186     oskar_mem_free(z_i, &status);
187     oskar_mem_free(weights, &status);
188     oskar_mem_free(element_types, &status);
189     oskar_mem_free(element_types_cpu, &status);
190     oskar_mem_free(beam, &status);
191     oskar_mem_free(signal, &status);
192 
193     return status;
194 }
195