aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/benchmark.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/benchmark.c')
-rw-r--r--src/lib/util/benchmark.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/lib/util/benchmark.c b/src/lib/util/benchmark.c
new file mode 100644
index 000000000..c9fc8842e
--- /dev/null
+++ b/src/lib/util/benchmark.c
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file util/benchmark.c
23 * @brief benchmarking for various operations
24 * @author Florian Dold <flo@dold.me>
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "benchmark.h"
31#include <pthread.h>
32#include <sys/syscall.h>
33
34/**
35 * Thread-local storage key for the benchmark data.
36 */
37static pthread_key_t key;
38
39/**
40 * One-time initialization marker for key.
41 */
42static pthread_once_t key_once = PTHREAD_ONCE_INIT;
43
44
45/**
46 * Write benchmark data to a file.
47 *
48 * @param bd the benchmark data
49 */
50static void
51write_benchmark_data (struct BenchmarkData *bd)
52{
53 struct GNUNET_DISK_FileHandle *fh;
54 pid_t pid = getpid ();
55 pid_t tid = syscall (SYS_gettid);
56 char *benchmark_dir;
57 char *s;
58
59 benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
60
61 if (NULL == benchmark_dir)
62 return;
63
64 if (GNUNET_OK != GNUNET_DISK_directory_create (benchmark_dir))
65 {
66 GNUNET_break (0);
67 return;
68 }
69
70 GNUNET_asprintf (&s, "%s/gnunet-benchmark-ops-%s-%llu-%llu.txt",
71 benchmark_dir,
72 (pid == tid) ? "main" : "thread",
73 (unsigned long long) pid,
74 (unsigned long long) tid);
75
76 fh = GNUNET_DISK_file_open (s,
77 (GNUNET_DISK_OPEN_WRITE
78 | GNUNET_DISK_OPEN_TRUNCATE
79 | GNUNET_DISK_OPEN_CREATE),
80 (GNUNET_DISK_PERM_USER_READ
81 | GNUNET_DISK_PERM_USER_WRITE));
82 GNUNET_assert (NULL != fh);
83 GNUNET_free (s);
84
85#define WRITE_BENCHMARK_OP(opname) do { \
86 GNUNET_asprintf (&s, "op " #opname " count %llu time_us %llu\n", \
87 (unsigned long long) bd->opname ## _count, \
88 (unsigned long long) bd->opname ## _time.rel_value_us); \
89 GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, \
90 strlen ( \
91 s))); \
92 GNUNET_free (s); \
93} while (0)
94
95 WRITE_BENCHMARK_OP (ecc_ecdh);
96 WRITE_BENCHMARK_OP (ecdh_eddsa);
97 WRITE_BENCHMARK_OP (ecdhe_key_create);
98 WRITE_BENCHMARK_OP (ecdhe_key_get_public);
99 WRITE_BENCHMARK_OP (ecdsa_ecdh);
100 WRITE_BENCHMARK_OP (ecdsa_key_create);
101 WRITE_BENCHMARK_OP (ecdsa_key_get_public);
102 WRITE_BENCHMARK_OP (ecdsa_sign);
103 WRITE_BENCHMARK_OP (ecdsa_verify);
104 WRITE_BENCHMARK_OP (eddsa_ecdh);
105 WRITE_BENCHMARK_OP (eddsa_key_create);
106 WRITE_BENCHMARK_OP (eddsa_key_get_public);
107 WRITE_BENCHMARK_OP (eddsa_sign);
108 WRITE_BENCHMARK_OP (eddsa_verify);
109 WRITE_BENCHMARK_OP (hash);
110 WRITE_BENCHMARK_OP (hash_context_finish);
111 WRITE_BENCHMARK_OP (hash_context_read);
112 WRITE_BENCHMARK_OP (hash_context_start);
113 WRITE_BENCHMARK_OP (hkdf);
114 WRITE_BENCHMARK_OP (rsa_blind);
115 WRITE_BENCHMARK_OP (rsa_private_key_create);
116 WRITE_BENCHMARK_OP (rsa_private_key_get_public);
117 WRITE_BENCHMARK_OP (rsa_sign_blinded);
118 WRITE_BENCHMARK_OP (rsa_unblind);
119 WRITE_BENCHMARK_OP (rsa_verify);
120
121#undef WRITE_BENCHMARK_OP
122
123 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
124
125 GNUNET_asprintf (&s, "%s/gnunet-benchmark-urls-%s-%llu-%llu.txt",
126 benchmark_dir,
127 (pid == tid) ? "main" : "thread",
128 (unsigned long long) pid,
129 (unsigned long long) tid);
130
131 fh = GNUNET_DISK_file_open (s,
132 (GNUNET_DISK_OPEN_WRITE
133 | GNUNET_DISK_OPEN_TRUNCATE
134 | GNUNET_DISK_OPEN_CREATE),
135 (GNUNET_DISK_PERM_USER_READ
136 | GNUNET_DISK_PERM_USER_WRITE));
137 GNUNET_assert (NULL != fh);
138 GNUNET_free (s);
139
140 for (unsigned int i = 0; i < bd->urd_len; i++)
141 {
142 struct UrlRequestData *urd = &bd->urd[i];
143 GNUNET_asprintf (&s,
144 "url %s status %u count %llu time_us %llu time_us_max %llu bytes_sent %llu bytes_received %llu\n",
145 urd->request_url,
146 urd->status,
147 (unsigned long long) urd->count,
148 (unsigned long long) urd->time.rel_value_us,
149 (unsigned long long) urd->time_max.rel_value_us,
150 (unsigned long long) urd->bytes_sent,
151 (unsigned long long) urd->bytes_received);
152 GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s,
153 strlen (
154 s)));
155 GNUNET_free (s);
156 }
157
158 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
159}
160
161
162/**
163 * Called when the main thread exits and benchmark data for it was created.
164 */
165static void
166main_thread_destructor ()
167{
168 struct BenchmarkData *bd;
169
170 bd = pthread_getspecific (key);
171 if (NULL != bd)
172 write_benchmark_data (bd);
173}
174
175
176/**
177 * Called when a thread exits and benchmark data for it was created.
178 *
179 * @param cls closure
180 */
181static void
182thread_destructor (void *cls)
183{
184 struct BenchmarkData *bd = cls;
185
186 // main thread will be handled by atexit
187 if (getpid () == (pid_t) syscall (SYS_gettid))
188 return;
189
190 GNUNET_assert (NULL != bd);
191 write_benchmark_data (bd);
192}
193
194
195/**
196 * Initialize the thread-local variable key for benchmark data.
197 */
198static void
199make_key ()
200{
201 (void) pthread_key_create (&key, &thread_destructor);
202}
203
204
205/**
206 * Acquire the benchmark data for the current thread, allocate if necessary.
207 * Installs handler to collect the benchmark data on thread termination.
208 *
209 * @return benchmark data for the current thread
210 */
211struct BenchmarkData *
212get_benchmark_data (void)
213{
214 struct BenchmarkData *bd;
215
216 (void) pthread_once (&key_once, &make_key);
217
218 if (NULL == (bd = pthread_getspecific (key)))
219 {
220 bd = GNUNET_new (struct BenchmarkData);
221 (void) pthread_setspecific (key, bd);
222 if (getpid () == (pid_t) syscall (SYS_gettid))
223 {
224 // We're the main thread!
225 atexit (main_thread_destructor);
226 }
227 }
228 return bd;
229}
230
231
232/**
233 * Get benchmark data for a URL. If the URL is too long, it's truncated
234 * before looking up the corresponding benchmark data.
235 *
236 * Statistics are bucketed by URL and status code.
237 *
238 * @param url url to get request data for
239 * @param status http status code
240 */
241struct UrlRequestData *
242get_url_benchmark_data (char *url, unsigned int status)
243{
244 char trunc[MAX_BENCHMARK_URL_LEN];
245 struct BenchmarkData *bd;
246
247 if (NULL == url)
248 {
249 /* Should not happen unless curl barfs */
250 GNUNET_break (0);
251 url = "<empty>";
252 }
253
254 memcpy (trunc, url, MAX_BENCHMARK_URL_LEN);
255 trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
256
257 /* We're not interested in what's after the query string */
258 for (size_t i = 0; i < strlen (trunc); i++)
259 {
260 if (trunc[i] == '?')
261 {
262 trunc[i] = 0;
263 break;
264 }
265 }
266
267 bd = get_benchmark_data ();
268
269 GNUNET_assert (bd->urd_len <= bd->urd_capacity);
270
271 for (unsigned int i = 0; i < bd->urd_len; i++)
272 {
273 if ((0 == strcmp (trunc, bd->urd[i].request_url)) &&
274 (bd->urd[i].status == status))
275 return &bd->urd[i];
276 }
277
278 {
279 struct UrlRequestData urd = { 0 };
280
281 memcpy (&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
282 urd.status = status;
283
284 if (bd->urd_len == bd->urd_capacity)
285 {
286 bd->urd_capacity = 2 * (bd->urd_capacity + 1);
287 bd->urd = GNUNET_realloc (bd->urd, bd->urd_capacity * sizeof(struct
288 UrlRequestData));
289 }
290
291 bd->urd[bd->urd_len++] = urd;
292 return &bd->urd[bd->urd_len - 1];
293 }
294}