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