1 | /* Skeleton for benchmark programs. |
2 | Copyright (C) 2013-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <string.h> |
20 | #include <stdint.h> |
21 | #include <stdbool.h> |
22 | #include <stdio.h> |
23 | #include <time.h> |
24 | #include <inttypes.h> |
25 | #include "bench-timing.h" |
26 | #include "json-lib.h" |
27 | #include "bench-util.h" |
28 | |
29 | #include "bench-util.c" |
30 | |
31 | #define TIMESPEC_AFTER(a, b) \ |
32 | (((a).tv_sec == (b).tv_sec) \ |
33 | ? ((a).tv_nsec > (b).tv_nsec) \ |
34 | : ((a).tv_sec > (b).tv_sec)) |
35 | int |
36 | main (int argc, char **argv) |
37 | { |
38 | unsigned long i, k; |
39 | struct timespec runtime; |
40 | timing_t start, end; |
41 | bool detailed = false; |
42 | json_ctx_t json_ctx; |
43 | |
44 | if (argc == 2 && !strcmp (argv[1], "-d" )) |
45 | detailed = true; |
46 | |
47 | bench_start (); |
48 | |
49 | memset (&runtime, 0, sizeof (runtime)); |
50 | |
51 | unsigned long iters = 1000; |
52 | |
53 | #ifdef BENCH_INIT |
54 | BENCH_INIT (); |
55 | #endif |
56 | |
57 | json_init (ctx: &json_ctx, indent_level: 2, stdout); |
58 | |
59 | /* Begin function. */ |
60 | json_attr_object_begin (ctx: &json_ctx, name: FUNCNAME); |
61 | |
62 | for (int v = 0; v < NUM_VARIANTS; v++) |
63 | { |
64 | /* Run for approximately DURATION seconds. */ |
65 | clock_gettime (CLOCK_MONOTONIC_RAW, tp: &runtime); |
66 | runtime.tv_sec += DURATION; |
67 | |
68 | bool is_bench = strncmp (VARIANT (v), "workload-" , 9) == 0; |
69 | double d_total_i = 0; |
70 | timing_t total = 0, max = 0, min = 0x7fffffffffffffff; |
71 | timing_t throughput = 0, latency = 0; |
72 | int64_t c = 0; |
73 | uint64_t cur; |
74 | BENCH_VARS; |
75 | while (1) |
76 | { |
77 | if (is_bench) |
78 | { |
79 | /* Benchmark a real trace of calls - all samples are iterated |
80 | over once before repeating. This models actual use more |
81 | accurately than repeating the same sample many times. */ |
82 | TIMING_NOW (start); |
83 | for (k = 0; k < iters; k++) |
84 | for (i = 0; i < NUM_SAMPLES (v); i++) |
85 | BENCH_FUNC (v, i); |
86 | TIMING_NOW (end); |
87 | TIMING_DIFF (cur, start, end); |
88 | TIMING_ACCUM (throughput, cur); |
89 | |
90 | TIMING_NOW (start); |
91 | for (k = 0; k < iters; k++) |
92 | for (i = 0; i < NUM_SAMPLES (v); i++) |
93 | BENCH_FUNC_LAT (v, i); |
94 | TIMING_NOW (end); |
95 | TIMING_DIFF (cur, start, end); |
96 | TIMING_ACCUM (latency, cur); |
97 | |
98 | d_total_i += iters * NUM_SAMPLES (v); |
99 | } |
100 | else |
101 | for (i = 0; i < NUM_SAMPLES (v); i++) |
102 | { |
103 | TIMING_NOW (start); |
104 | for (k = 0; k < iters; k++) |
105 | BENCH_FUNC (v, i); |
106 | TIMING_NOW (end); |
107 | |
108 | TIMING_DIFF (cur, start, end); |
109 | |
110 | if (cur > max) |
111 | max = cur; |
112 | |
113 | if (cur < min) |
114 | min = cur; |
115 | |
116 | TIMING_ACCUM (total, cur); |
117 | /* Accumulate timings for the value. In the end we will divide |
118 | by the total iterations. */ |
119 | RESULT_ACCUM (cur, v, i, c * iters, (c + 1) * iters); |
120 | |
121 | d_total_i += iters; |
122 | } |
123 | c++; |
124 | struct timespec curtime; |
125 | |
126 | memset (&curtime, 0, sizeof (curtime)); |
127 | clock_gettime (CLOCK_MONOTONIC_RAW, tp: &curtime); |
128 | if (TIMESPEC_AFTER (curtime, runtime)) |
129 | goto done; |
130 | } |
131 | |
132 | double d_total_s; |
133 | double d_iters; |
134 | |
135 | done: |
136 | d_total_s = total; |
137 | d_iters = iters; |
138 | |
139 | /* Begin variant. */ |
140 | json_attr_object_begin (ctx: &json_ctx, name: VARIANT (v)); |
141 | |
142 | if (is_bench) |
143 | { |
144 | json_attr_double (ctx: &json_ctx, name: "duration" , d: throughput + latency); |
145 | json_attr_double (ctx: &json_ctx, name: "iterations" , d: 2 * d_total_i); |
146 | json_attr_double (ctx: &json_ctx, name: "reciprocal-throughput" , |
147 | d: throughput / d_total_i); |
148 | json_attr_double (ctx: &json_ctx, name: "latency" , d: latency / d_total_i); |
149 | json_attr_double (ctx: &json_ctx, name: "max-throughput" , |
150 | d: d_total_i / throughput * 1000000000.0); |
151 | json_attr_double (ctx: &json_ctx, name: "min-throughput" , |
152 | d: d_total_i / latency * 1000000000.0); |
153 | } |
154 | else |
155 | { |
156 | json_attr_double (ctx: &json_ctx, name: "duration" , d: d_total_s); |
157 | json_attr_double (ctx: &json_ctx, name: "iterations" , d: d_total_i); |
158 | json_attr_double (ctx: &json_ctx, name: "max" , d: max / d_iters); |
159 | json_attr_double (ctx: &json_ctx, name: "min" , d: min / d_iters); |
160 | json_attr_double (ctx: &json_ctx, name: "mean" , d: d_total_s / d_total_i); |
161 | } |
162 | |
163 | if (detailed && !is_bench) |
164 | { |
165 | json_array_begin (ctx: &json_ctx, name: "timings" ); |
166 | |
167 | for (int i = 0; i < NUM_SAMPLES (v); i++) |
168 | json_element_double (ctx: &json_ctx, d: RESULT (v, i)); |
169 | |
170 | json_array_end (ctx: &json_ctx); |
171 | } |
172 | |
173 | /* End variant. */ |
174 | json_attr_object_end (ctx: &json_ctx); |
175 | } |
176 | |
177 | /* End function. */ |
178 | json_attr_object_end (ctx: &json_ctx); |
179 | |
180 | return 0; |
181 | } |
182 | |