1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <syscall.h>
16
17 #include <cmath>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <ctime>
22
23 #include "pffft_sapi.sapi.h" // NOLINT(build/include)
24 #include "absl/flags/flag.h"
25 #include "absl/flags/parse.h"
26 #include "absl/log/globals.h"
27 #include "absl/log/initialize.h"
28 #include "sandboxed_api/vars.h"
29
30 class PffftSapiSandbox : public PffftSandbox {
31 public:
ModifyPolicy(sandbox2::PolicyBuilder *)32 std::unique_ptr<sandbox2::Policy> ModifyPolicy(sandbox2::PolicyBuilder*) {
33 return sandbox2::PolicyBuilder()
34 .AllowStaticStartup()
35 .AllowOpen()
36 .AllowRead()
37 .AllowWrite()
38 .AllowSystemMalloc()
39 .AllowExit()
40 .AllowSyscalls({
41 __NR_futex,
42 __NR_close,
43 __NR_getrusage,
44 })
45 .BuildOrDie();
46 }
47 };
48
49 ABSL_FLAG(bool, verbose_output, true, "Whether to display verbose output");
50
UclockSec()51 double UclockSec() { return static_cast<double>(clock()) / CLOCKS_PER_SEC; }
52
ShowOutput(const char * name,int n,int complex,float flops,float t0,float t1,int max_iter)53 void ShowOutput(const char* name, int n, int complex, float flops, float t0,
54 float t1, int max_iter) {
55 float mflops = flops / 1e6 / (t1 - t0 + 1e-16);
56 if (absl::GetFlag(FLAGS_verbose_output)) {
57 if (flops != -1) {
58 printf("|%9.0f ", mflops);
59 } else {
60 printf("| n/a ");
61 }
62 } else if (flops != -1) {
63 printf("n=%5d, %s %16s : %6.0f MFlops [t=%6.0f ns, %d runs]\n", n,
64 (complex ? "CPLX" : "REAL"), name, mflops,
65 (t1 - t0) / 2 / max_iter * 1e9, max_iter);
66 }
67 fflush(stdout);
68 }
69
PffftMain()70 absl::Status PffftMain() {
71 LOG(INFO) << "Initializing sandbox...\n";
72
73 PffftSapiSandbox sandbox;
74 SAPI_RETURN_IF_ERROR(sandbox.Init());
75
76 PffftApi api(&sandbox);
77
78 // kTransformSizes is a vector keeping the values by which iterates n, its
79 // value representing the input length. More concrete, n is the number of data
80 // points the caclulus is up to (determinating its accuracy). To show the
81 // performance of Fast-Fourier Transformations the program is testing for
82 // various values of n.
83 constexpr int kTransformSizes[] = {
84 64, 96, 128, 160, 192, 256, 384, 5 * 96, 512, 5 * 128,
85 3 * 256, 800, 1024, 2048, 2400, 4096, 8192, 9 * 1024, 16384, 32768};
86
87 for (int complex : {0, 1}) {
88 for (int n : kTransformSizes) {
89 const int n_float = n * (complex ? 2 : 1);
90 int n_bytes = n_float * sizeof(float);
91
92 std::vector<float> work(2 * n_float + 15, 0.0);
93 sapi::v::Array<float> work_array(&work[0], work.size());
94
95 std::vector<float> x(n_bytes, 0.0);
96 sapi::v::Array<float> x_array(&x[0], x.size());
97
98 std::vector<float> y(n_bytes, 0.0);
99 sapi::v::Array<float> y_array(&y[0], y.size());
100
101 std::vector<float> z(n_bytes, 0.0);
102 sapi::v::Array<float> z_array(&z[0], z.size());
103
104 double t0;
105 double t1;
106 double flops;
107
108 int max_iter = 5120000 / n * 4;
109
110 for (int k = 0; k < n_float; ++k) {
111 x[k] = 0;
112 }
113
114 // FFTPack benchmark
115 {
116 // SIMD_SZ == 4 (returning value of pffft_simd_size())
117 int simd_size_iter = max_iter / 4;
118
119 if (simd_size_iter == 0) simd_size_iter = 1;
120 if (complex) {
121 SAPI_RETURN_IF_ERROR(api.cffti(n, work_array.PtrBoth()));
122 } else {
123 SAPI_RETURN_IF_ERROR(api.rffti(n, work_array.PtrBoth()));
124 }
125 t0 = UclockSec();
126
127 for (int iter = 0; iter < simd_size_iter; ++iter) {
128 if (complex) {
129 SAPI_RETURN_IF_ERROR(
130 api.cfftf(n, x_array.PtrBoth(), work_array.PtrBoth()));
131 SAPI_RETURN_IF_ERROR(
132 api.cfftb(n, x_array.PtrBoth(), work_array.PtrBoth()));
133 } else {
134 SAPI_RETURN_IF_ERROR(
135 api.rfftf(n, x_array.PtrBoth(), work_array.PtrBoth()));
136 SAPI_RETURN_IF_ERROR(
137 api.rfftb(n, x_array.PtrBoth(), work_array.PtrBoth()));
138 }
139 }
140 t1 = UclockSec();
141
142 flops = (simd_size_iter * 2) *
143 ((complex ? 5 : 2.5) * static_cast<double>(n) *
144 log(static_cast<double>(n)) / M_LN2);
145 ShowOutput("FFTPack", n, complex, flops, t0, t1, simd_size_iter);
146 }
147
148 // PFFFT benchmark
149 {
150 SAPI_ASSIGN_OR_RETURN(
151 PFFFT_Setup * s,
152 api.pffft_new_setup(n, complex ? PFFFT_COMPLEX : PFFFT_REAL));
153
154 sapi::v::RemotePtr s_reg(s);
155
156 t0 = UclockSec();
157 for (int iter = 0; iter < max_iter; ++iter) {
158 SAPI_RETURN_IF_ERROR(
159 api.pffft_transform(&s_reg, x_array.PtrBoth(), z_array.PtrBoth(),
160 y_array.PtrBoth(), PFFFT_FORWARD));
161 SAPI_RETURN_IF_ERROR(
162 api.pffft_transform(&s_reg, x_array.PtrBoth(), z_array.PtrBoth(),
163 y_array.PtrBoth(), PFFFT_FORWARD));
164 }
165
166 t1 = UclockSec();
167 SAPI_RETURN_IF_ERROR(api.pffft_destroy_setup(&s_reg));
168
169 flops = (max_iter * 2) * ((complex ? 5 : 2.5) * static_cast<double>(n) *
170 log(static_cast<double>(n)) / M_LN2);
171 ShowOutput("PFFFT", n, complex, flops, t0, t1, max_iter);
172
173 LOG(INFO) << "n = " << n << " SUCCESSFULLY";
174 }
175 }
176 }
177
178 return absl::OkStatus();
179 }
180
main(int argc,char * argv[])181 int main(int argc, char* argv[]) {
182 absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
183 absl::ParseCommandLine(argc, argv);
184 absl::InitializeLog();
185
186 if (absl::Status status = PffftMain(); !status.ok()) {
187 LOG(ERROR) << "Initialization failed: " << status.ToString();
188 return EXIT_FAILURE;
189 }
190
191 return EXIT_SUCCESS;
192 }
193