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(__i386__) || defined(__x86_64__)) && defined(__F16C__)
15 	#include <immintrin.h>
16 #endif
17 
18 #if defined(__ARM_NEON__) || defined(__aarch64__)
19 	#include <arm_neon.h>
20 #endif
21 
22 
fp16_alt_from_fp32_value(benchmark::State & state)23 static void fp16_alt_from_fp32_value(benchmark::State& state) {
24 	const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
25 	auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
26 
27 	std::vector<float> fp32(state.range(0));
28 	std::vector<uint16_t> fp16(state.range(0));
29 	std::generate(fp32.begin(), fp32.end(), std::ref(rng));
30 
31 	while (state.KeepRunning()) {
32 		float* input = fp32.data();
33 		benchmark::DoNotOptimize(input);
34 
35 		uint16_t* output = fp16.data();
36 		const size_t n = state.range(0);
37 		for (size_t i = 0; i < n; i++) {
38 			output[i] = fp16_alt_from_fp32_value(input[i]);
39 		}
40 
41 		benchmark::DoNotOptimize(output);
42 	}
43 	state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
44 }
45 BENCHMARK(fp16_alt_from_fp32_value)->RangeMultiplier(2)->Range(1<<10, 64<<20);
46 
47 #if defined(__ARM_NEON_FP) && (__ARM_NEON_FP & 0x2) || defined(__aarch64__)
hardware_vcvt_f16_f32(benchmark::State & state)48 	static void hardware_vcvt_f16_f32(benchmark::State& state) {
49 		const uint_fast32_t seed = std::chrono::system_clock::now().time_since_epoch().count();
50 		auto rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::mt19937(seed));
51 
52 		std::vector<float> fp32(state.range(0));
53 		std::vector<uint16_t> fp16(state.range(0));
54 		std::generate(fp32.begin(), fp32.end(), std::ref(rng));
55 
56 		while (state.KeepRunning()) {
57 			float* input = fp32.data();
58 			benchmark::DoNotOptimize(input);
59 
60 			uint16_t* output = fp16.data();
61 			const size_t n = state.range(0);
62 			#if defined(__aarch64__)
63 				const unsigned int fpcr = __builtin_aarch64_get_fpcr();
64 				/* Disable flush-to-zero (bit 24) and enable Alternative FP16 format (bit 26) */
65 				__builtin_aarch64_set_fpcr((fpcr & 0xFEFFFFFFu) | 0x08000000u);
66 			#else
67 				unsigned int fpscr;
68 				__asm__ __volatile__ ("VMRS %[fpscr], fpscr" : [fpscr] "=r" (fpscr));
69 				/* Disable flush-to-zero (bit 24) and enable Alternative FP16 format (bit 26) */
70 				__asm__ __volatile__ ("VMSR fpscr, %[fpscr]" :
71 					: [fpscr] "r" ((fpscr & 0xFEFFFFFFu) | 0x08000000u));
72 			#endif
73 			for (size_t i = 0; i < n; i += 4) {
74 				vst1_u16(&output[i],
75 					(uint16x4_t) vcvt_f16_f32(
76 						vld1q_f32(&input[i])));
77 			}
78 			#if defined(__aarch64__)
79 				__builtin_aarch64_set_fpcr(fpcr);
80 			#else
81 				__asm__ __volatile__ ("VMSR fpscr, %[fpscr]" :: [fpscr] "r" (fpscr));
82 			#endif
83 
84 			benchmark::DoNotOptimize(output);
85 		}
86 		state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
87 	}
88 	BENCHMARK(hardware_vcvt_f16_f32)->RangeMultiplier(2)->Range(1<<10, 64<<20);
89 #endif
90 
91 BENCHMARK_MAIN();
92