Blender
V4.5
source
blender
blenlib
tests
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
14
namespace
blender::tests
{
15
16
TEST
(math_half, half_to_float_scalar)
17
{
18
EXPECT_EQ
(
blender::math::half_to_float
(0), 0.0f);
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);
28
EXPECT_EQ
(
blender::math::half_to_float
(16384), 2.0f);
29
EXPECT_EQ
(
blender::math::half_to_float
(31743), 65504.0f);
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)));
33
EXPECT_EQ
(
blender::math::half_to_float
(32768), -0.0f);
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
43
TEST
(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
81
TEST
(math_half,
half_to_float_array
)
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
105
blender::math::half_to_float_array
(src, dst, 13);
106
EXPECT_EQ_ARRAY(
exp
, dst, 14);
107
}
108
109
TEST
(math_half,
float_to_half_array
)
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
130
blender::math::float_to_half_array
(src, dst, 13);
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
*/
149
TEST
(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
163
TEST
(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
*/
198
TEST
(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;
204
uint16_t
h =
blender::math::float_to_half
(f);
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
211
TEST
(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
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
BLI_math_half.hh
HFUN
#define HFUN(v)
BLI_time.h
Platform independent time functions.
BLI_time_now_seconds
double BLI_time_now_seconds(void)
Definition
time.cc:65
printf
#define printf(...)
Definition
gpu_glsl_cpp_stubs.hh:32
blender::draw::gpencil::uint32_t
unsigned int uint32_t
Definition
gpencil_cache_utils.cc:81
blender::draw::gpencil::uint16_t
unsigned short uint16_t
Definition
gpencil_cache_utils.cc:80
blender::math::float_to_half_array
void float_to_half_array(const float *src, uint16_t *dst, size_t length)
Definition
math_half.cc:221
blender::math::float_to_half
uint16_t float_to_half(float v)
Definition
math_half.cc:27
blender::math::exp
T exp(const T &x)
Definition
BLI_math_base.hh:202
blender::math::half_to_float_array
void half_to_float_array(const uint16_t *src, float *dst, size_t length)
Definition
math_half.cc:257
blender::math::half_to_float
float half_to_float(uint16_t v)
Definition
math_half.cc:91
blender::tests
Definition
BLF_tests.cc:9
blender::tests::TEST
TEST(blf_load, load)
Definition
BLF_tests.cc:34
blender::TEST
TEST(BLI_string_utils, BLI_string_replace)
Definition
BLI_string_utils_test.cc:15
i
i
Definition
text_draw.cc:230
Generated on
for Blender by
doxygen
1.18.0