xref: /aosp_15_r20/external/FP16/bench/to-ieee-array.cc (revision 5f32b7105932ea8520a0e8811c640f936367d707)
1*5f32b710SXin Li #include <benchmark/benchmark.h>
2*5f32b710SXin Li 
3*5f32b710SXin Li #include <fp16.h>
4*5f32b710SXin Li #ifndef EMSCRIPTEN
5*5f32b710SXin Li 	#include <fp16/psimd.h>
6*5f32b710SXin Li #endif
7*5f32b710SXin Li 
8*5f32b710SXin Li #include <vector>
9*5f32b710SXin Li #include <random>
10*5f32b710SXin Li #include <chrono>
11*5f32b710SXin Li #include <functional>
12*5f32b710SXin Li #include <algorithm>
13*5f32b710SXin Li 
14*5f32b710SXin Li #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__)
15*5f32b710SXin Li 	#include <immintrin.h>
16*5f32b710SXin Li #endif
17*5f32b710SXin Li 
18*5f32b710SXin Li #if defined(__ARM_NEON__) || defined(__aarch64__)
19*5f32b710SXin Li 	#include <arm_neon.h>
20*5f32b710SXin Li #endif
21*5f32b710SXin Li 
22*5f32b710SXin Li #ifdef FP16_COMPARATIVE_BENCHMARKS
23*5f32b710SXin Li 	#include <third-party/THHalf.h>
24*5f32b710SXin Li 	#include <third-party/npy-halffloat.h>
25*5f32b710SXin Li 	#include <third-party/eigen-half.h>
26*5f32b710SXin Li 	#include <third-party/float16-compressor.h>
27*5f32b710SXin Li 	#include <third-party/half.hpp>
28*5f32b710SXin Li #endif
29*5f32b710SXin Li 
30*5f32b710SXin Li 
fp16_ieee_from_fp32_value(benchmark::State & state)31*5f32b710SXin Li static void fp16_ieee_from_fp32_value(benchmark::State& state) {
32*5f32b710SXin Li 	const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
33*5f32b710SXin Li 	auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
34*5f32b710SXin Li 
35*5f32b710SXin Li 	std::vector<float> fp32(state.range(0));
36*5f32b710SXin Li 	std::vector<uint16_t> fp16(state.range(0));
37*5f32b710SXin Li 	std::generate(fp32.begin(), fp32.end(), std::ref(rng));
38*5f32b710SXin Li 
39*5f32b710SXin Li 	while (state.KeepRunning()) {
40*5f32b710SXin Li 		float* input = fp32.data();
41*5f32b710SXin Li 		benchmark::DoNotOptimize(input);
42*5f32b710SXin Li 
43*5f32b710SXin Li 		uint16_t* output = fp16.data();
44*5f32b710SXin Li 		const size_t n = state.range(0);
45*5f32b710SXin Li 		for (size_t i = 0; i < n; i++) {
46*5f32b710SXin Li 			output[i] = fp16_ieee_from_fp32_value(input[i]);
47*5f32b710SXin Li 		}
48*5f32b710SXin Li 
49*5f32b710SXin Li 		benchmark::DoNotOptimize(output);
50*5f32b710SXin Li 	}
51*5f32b710SXin Li 	state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
52*5f32b710SXin Li }
53*5f32b710SXin Li BENCHMARK(fp16_ieee_from_fp32_value)->RangeMultiplier(2)->Range(1<<10, 64<<20);
54*5f32b710SXin Li 
55*5f32b710SXin Li #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__)
hardware_mm_cvtps_ph(benchmark::State & state)56*5f32b710SXin Li 	static void hardware_mm_cvtps_ph(benchmark::State& state) {
57*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
58*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
59*5f32b710SXin Li 
60*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
61*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
62*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
63*5f32b710SXin Li 
64*5f32b710SXin Li 		while (state.KeepRunning()) {
65*5f32b710SXin Li 			float* input = fp32.data();
66*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
67*5f32b710SXin Li 
68*5f32b710SXin Li 			uint16_t* output = fp16.data();
69*5f32b710SXin Li 			const size_t n = state.range(0);
70*5f32b710SXin Li 			for (size_t i = 0; i < n; i += 4) {
71*5f32b710SXin Li 				_mm_storel_epi64(
72*5f32b710SXin Li 					static_cast<__m128i*>(static_cast<void*>(&output[i])),
73*5f32b710SXin Li 					_mm_cvtps_ph(_mm_loadu_ps(&input[i]), _MM_FROUND_CUR_DIRECTION));
74*5f32b710SXin Li 			}
75*5f32b710SXin Li 
76*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
77*5f32b710SXin Li 		}
78*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
79*5f32b710SXin Li 	}
80*5f32b710SXin Li 	BENCHMARK(hardware_mm_cvtps_ph)->RangeMultiplier(2)->Range(1<<10, 64<<20);
81*5f32b710SXin Li 
hardware_mm256_cvtps_ph(benchmark::State & state)82*5f32b710SXin Li 	static void hardware_mm256_cvtps_ph(benchmark::State& state) {
83*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
84*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
85*5f32b710SXin Li 
86*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
87*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
88*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
89*5f32b710SXin Li 
90*5f32b710SXin Li 		while (state.KeepRunning()) {
91*5f32b710SXin Li 			float* input = fp32.data();
92*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
93*5f32b710SXin Li 
94*5f32b710SXin Li 			uint16_t* output = fp16.data();
95*5f32b710SXin Li 			const size_t n = state.range(0);
96*5f32b710SXin Li 			for (size_t i = 0; i < n; i += 8) {
97*5f32b710SXin Li 				_mm_storeu_si128(
98*5f32b710SXin Li 					static_cast<__m128i*>(static_cast<void*>(&output[i])),
99*5f32b710SXin Li 					_mm256_cvtps_ph(_mm256_loadu_ps(&input[i]), _MM_FROUND_CUR_DIRECTION));
100*5f32b710SXin Li 			}
101*5f32b710SXin Li 
102*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
103*5f32b710SXin Li 		}
104*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
105*5f32b710SXin Li 	}
106*5f32b710SXin Li 	BENCHMARK(hardware_mm256_cvtps_ph)->RangeMultiplier(2)->Range(1<<10, 64<<20);
107*5f32b710SXin Li #endif
108*5f32b710SXin Li 
109*5f32b710SXin Li #if defined(__ARM_NEON_FP) && (__ARM_NEON_FP & 0x2) || defined(__aarch64__)
hardware_vcvt_f16_f32(benchmark::State & state)110*5f32b710SXin Li 	static void hardware_vcvt_f16_f32(benchmark::State& state) {
111*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
112*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
113*5f32b710SXin Li 
114*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
115*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
116*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
117*5f32b710SXin Li 
118*5f32b710SXin Li 		while (state.KeepRunning()) {
119*5f32b710SXin Li 			float* input = fp32.data();
120*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
121*5f32b710SXin Li 
122*5f32b710SXin Li 			uint16_t* output = fp16.data();
123*5f32b710SXin Li 			const size_t n = state.range(0);
124*5f32b710SXin Li 			#if defined(__aarch64__)
125*5f32b710SXin Li 				const unsigned int fpcr = __builtin_aarch64_get_fpcr();
126*5f32b710SXin Li 				/* Disable flush-to-zero (bit 24) and Alternative FP16 format (bit 26) */
127*5f32b710SXin Li 				__builtin_aarch64_set_fpcr(fpcr & 0xF6FFFFFFu);
128*5f32b710SXin Li 			#else
129*5f32b710SXin Li 				unsigned int fpscr;
130*5f32b710SXin Li 				__asm__ __volatile__ ("VMRS %[fpscr], fpscr" : [fpscr] "=r" (fpscr));
131*5f32b710SXin Li 				/* Disable flush-to-zero (bit 24) and Alternative FP16 format (bit 26) */
132*5f32b710SXin Li 				__asm__ __volatile__ ("VMSR fpscr, %[fpscr]" :
133*5f32b710SXin Li 					: [fpscr] "r" (fpscr & 0xF6FFFFFFu));
134*5f32b710SXin Li 			#endif
135*5f32b710SXin Li 			for (size_t i = 0; i < n; i += 4) {
136*5f32b710SXin Li 				vst1_u16(&output[i],
137*5f32b710SXin Li 					(uint16x4_t) vcvt_f16_f32(
138*5f32b710SXin Li 						vld1q_f32(&input[i])));
139*5f32b710SXin Li 			}
140*5f32b710SXin Li 			#if defined(__aarch64__)
141*5f32b710SXin Li 				__builtin_aarch64_set_fpcr(fpcr);
142*5f32b710SXin Li 			#else
143*5f32b710SXin Li 				__asm__ __volatile__ ("VMSR fpscr, %[fpscr]" :: [fpscr] "r" (fpscr));
144*5f32b710SXin Li 			#endif
145*5f32b710SXin Li 
146*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
147*5f32b710SXin Li 		}
148*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
149*5f32b710SXin Li 	}
150*5f32b710SXin Li 	BENCHMARK(hardware_vcvt_f16_f32)->RangeMultiplier(2)->Range(1<<10, 64<<20);
151*5f32b710SXin Li #endif
152*5f32b710SXin Li 
153*5f32b710SXin Li #ifdef FP16_COMPARATIVE_BENCHMARKS
TH_float2halfbits(benchmark::State & state)154*5f32b710SXin Li 	static void TH_float2halfbits(benchmark::State& state) {
155*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
156*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
157*5f32b710SXin Li 
158*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
159*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
160*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
161*5f32b710SXin Li 
162*5f32b710SXin Li 		while (state.KeepRunning()) {
163*5f32b710SXin Li 			float* input = fp32.data();
164*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
165*5f32b710SXin Li 
166*5f32b710SXin Li 			uint16_t* output = fp16.data();
167*5f32b710SXin Li 			const size_t n = state.range(0);
168*5f32b710SXin Li 			for (size_t i = 0; i < n; i++) {
169*5f32b710SXin Li 				TH_float2halfbits(&input[i], &output[i]);
170*5f32b710SXin Li 			}
171*5f32b710SXin Li 
172*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
173*5f32b710SXin Li 		}
174*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
175*5f32b710SXin Li 	}
176*5f32b710SXin Li 	BENCHMARK(TH_float2halfbits)->RangeMultiplier(2)->Range(1<<10, 64<<20);
177*5f32b710SXin Li 
npy_floatbits_to_halfbits(benchmark::State & state)178*5f32b710SXin Li 	static void npy_floatbits_to_halfbits(benchmark::State& state) {
179*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
180*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
181*5f32b710SXin Li 
182*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
183*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
184*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
185*5f32b710SXin Li 
186*5f32b710SXin Li 		while (state.KeepRunning()) {
187*5f32b710SXin Li 			float* input = fp32.data();
188*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
189*5f32b710SXin Li 
190*5f32b710SXin Li 			uint16_t* output = fp16.data();
191*5f32b710SXin Li 			const size_t n = state.range(0);
192*5f32b710SXin Li 			for (size_t i = 0; i < n; i++) {
193*5f32b710SXin Li 				output[i] = npy_floatbits_to_halfbits(fp32_to_bits(input[i]));
194*5f32b710SXin Li 			}
195*5f32b710SXin Li 
196*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
197*5f32b710SXin Li 		}
198*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
199*5f32b710SXin Li 	}
200*5f32b710SXin Li 	BENCHMARK(npy_floatbits_to_halfbits)->RangeMultiplier(2)->Range(1<<10, 64<<20);
201*5f32b710SXin Li 
Eigen_float_to_half_rtne(benchmark::State & state)202*5f32b710SXin Li 	static void Eigen_float_to_half_rtne(benchmark::State& state) {
203*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
204*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
205*5f32b710SXin Li 
206*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
207*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
208*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
209*5f32b710SXin Li 
210*5f32b710SXin Li 		while (state.KeepRunning()) {
211*5f32b710SXin Li 			float* input = fp32.data();
212*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
213*5f32b710SXin Li 
214*5f32b710SXin Li 			uint16_t* output = fp16.data();
215*5f32b710SXin Li 			const size_t n = state.range(0);
216*5f32b710SXin Li 			for (size_t i = 0; i < n; i++) {
217*5f32b710SXin Li 				output[i] = Eigen::half_impl::float_to_half_rtne(input[i]).x;
218*5f32b710SXin Li 			}
219*5f32b710SXin Li 
220*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
221*5f32b710SXin Li 		}
222*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
223*5f32b710SXin Li 	}
224*5f32b710SXin Li 	BENCHMARK(Eigen_float_to_half_rtne)->RangeMultiplier(2)->Range(1<<10, 64<<20);
225*5f32b710SXin Li 
Float16Compressor_compress(benchmark::State & state)226*5f32b710SXin Li 	static void Float16Compressor_compress(benchmark::State& state) {
227*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
228*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
229*5f32b710SXin Li 
230*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
231*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
232*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
233*5f32b710SXin Li 
234*5f32b710SXin Li 		while (state.KeepRunning()) {
235*5f32b710SXin Li 			float* input = fp32.data();
236*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
237*5f32b710SXin Li 
238*5f32b710SXin Li 			uint16_t* output = fp16.data();
239*5f32b710SXin Li 			const size_t n = state.range(0);
240*5f32b710SXin Li 			for (size_t i = 0; i < n; i++) {
241*5f32b710SXin Li 				output[i] = Float16Compressor::compress(input[i]);
242*5f32b710SXin Li 			}
243*5f32b710SXin Li 
244*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
245*5f32b710SXin Li 		}
246*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
247*5f32b710SXin Li 	}
248*5f32b710SXin Li 	BENCHMARK(Float16Compressor_compress)->RangeMultiplier(2)->Range(1<<10, 64<<20);
249*5f32b710SXin Li 
half_float_detail_float2half_table(benchmark::State & state)250*5f32b710SXin Li 	static void half_float_detail_float2half_table(benchmark::State& state) {
251*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
252*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
253*5f32b710SXin Li 
254*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
255*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
256*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
257*5f32b710SXin Li 
258*5f32b710SXin Li 		while (state.KeepRunning()) {
259*5f32b710SXin Li 			float* input = fp32.data();
260*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
261*5f32b710SXin Li 
262*5f32b710SXin Li 			uint16_t* output = fp16.data();
263*5f32b710SXin Li 			const size_t n = state.range(0);
264*5f32b710SXin Li 			for (size_t i = 0; i < n; i++) {
265*5f32b710SXin Li 				output[i] =
266*5f32b710SXin Li 					half_float::detail::float2half_impl<std::round_to_nearest>(
267*5f32b710SXin Li 						input[i], half_float::detail::true_type());
268*5f32b710SXin Li 			}
269*5f32b710SXin Li 
270*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
271*5f32b710SXin Li 		}
272*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
273*5f32b710SXin Li 	}
274*5f32b710SXin Li 	BENCHMARK(half_float_detail_float2half_table)->RangeMultiplier(2)->Range(1<<10, 64<<20);
275*5f32b710SXin Li 
half_float_detail_float2half_branch(benchmark::State & state)276*5f32b710SXin Li 	static void half_float_detail_float2half_branch(benchmark::State& state) {
277*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
278*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
279*5f32b710SXin Li 
280*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
281*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
282*5f32b710SXin Li 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
283*5f32b710SXin Li 
284*5f32b710SXin Li 		while (state.KeepRunning()) {
285*5f32b710SXin Li 			float* input = fp32.data();
286*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
287*5f32b710SXin Li 
288*5f32b710SXin Li 			uint16_t* output = fp16.data();
289*5f32b710SXin Li 			const size_t n = state.range(0);
290*5f32b710SXin Li 			for (size_t i = 0; i < n; i++) {
291*5f32b710SXin Li 				output[i] =
292*5f32b710SXin Li 					half_float::detail::float2half_impl<std::round_to_nearest>(
293*5f32b710SXin Li 						input[i], half_float::detail::false_type());
294*5f32b710SXin Li 			}
295*5f32b710SXin Li 
296*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
297*5f32b710SXin Li 		}
298*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
299*5f32b710SXin Li 	}
300*5f32b710SXin Li 	BENCHMARK(half_float_detail_float2half_branch)->RangeMultiplier(2)->Range(1<<10, 64<<20);
301*5f32b710SXin Li #endif
302*5f32b710SXin Li 
303*5f32b710SXin Li BENCHMARK_MAIN();
304