1 // Copyright 2020-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 #pragma once
4
5 #include <algorithm>
6 #include <chrono>
7 #include <random>
8 #include "../common/simd.h"
9 #include "openvkl_testing.h"
10 #include "rkcommon/utility/random.h"
11
12 /*
13 * Utilities for our benchmarking suite.
14 */
15
16 /*
17 * Macro which can be used to add warm-up iterations to Google benchmarks.
18 */
19
20 #define BENCHMARK_WARMUP_MIN_SECONDS 1.f
21
22 #define BENCHMARK_WARMUP_AND_RUN(BODY) \
23 /* warm-up iterations */ \
24 auto begin = std::chrono::steady_clock::now(); \
25 while (true) { \
26 BODY; \
27 auto end = std::chrono::steady_clock::now(); \
28 float durationSeconds = \
29 (std::chrono::duration_cast<std::chrono::microseconds>(end - begin) \
30 .count()) / \
31 1000000.f; \
32 if (durationSeconds >= BENCHMARK_WARMUP_MIN_SECONDS) { \
33 break; \
34 } \
35 } \
36 \
37 /* benchmark loop */ \
38 for (auto _ : state) { \
39 BODY; \
40 }
41
42 /*
43 * Traits classes for API programming models.
44 * These allow differentiating between benchmark implementations.
45 */
46 namespace programming_model {
47
48 struct Scalar
49 {
50 };
51
52 template <int W>
53 struct Vector
54 {
55 };
56
57 template <unsigned int N>
58 struct Stream
59 {
60 };
61
62 template <unsigned int M>
63 struct ScalarM
64 {
65 };
66
67 template <unsigned int M, int W>
68 struct VectorM
69 {
70 };
71
72 template <unsigned int M, unsigned int N>
73 struct StreamM
74 {
75 };
76
77 } // namespace programming_model
78
79 /*
80 * Coordinate generators for point sampling APIs (vklComputeSample,
81 * vklComputeGradient).
82 *
83 * The required interface is:
84 *
85 * const char * name(): A string that is used to generate test names.
86 * vkl_vec3f getNextSamplePos(): The next sample position.
87 */
88 namespace coordinate_generator {
89 using openvkl::vvec3fn;
90
91 /*
92 * The Fixed coordinate generator always generates the same point.
93 */
94 struct Fixed
95 {
nameFixed96 inline static constexpr const char *name()
97 {
98 return "Fixed";
99 }
100
valueFixed101 inline static constexpr float value()
102 {
103 return 0.1701f;
104 }
105
FixedFixed106 explicit inline Fixed(vkl_box3f const & /*bbox*/) {}
107
108 template <unsigned int N>
getNextNFixed109 inline void getNextN(vkl_vec3f *pos)
110 {
111 if (firstCall) {
112 std::fill(pos, pos + N, vkl_vec3f{value(), value(), value()});
113 firstCall = false;
114 }
115 }
116
117 template <int W>
getNextVFixed118 inline void getNextV(vvec3fn<W> *pos)
119 {
120 if (firstCall) {
121 std::fill(pos->x.v, pos->x.v + W, value());
122 std::fill(pos->y.v, pos->y.v + W, value());
123 std::fill(pos->z.v, pos->z.v + W, value());
124 firstCall = false;
125 }
126 }
127
128 bool firstCall{true};
129 };
130
131 /*
132 * The Random coordinate generator will generate points inside the
133 * volume bounding box.
134 * Note: This is currently non-deterministic!
135 */
136 struct Random
137 {
138 using Dist = rkcommon::utility::pcg32_biased_float_distribution;
139
nameRandom140 inline static constexpr const char *name()
141 {
142 return "Random";
143 }
144
RandomRandom145 explicit inline Random(vkl_box3f const &bbox)
146 : rd(),
147 distX(rd(), 0, bbox.lower.x, bbox.upper.x),
148 distY(rd(), 0, bbox.lower.y, bbox.upper.y),
149 distZ(rd(), 0, bbox.lower.z, bbox.upper.z)
150 {
151 }
152
153 template <unsigned int N>
getNextNRandom154 inline void getNextN(vkl_vec3f *pos)
155 {
156 for (unsigned int i = 0; i < N; ++i) {
157 pos[i].x = distX();
158 pos[i].y = distY();
159 pos[i].z = distZ();
160 }
161 }
162
163 template <int W>
getNextVRandom164 inline void getNextV(vvec3fn<W> *pos)
165 {
166 for (int i = 0; i < W; ++i) {
167 pos->x[i] = distX();
168 pos->y[i] = distY();
169 pos->z[i] = distZ();
170 }
171 }
172
173 std::random_device rd; // Generate seeds randomly.
174 Dist distX;
175 Dist distY;
176 Dist distZ;
177 };
178
179 } // namespace coordinate_generator
180
181 /*
182 * A convenience wrapper that takes an Api class and registers it as a
183 * benchmark.
184 */
185 template <class Api>
registerBenchmark()186 inline benchmark::internal::Benchmark *registerBenchmark()
187 {
188 return benchmark::RegisterBenchmark(Api::name().c_str(), Api::run);
189 }
190
191