1 | /* arc4random benchmarks. |
2 | Copyright (C) 2022-2024 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 "bench-timing.h" |
20 | #include "bench-util.h" |
21 | #include "json-lib.h" |
22 | #include <array_length.h> |
23 | #include <intprops.h> |
24 | #include <signal.h> |
25 | #include <stdbool.h> |
26 | #include <stdio.h> |
27 | #include <stdlib.h> |
28 | #include <support/support.h> |
29 | #include <support/timespec.h> |
30 | #include <support/xthread.h> |
31 | |
32 | static volatile sig_atomic_t timer_finished; |
33 | |
34 | static void timer_callback (int unused) |
35 | { |
36 | timer_finished = 1; |
37 | } |
38 | |
39 | static timer_t timer; |
40 | |
41 | /* Run for approximately DURATION seconds, and it does not matter who |
42 | receive the signal (so not need to mask it on main thread). */ |
43 | static void |
44 | timer_start (void) |
45 | { |
46 | timer_finished = 0; |
47 | timer = support_create_timer (sec: DURATION, nsec: 0, false, callback: timer_callback); |
48 | } |
49 | static void |
50 | timer_stop (void) |
51 | { |
52 | support_delete_timer (timer); |
53 | } |
54 | |
55 | static const uint32_t sizes[] = { 0, 16, 32, 48, 64, 80, 96, 112, 128 }; |
56 | |
57 | static double |
58 | bench_throughput (void) |
59 | { |
60 | uint64_t n = 0; |
61 | |
62 | struct timespec start, end; |
63 | clock_gettime (CLOCK_MONOTONIC, tp: &start); |
64 | while (1) |
65 | { |
66 | DO_NOT_OPTIMIZE_OUT (arc4random ()); |
67 | n++; |
68 | |
69 | if (timer_finished == 1) |
70 | break; |
71 | } |
72 | clock_gettime (CLOCK_MONOTONIC, tp: &end); |
73 | struct timespec diff = timespec_sub (end, start); |
74 | |
75 | double total = (double) n * sizeof (uint32_t); |
76 | double duration = (double) diff.tv_sec |
77 | + (double) diff.tv_nsec / TIMESPEC_HZ; |
78 | |
79 | return total / duration; |
80 | } |
81 | |
82 | static double |
83 | bench_latency (void) |
84 | { |
85 | timing_t start, stop, cur; |
86 | const size_t iters = 1024; |
87 | |
88 | TIMING_NOW (start); |
89 | for (size_t i = 0; i < iters; i++) |
90 | DO_NOT_OPTIMIZE_OUT (arc4random ()); |
91 | TIMING_NOW (stop); |
92 | |
93 | TIMING_DIFF (cur, start, stop); |
94 | |
95 | return (double) (cur) / (double) iters; |
96 | } |
97 | |
98 | static double |
99 | bench_buf_throughput (size_t len) |
100 | { |
101 | uint8_t buf[len]; |
102 | uint64_t n = 0; |
103 | |
104 | struct timespec start, end; |
105 | clock_gettime (CLOCK_MONOTONIC, tp: &start); |
106 | while (1) |
107 | { |
108 | arc4random_buf (buf: buf, size: len); |
109 | n++; |
110 | |
111 | if (timer_finished == 1) |
112 | break; |
113 | } |
114 | clock_gettime (CLOCK_MONOTONIC, tp: &end); |
115 | struct timespec diff = timespec_sub (end, start); |
116 | |
117 | double total = (double) n * len; |
118 | double duration = (double) diff.tv_sec |
119 | + (double) diff.tv_nsec / TIMESPEC_HZ; |
120 | |
121 | return total / duration; |
122 | } |
123 | |
124 | static double |
125 | bench_buf_latency (size_t len) |
126 | { |
127 | timing_t start, stop, cur; |
128 | const size_t iters = 1024; |
129 | |
130 | uint8_t buf[len]; |
131 | |
132 | TIMING_NOW (start); |
133 | for (size_t i = 0; i < iters; i++) |
134 | arc4random_buf (buf: buf, size: len); |
135 | TIMING_NOW (stop); |
136 | |
137 | TIMING_DIFF (cur, start, stop); |
138 | |
139 | return (double) (cur) / (double) iters; |
140 | } |
141 | |
142 | static void |
143 | bench_singlethread (json_ctx_t *json_ctx) |
144 | { |
145 | json_element_object_begin (ctx: json_ctx); |
146 | |
147 | json_array_begin (ctx: json_ctx, name: "throughput" ); |
148 | for (int i = 0; i < array_length (sizes); i++) |
149 | { |
150 | timer_start (); |
151 | double r = sizes[i] == 0 |
152 | ? bench_throughput () : bench_buf_throughput (len: sizes[i]); |
153 | timer_stop (); |
154 | |
155 | json_element_double (ctx: json_ctx, d: r); |
156 | } |
157 | json_array_end (ctx: json_ctx); |
158 | |
159 | json_array_begin (ctx: json_ctx, name: "latency" ); |
160 | for (int i = 0; i < array_length (sizes); i++) |
161 | { |
162 | timer_start (); |
163 | double r = sizes[i] == 0 |
164 | ? bench_latency () : bench_buf_latency (len: sizes[i]); |
165 | timer_stop (); |
166 | |
167 | json_element_double (ctx: json_ctx, d: r); |
168 | } |
169 | json_array_end (ctx: json_ctx); |
170 | |
171 | json_element_object_end (ctx: json_ctx); |
172 | } |
173 | |
174 | static void |
175 | run_bench (json_ctx_t *json_ctx, const char *name, |
176 | char *const*fnames, size_t fnameslen, |
177 | void (*bench) (json_ctx_t *ctx)) |
178 | { |
179 | json_attr_object_begin (ctx: json_ctx, name); |
180 | json_array_begin (ctx: json_ctx, name: "functions" ); |
181 | for (int i = 0; i < fnameslen; i++) |
182 | json_element_string (ctx: json_ctx, s: fnames[i]); |
183 | json_array_end (ctx: json_ctx); |
184 | |
185 | json_array_begin (ctx: json_ctx, name: "results" ); |
186 | bench (json_ctx); |
187 | json_array_end (ctx: json_ctx); |
188 | json_attr_object_end (ctx: json_ctx); |
189 | } |
190 | |
191 | static int |
192 | do_test (void) |
193 | { |
194 | char *fnames[array_length (sizes)]; |
195 | for (int i = 0; i < array_length (sizes); i++) |
196 | if (sizes[i] == 0) |
197 | fnames[i] = xasprintf (format: "arc4random" ); |
198 | else |
199 | fnames[i] = xasprintf (format: "arc4random_buf(%u)" , sizes[i]); |
200 | |
201 | json_ctx_t json_ctx; |
202 | json_init (ctx: &json_ctx, indent_level: 0, stdout); |
203 | |
204 | json_document_begin (ctx: &json_ctx); |
205 | json_attr_string (ctx: &json_ctx, name: "timing_type" , TIMING_TYPE); |
206 | |
207 | run_bench (json_ctx: &json_ctx, name: "single-thread" , fnames, array_length (fnames), |
208 | bench: bench_singlethread); |
209 | |
210 | json_document_end (ctx: &json_ctx); |
211 | |
212 | for (int i = 0; i < array_length (sizes); i++) |
213 | free (ptr: fnames[i]); |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | #include <support/test-driver.c> |
219 | |