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