xref: /aosp_15_r20/external/FP16/bench/from-alt-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(__ARM_NEON__) || defined(__aarch64__)
15*5f32b710SXin Li 	#include <arm_neon.h>
16*5f32b710SXin Li #endif
17*5f32b710SXin Li 
18*5f32b710SXin Li 
fp16_alt_to_fp32_bits(benchmark::State & state)19*5f32b710SXin Li static void fp16_alt_to_fp32_bits(benchmark::State& state) {
20*5f32b710SXin Li 	const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
21*5f32b710SXin Li 	auto rng = std::bind(std::uniform_int_distribution<uint16_t>(0, 0x7BFF), std::mt19937(seed));
22*5f32b710SXin Li 
23*5f32b710SXin Li 	std::vector<uint16_t> fp16(state.range(0));
24*5f32b710SXin Li 	std::vector<uint32_t> fp32(state.range(0));
25*5f32b710SXin Li 	std::generate(fp16.begin(), fp16.end(),
26*5f32b710SXin Li 		[&rng]{ return fp16_alt_from_fp32_value(rng()); });
27*5f32b710SXin Li 
28*5f32b710SXin Li 	while (state.KeepRunning()) {
29*5f32b710SXin Li 		uint16_t* input = fp16.data();
30*5f32b710SXin Li 		benchmark::DoNotOptimize(input);
31*5f32b710SXin Li 
32*5f32b710SXin Li 		uint32_t* output = fp32.data();
33*5f32b710SXin Li 		const size_t n = state.range(0);
34*5f32b710SXin Li 		for (size_t i = 0; i < n; i++) {
35*5f32b710SXin Li 			output[i] = fp16_alt_to_fp32_bits(input[i]);
36*5f32b710SXin Li 		}
37*5f32b710SXin Li 
38*5f32b710SXin Li 		benchmark::DoNotOptimize(output);
39*5f32b710SXin Li 	}
40*5f32b710SXin Li 	state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
41*5f32b710SXin Li }
42*5f32b710SXin Li BENCHMARK(fp16_alt_to_fp32_bits)->RangeMultiplier(2)->Range(1<<10, 64<<20);
43*5f32b710SXin Li 
fp16_alt_to_fp32_value(benchmark::State & state)44*5f32b710SXin Li static void fp16_alt_to_fp32_value(benchmark::State& state) {
45*5f32b710SXin Li 	const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
46*5f32b710SXin Li 	auto rng = std::bind(std::uniform_int_distribution<uint16_t>(0, 0x7BFF), std::mt19937(seed));
47*5f32b710SXin Li 
48*5f32b710SXin Li 	std::vector<uint16_t> fp16(state.range(0));
49*5f32b710SXin Li 	std::vector<float> fp32(state.range(0));
50*5f32b710SXin Li 	std::generate(fp16.begin(), fp16.end(),
51*5f32b710SXin Li 		[&rng]{ return fp16_alt_from_fp32_value(rng()); });
52*5f32b710SXin Li 
53*5f32b710SXin Li 	while (state.KeepRunning()) {
54*5f32b710SXin Li 		uint16_t* input = fp16.data();
55*5f32b710SXin Li 		benchmark::DoNotOptimize(input);
56*5f32b710SXin Li 
57*5f32b710SXin Li 		float* output = fp32.data();
58*5f32b710SXin Li 		const size_t n = state.range(0);
59*5f32b710SXin Li 		for (size_t i = 0; i < n; i++) {
60*5f32b710SXin Li 			output[i] = fp16_alt_to_fp32_value(input[i]);
61*5f32b710SXin Li 		}
62*5f32b710SXin Li 
63*5f32b710SXin Li 		benchmark::DoNotOptimize(output);
64*5f32b710SXin Li 	}
65*5f32b710SXin Li 	state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
66*5f32b710SXin Li }
67*5f32b710SXin Li BENCHMARK(fp16_alt_to_fp32_value)->RangeMultiplier(2)->Range(1<<10, 64<<20);
68*5f32b710SXin Li 
69*5f32b710SXin Li #ifndef EMSCRIPTEN
fp16_alt_to_fp32_psimd(benchmark::State & state)70*5f32b710SXin Li 	static void fp16_alt_to_fp32_psimd(benchmark::State& state) {
71*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
72*5f32b710SXin Li 		auto rng = std::bind(std::uniform_int_distribution<uint16_t>(0, 0x7BFF), std::mt19937(seed));
73*5f32b710SXin Li 
74*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
75*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
76*5f32b710SXin Li 		std::generate(fp16.begin(), fp16.end(),
77*5f32b710SXin Li 			[&rng]{ return fp16_alt_from_fp32_value(rng()); });
78*5f32b710SXin Li 
79*5f32b710SXin Li 		while (state.KeepRunning()) {
80*5f32b710SXin Li 			uint16_t* input = fp16.data();
81*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
82*5f32b710SXin Li 
83*5f32b710SXin Li 			float* output = fp32.data();
84*5f32b710SXin Li 			const size_t n = state.range(0);
85*5f32b710SXin Li 			for (size_t i = 0; i < n - 4; i += 4) {
86*5f32b710SXin Li 				psimd_store_f32(&output[i],
87*5f32b710SXin Li 					fp16_alt_to_fp32_psimd(
88*5f32b710SXin Li 						psimd_load_u16(&input[i])));
89*5f32b710SXin Li 			}
90*5f32b710SXin Li 			const psimd_u16 last_vector = { input[n - 4], input[n - 3], input[n - 2], input[n - 1] };
91*5f32b710SXin Li 			psimd_store_f32(&output[n - 4],
92*5f32b710SXin Li 				fp16_alt_to_fp32_psimd(last_vector));
93*5f32b710SXin Li 
94*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
95*5f32b710SXin Li 		}
96*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
97*5f32b710SXin Li 	}
98*5f32b710SXin Li 	BENCHMARK(fp16_alt_to_fp32_psimd)->RangeMultiplier(2)->Range(1<<10, 64<<20);
99*5f32b710SXin Li 
fp16_alt_to_fp32x2_psimd(benchmark::State & state)100*5f32b710SXin Li 	static void fp16_alt_to_fp32x2_psimd(benchmark::State& state) {
101*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
102*5f32b710SXin Li 		auto rng = std::bind(std::uniform_int_distribution<uint16_t>(0, 0x7BFF), std::mt19937(seed));
103*5f32b710SXin Li 
104*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
105*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
106*5f32b710SXin Li 		std::generate(fp16.begin(), fp16.end(),
107*5f32b710SXin Li 			[&rng]{ return fp16_alt_from_fp32_value(rng()); });
108*5f32b710SXin Li 
109*5f32b710SXin Li 		while (state.KeepRunning()) {
110*5f32b710SXin Li 			uint16_t* input = fp16.data();
111*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
112*5f32b710SXin Li 
113*5f32b710SXin Li 			float* output = fp32.data();
114*5f32b710SXin Li 			const size_t n = state.range(0);
115*5f32b710SXin Li 			for (size_t i = 0; i < n; i += 8) {
116*5f32b710SXin Li 				const psimd_f32x2 data =
117*5f32b710SXin Li 					fp16_alt_to_fp32x2_psimd(
118*5f32b710SXin Li 						psimd_load_u16(&input[i]));
119*5f32b710SXin Li 				psimd_store_f32(&output[i], data.lo);
120*5f32b710SXin Li 				psimd_store_f32(&output[i + 4], data.hi);
121*5f32b710SXin Li 			}
122*5f32b710SXin Li 
123*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
124*5f32b710SXin Li 		}
125*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
126*5f32b710SXin Li 	}
127*5f32b710SXin Li 	BENCHMARK(fp16_alt_to_fp32x2_psimd)->RangeMultiplier(2)->Range(1<<10, 64<<20);
128*5f32b710SXin Li #endif
129*5f32b710SXin Li 
130*5f32b710SXin Li #if defined(__ARM_NEON_FP) && (__ARM_NEON_FP & 0x2) || defined(__aarch64__)
hardware_vcvt_f32_f16(benchmark::State & state)131*5f32b710SXin Li 	static void hardware_vcvt_f32_f16(benchmark::State& state) {
132*5f32b710SXin Li 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
133*5f32b710SXin Li 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
134*5f32b710SXin Li 
135*5f32b710SXin Li 		std::vector<uint16_t> fp16(state.range(0));
136*5f32b710SXin Li 		std::vector<float> fp32(state.range(0));
137*5f32b710SXin Li 		std::generate(fp16.begin(), fp16.end(),
138*5f32b710SXin Li 			[&rng]{ return fp16_ieee_from_fp32_value(rng()); });
139*5f32b710SXin Li 
140*5f32b710SXin Li 		while (state.KeepRunning()) {
141*5f32b710SXin Li 			uint16_t* input = fp16.data();
142*5f32b710SXin Li 			benchmark::DoNotOptimize(input);
143*5f32b710SXin Li 
144*5f32b710SXin Li 			float* output = fp32.data();
145*5f32b710SXin Li 			const size_t n = state.range(0);
146*5f32b710SXin Li 			#if defined(__aarch64__)
147*5f32b710SXin Li 				const unsigned int fpcr = __builtin_aarch64_get_fpcr();
148*5f32b710SXin Li 				/* Disable flush-to-zero (bit 24) and enable Alternative FP16 format (bit 26) */
149*5f32b710SXin Li 				__builtin_aarch64_set_fpcr((fpcr & 0xFEFFFFFFu) | 0x08000000u);
150*5f32b710SXin Li 			#else
151*5f32b710SXin Li 				unsigned int fpscr;
152*5f32b710SXin Li 				__asm__ __volatile__ ("VMRS %[fpscr], fpscr" : [fpscr] "=r" (fpscr));
153*5f32b710SXin Li 				/* Disable flush-to-zero (bit 24) and enable Alternative FP16 format (bit 26) */
154*5f32b710SXin Li 				__asm__ __volatile__ ("VMSR fpscr, %[fpscr]" :
155*5f32b710SXin Li 					: [fpscr] "r" ((fpscr & 0xFEFFFFFFu) | 0x08000000u));
156*5f32b710SXin Li 			#endif
157*5f32b710SXin Li 			for (size_t i = 0; i < n; i += 4) {
158*5f32b710SXin Li 				vst1q_f32(&output[i],
159*5f32b710SXin Li 					vcvt_f32_f16(
160*5f32b710SXin Li 						(float16x4_t) vld1_u16(&input[i])));
161*5f32b710SXin Li 			}
162*5f32b710SXin Li 			#if defined(__aarch64__)
163*5f32b710SXin Li 				__builtin_aarch64_set_fpcr(fpcr);
164*5f32b710SXin Li 			#else
165*5f32b710SXin Li 				__asm__ __volatile__ ("VMSR fpscr, %[fpscr]" :: [fpscr] "r" (fpscr));
166*5f32b710SXin Li 			#endif
167*5f32b710SXin Li 
168*5f32b710SXin Li 			benchmark::DoNotOptimize(output);
169*5f32b710SXin Li 		}
170*5f32b710SXin Li 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
171*5f32b710SXin Li 	}
172*5f32b710SXin Li 	BENCHMARK(hardware_vcvt_f32_f16)->RangeMultiplier(2)->Range(1<<10, 64<<20);
173*5f32b710SXin Li #endif
174*5f32b710SXin Li 
175*5f32b710SXin Li BENCHMARK_MAIN();
176