xref: /trueos/lib/libdispatch/src/benchmark.c (revision bf5f91cb28c5878845eb00fbf329c042f6c643c9)
1 /*
2  * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 
21 #include "internal.h"
22 
23 struct __dispatch_benchmark_data_s {
24 #if HAVE_MACH_ABSOLUTE_TIME
25 	mach_timebase_info_data_t tbi;
26 #endif
27 	uint64_t loop_cost;
28 	void (*func)(void *);
29 	void *ctxt;
30 	size_t count;
31 };
32 
33 static void
_dispatch_benchmark_init(void * context)34 _dispatch_benchmark_init(void *context)
35 {
36 	struct __dispatch_benchmark_data_s *bdata = context;
37 	// try and simulate performance of real benchmark as much as possible
38 	// keep 'f', 'c' and 'cnt' in registers
39 	register void (*f)(void *) = bdata->func;
40 	register void *c = bdata->ctxt;
41 	register size_t cnt = bdata->count;
42 	size_t i = 0;
43 	uint64_t start, delta;
44 #if defined(__LP64__)
45 	__uint128_t lcost;
46 #else
47 	long double lcost;
48 #endif
49 #if HAVE_MACH_ABSOLUTE_TIME
50 	kern_return_t kr;
51 
52 	kr = mach_timebase_info(&bdata->tbi);
53 	dispatch_assert_zero(kr);
54 #endif
55 
56 	start = _dispatch_absolute_time();
57 	do {
58 		i++;
59 		f(c);
60 	} while (i < cnt);
61 	delta = _dispatch_absolute_time() - start;
62 
63 	lcost = delta;
64 #if HAVE_MACH_ABSOLUTE_TIME
65 	lcost *= bdata->tbi.numer;
66 	lcost /= bdata->tbi.denom;
67 #endif
68 	lcost /= cnt;
69 
70 	bdata->loop_cost = lcost > UINT64_MAX ? UINT64_MAX : (uint64_t)lcost;
71 }
72 
73 #ifdef __BLOCKS__
74 uint64_t
75 dispatch_benchmark(size_t count, void (^block)(void))
76 {
77 	return dispatch_benchmark_f(count, block, _dispatch_Block_invoke(block));
78 }
79 #endif
80 
81 static void
_dispatch_benchmark_dummy_function(void * ctxt DISPATCH_UNUSED)82 _dispatch_benchmark_dummy_function(void *ctxt DISPATCH_UNUSED)
83 {
84 }
85 
86 uint64_t
dispatch_benchmark_f(size_t count,register void * ctxt,register void (* func)(void *))87 dispatch_benchmark_f(size_t count, register void *ctxt,
88 		register void (*func)(void *))
89 {
90 	static struct __dispatch_benchmark_data_s bdata = {
91 		.func = _dispatch_benchmark_dummy_function,
92 		.count = 10000000ul, // ten million
93 	};
94 	static dispatch_once_t pred;
95 	uint64_t ns, start, delta;
96 #if defined(__LP64__)
97 	__uint128_t conversion, big_denom;
98 #else
99 	long double conversion, big_denom;
100 #endif
101 	size_t i = 0;
102 
103 	dispatch_once_f(&pred, &bdata, _dispatch_benchmark_init);
104 
105 	if (slowpath(count == 0)) {
106 		return 0;
107 	}
108 
109 	start = _dispatch_absolute_time();
110 	do {
111 		i++;
112 		func(ctxt);
113 	} while (i < count);
114 	delta = _dispatch_absolute_time() - start;
115 
116 	conversion = delta;
117 #if HAVE_MACH_ABSOLUTE_TIME
118 	conversion *= bdata.tbi.numer;
119 	big_denom = bdata.tbi.denom;
120 #else
121 	big_denom = delta;
122 #endif
123 	big_denom *= count;
124 	conversion /= big_denom;
125 	ns = conversion > UINT64_MAX ? UINT64_MAX : (uint64_t)conversion;
126 
127 	return ns - bdata.loop_cost;
128 }
129