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
23struct __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
33static void
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__
74uint64_t
75dispatch_benchmark(size_t count, void (^block)(void))
76{
77	return dispatch_benchmark_f(count, block, _dispatch_Block_invoke(block));
78}
79#endif
80
81static void
82_dispatch_benchmark_dummy_function(void *ctxt DISPATCH_UNUSED)
83{
84}
85
86uint64_t
87dispatch_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