Blender V4.3
BLI_math_half_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "testing/testing.h"
6
7#include "BLI_math_half.hh"
8#include "BLI_time.h"
9
10#include <cmath>
11
12// #define DO_PERF_TESTS 1
13
14namespace blender::tests {
15
16TEST(math_half, half_to_float_scalar)
17{
19 EXPECT_EQ(blender::math::half_to_float(1), 5.960464478e-08f);
20 EXPECT_EQ(blender::math::half_to_float(32), 1.907348633e-06f);
21 EXPECT_EQ(blender::math::half_to_float(37), 2.205371857e-06f);
22 EXPECT_EQ(blender::math::half_to_float(511), 3.045797348e-05f);
23 EXPECT_EQ(blender::math::half_to_float(999), 5.954504013e-05f);
24 EXPECT_EQ(blender::math::half_to_float(1024), 6.103515625e-05f);
25 EXPECT_EQ(blender::math::half_to_float(1357), 8.088350296e-05f);
26 EXPECT_EQ(blender::math::half_to_float(6789), 0.003183364868f);
27 EXPECT_EQ(blender::math::half_to_float(16383), 1.999023438f);
30 EXPECT_EQ(blender::math::half_to_float(31744), std::numeric_limits<float>::infinity());
31 EXPECT_TRUE(std::isnan(blender::math::half_to_float(31746)));
32 EXPECT_TRUE(std::isnan(blender::math::half_to_float(32767)));
34 EXPECT_EQ(blender::math::half_to_float(32769), -5.960464478e-08f);
35 EXPECT_EQ(blender::math::half_to_float(46765), -0.4172363281f);
36 EXPECT_EQ(blender::math::half_to_float(54501), -78.3125f);
37 EXPECT_EQ(blender::math::half_to_float(64511), -65504.0f);
38 EXPECT_EQ(blender::math::half_to_float(64512), -std::numeric_limits<float>::infinity());
39 EXPECT_TRUE(std::isnan(blender::math::half_to_float(64513)));
40 EXPECT_TRUE(std::isnan(blender::math::half_to_float(65535)));
41}
42
43TEST(math_half, float_to_half_scalar)
44{
45#define HFUN(v) blender::math::float_to_half(v)
46 EXPECT_EQ(HFUN(0.0f), 0);
47 EXPECT_EQ(HFUN(std::numeric_limits<float>::min()), 0);
48 EXPECT_EQ(HFUN(5.960464478e-08f), 1);
49 EXPECT_EQ(HFUN(1.907348633e-06f), 32);
50 EXPECT_EQ(HFUN(2.205371857e-06f), 37);
51 EXPECT_EQ(HFUN(3.045797348e-05f), 511);
52 EXPECT_EQ(HFUN(5.954504013e-05f), 999);
53 EXPECT_EQ(HFUN(6.103515625e-05f), 1024);
54 EXPECT_EQ(HFUN(8.088350296e-05f), 1357);
55 EXPECT_EQ(HFUN(0.003183364868f), 6789);
56 EXPECT_EQ(HFUN(0.1f), 11878);
57 EXPECT_EQ(HFUN(1.0f), 15360);
58 EXPECT_EQ(HFUN(1.999023438f), 16383);
59 EXPECT_EQ(HFUN(1.999523438f), 16384);
60 EXPECT_EQ(HFUN(2.0f), 16384);
61 EXPECT_EQ(HFUN(11.0f), 18816);
62 EXPECT_EQ(HFUN(65504.0f), 31743);
63 EXPECT_EQ(HFUN(65535.0f), 31744); /* FP16 inf */
64 EXPECT_EQ(HFUN(1.0e6f), 31744); /* FP16 inf */
65 EXPECT_EQ(HFUN(std::numeric_limits<float>::infinity()), 31744);
66 EXPECT_EQ(HFUN(std::numeric_limits<float>::max()), 31744);
67 EXPECT_EQ(HFUN(std::numeric_limits<float>::quiet_NaN()), 32256);
68 EXPECT_EQ(HFUN(-0.0f), 32768);
69 EXPECT_EQ(HFUN(-5.960464478e-08f), 32769);
70 EXPECT_EQ(HFUN(-0.4172363281f), 46765);
71 EXPECT_EQ(HFUN(-1.0f), 48128);
72 EXPECT_EQ(HFUN(-78.3125f), 54501);
73 EXPECT_EQ(HFUN(-123.5f), 55224);
74 EXPECT_EQ(HFUN(-65504.0f), 64511);
75 EXPECT_EQ(HFUN(-65536.0f), 64512); /* FP16 -inf */
76 EXPECT_EQ(HFUN(-1.0e6f), 64512); /* FP16 -inf */
77 EXPECT_EQ(HFUN(-std::numeric_limits<float>::infinity()), 64512);
78#undef HFUN
79}
80
82{
83 const uint16_t src[13] = {
84 0, 1, 6789, 16383, 16384, 31743, 31744, 32768, 32769, 46765, 54501, 64511, 64512};
85 /* One extra entry in destination, to check that function leaves it intact. */
86 const float exp[14] = {
87 0.0f,
88 5.960464478e-08f,
89 0.003183364868f,
90 1.999023438f,
91 2.0f,
92 65504.0f,
93 std::numeric_limits<float>::infinity(),
94 -0.0f,
95 -5.960464478e-08f,
96 -0.4172363281f,
97 -78.3125f,
98 -65504.0f,
99 -std::numeric_limits<float>::infinity(),
100 1.2345f,
101 };
102 float dst[14] = {};
103 dst[13] = 1.2345f;
104
106 EXPECT_EQ_ARRAY(exp, dst, 14);
107}
108
110{
111 const float src[13] = {0.0f,
112 5.960464478e-08f,
113 0.003183364868f,
114 1.999023438f,
115 2.0f,
116 65504.0f,
117 std::numeric_limits<float>::infinity(),
118 -0.0f,
119 -5.960464478e-08f,
120 -0.4172363281f,
121 -78.3125f,
122 -65504.0f,
123 -std::numeric_limits<float>::infinity()};
124 /* One extra entry in destination, to check that function leaves it intact. */
125 const uint16_t exp[14] = {
126 0, 1, 6789, 16383, 16384, 31743, 31744, 32768, 32769, 46765, 54501, 64511, 64512, 12345};
127 uint16_t dst[14] = {};
128 dst[13] = 12345;
129
131 EXPECT_EQ_ARRAY(exp, dst, 14);
132}
133
134#ifdef DO_PERF_TESTS
135
136/*
137 * Time to convert 100 million numbers FP16 -> FP32.
138 *
139 * Ryzen 5950X (VS2022):
140 * - `half_to_float`: 164ms
141 * - `half_to_float_array`: 132ms (scalar)
142 * - `half_to_float_array`: 84ms (SSE2 4x wide path)
143 * - `half_to_float_array`: 86ms (w/ AVX2 F16C - however Blender is not compiled for AVX2 yet)
144 *
145 * Mac M1 Max (Clang 15), using NEON VCVT:
146 * - `half_to_float`: 97ms
147 * - `half_to_float_array`: 53ms
148 */
149TEST(math_half_perf, half_to_float_scalar)
150{
151 double t0 = BLI_time_now_seconds();
152 size_t sum = 0;
153 for (int i = 0; i < 100'000'000; i++) {
154 float f = blender::math::half_to_float(uint16_t(i & 0xFFFF));
155 uint32_t fu;
156 memcpy(&fu, &f, sizeof(f));
157 sum += fu;
158 }
159 double t1 = BLI_time_now_seconds();
160 printf("- FP16->FP32 scalar: %.3fs sum %zu\n", t1 - t0, sum);
161}
162
163TEST(math_half_perf, half_to_float_array)
164{
165 const int test_size = 100'000'000;
166 uint16_t *src = new uint16_t[test_size];
167 float *dst = new float[test_size];
168 for (int i = 0; i < test_size; i++) {
169 src[i] = i & 0xFFFF;
170 }
171 double t0 = BLI_time_now_seconds();
172 size_t sum = 0;
173 blender::math::half_to_float_array(src, dst, test_size);
174 for (int i = 0; i < test_size; i++) {
175 uint32_t fu;
176 memcpy(&fu, &dst[i], sizeof(fu));
177 sum += fu;
178 }
179 double t1 = BLI_time_now_seconds();
180 printf("- FP16->FP32 array : %.3fs sum %zu\n", t1 - t0, sum);
181 delete[] src;
182 delete[] dst;
183}
184
185/*
186 * Time to convert 100 million numbers FP32 -> FP16.
187 *
188 * Ryzen 5950X (VS2022):
189 * - `float_to_half`: 242ms
190 * - `float_to_half_array`: 184ms (scalar)
191 * - `float_to_half_array`: 68ms (SSE2 4x wide path)
192 * - `float_to_half_array`: 50ms (w/ AVX2 F16C - however Blender is not compiled for AVX2 yet)
193 *
194 * Mac M1 Max (Clang 15), using NEON VCVT:
195 * - `float_to_half`: 93ms
196 * - `float_to_half_array`: 21ms
197 */
198TEST(math_half_perf, float_to_half_scalar)
199{
200 double t0 = BLI_time_now_seconds();
201 uint32_t sum = 0;
202 for (int i = 0; i < 100'000'000; i++) {
203 float f = ((i & 0xFFFF) - 0x8000) + 0.1f;
205 sum += h;
206 }
207 double t1 = BLI_time_now_seconds();
208 printf("- FP32->FP16 scalar: %.3fs sum %u\n", t1 - t0, sum);
209}
210
211TEST(math_half_perf, float_to_half_array)
212{
213 const int test_size = 100'000'000;
214 float *src = new float[test_size];
215 uint16_t *dst = new uint16_t[test_size];
216 for (int i = 0; i < test_size; i++) {
217 src[i] = ((i & 0xFFFF) - 0x8000) + 0.1f;
218 }
219
220 double t0 = BLI_time_now_seconds();
221 uint32_t sum = 0;
222 blender::math::float_to_half_array(src, dst, test_size);
223 for (int i = 0; i < test_size; i++) {
224 sum += dst[i];
225 }
226 double t1 = BLI_time_now_seconds();
227 printf("- FP32->FP16 array : %.3fs sum %u\n", t1 - t0, sum);
228 delete[] src;
229 delete[] dst;
230}
231
232#endif // #ifdef DO_PERF_TESTS
233
234} // namespace blender::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define HFUN(v)
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
static T sum(const btAlignedObjectArray< T > &items)
#define printf
void float_to_half_array(const float *src, uint16_t *dst, size_t length)
Definition math_half.cc:221
uint16_t float_to_half(float v)
Definition math_half.cc:27
T exp(const T &x)
void half_to_float_array(const uint16_t *src, float *dst, size_t length)
Definition math_half.cc:257
float half_to_float(uint16_t v)
Definition math_half.cc:91
TEST(any, DefaultConstructor)
unsigned short uint16_t
Definition stdint.h:79
unsigned int uint32_t
Definition stdint.h:80