aboutsummaryrefslogtreecommitdiff
path: root/src/testcurl/perf_get.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testcurl/perf_get.c')
-rw-r--r--src/testcurl/perf_get.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/src/testcurl/perf_get.c b/src/testcurl/perf_get.c
new file mode 100644
index 00000000..a316ed1e
--- /dev/null
+++ b/src/testcurl/perf_get.c
@@ -0,0 +1,491 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007, 2009, 2011 Christian Grothoff
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file perf_get.c
23 * @brief benchmark simple GET operations (sequential access).
24 * Note that we run libcurl in the same process at the
25 * same time, so the execution time given is the combined
26 * time for both MHD and libcurl; it is quite possible
27 * that more time is spend with libcurl than with MHD,
28 * so the performance scores calculated with this code
29 * should NOT be used to compare with other HTTP servers
30 * (since MHD is actually better); only the relative
31 * scores between MHD versions are meaningful.
32 * Furthermore, this code ONLY tests MHD processing
33 * a single request at a time. This is again
34 * not universally meaningful (i.e. when comparing
35 * multithreaded vs. single-threaded or select/poll).
36 * @author Christian Grothoff
37 */
38
39#include "MHD_config.h"
40#include "platform.h"
41#include <curl/curl.h>
42#include <microhttpd.h>
43#include <stdlib.h>
44#include <string.h>
45#include <time.h>
46#include "gauger.h"
47
48#ifndef WINDOWS
49#include <unistd.h>
50#include <sys/socket.h>
51#endif
52
53/**
54 * How many rounds of operations do we do for each
55 * test?
56 */
57#define ROUNDS 500
58
59/**
60 * Do we use HTTP 1.1?
61 */
62static int oneone;
63
64/**
65 * Response to return (re-used).
66 */
67static struct MHD_Response *response;
68
69/**
70 * Time this round was started.
71 */
72static unsigned long long start_time;
73
74
75/**
76 * Get the current timestamp
77 *
78 * @return current time in ms
79 */
80static unsigned long long
81now ()
82{
83 struct timeval tv;
84
85 GETTIMEOFDAY (&tv, NULL);
86 return (((unsigned long long) tv.tv_sec * 1000LL) +
87 ((unsigned long long) tv.tv_usec / 1000LL));
88}
89
90
91/**
92 * Start the timer.
93 */
94static void
95start_timer()
96{
97 start_time = now ();
98}
99
100
101/**
102 * Stop the timer and report performance
103 *
104 * @param desc description of the threading mode we used
105 */
106static void
107stop (const char *desc)
108{
109 double rps = ((double) (ROUNDS * 1000)) / ((double) (now() - start_time));
110
111 fprintf (stderr,
112 "Sequential GETs using %s: %f %s\n",
113 desc,
114 rps,
115 "requests/s");
116 GAUGER (desc,
117 "Sequential GETs",
118 rps,
119 "requests/s");
120}
121
122
123struct CBC
124{
125 char *buf;
126 size_t pos;
127 size_t size;
128};
129
130
131static size_t
132copyBuffer (void *ptr,
133 size_t size, size_t nmemb,
134 void *ctx)
135{
136 struct CBC *cbc = ctx;
137
138 if (cbc->pos + size * nmemb > cbc->size)
139 return 0; /* overflow */
140 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
141 cbc->pos += size * nmemb;
142 return size * nmemb;
143}
144
145static int
146ahc_echo (void *cls,
147 struct MHD_Connection *connection,
148 const char *url,
149 const char *method,
150 const char *version,
151 const char *upload_data, size_t *upload_data_size,
152 void **unused)
153{
154 static int ptr;
155 const char *me = cls;
156 int ret;
157
158 if (0 != strcmp (me, method))
159 return MHD_NO; /* unexpected method */
160 if (&ptr != *unused)
161 {
162 *unused = &ptr;
163 return MHD_YES;
164 }
165 *unused = NULL;
166 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
167 if (ret == MHD_NO)
168 abort ();
169 return ret;
170}
171
172
173static int
174testInternalGet (int poll_flag)
175{
176 struct MHD_Daemon *d;
177 CURL *c;
178 char buf[2048];
179 struct CBC cbc;
180 CURLcode errornum;
181 unsigned int i;
182
183 cbc.buf = buf;
184 cbc.size = 2048;
185 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
186 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
187 if (d == NULL)
188 return 1;
189 start_timer ();
190 for (i=0;i<ROUNDS;i++)
191 {
192 cbc.pos = 0;
193 c = curl_easy_init ();
194 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world");
195 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
196 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
197 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
198 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
199 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
200 if (oneone)
201 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
202 else
203 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
204 /* NOTE: use of CONNECTTIMEOUT without also
205 setting NOSIGNAL results in really weird
206 crashes on my system!*/
207 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
208 if (CURLE_OK != (errornum = curl_easy_perform (c)))
209 {
210 fprintf (stderr,
211 "curl_easy_perform failed: `%s'\n",
212 curl_easy_strerror (errornum));
213 curl_easy_cleanup (c);
214 MHD_stop_daemon (d);
215 return 2;
216 }
217 curl_easy_cleanup (c);
218 }
219 stop (poll_flag ? "internal poll" : "internal select");
220 MHD_stop_daemon (d);
221 if (cbc.pos != strlen ("/hello_world"))
222 return 4;
223 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
224 return 8;
225 return 0;
226}
227
228
229static int
230testMultithreadedGet (int poll_flag)
231{
232 struct MHD_Daemon *d;
233 CURL *c;
234 char buf[2048];
235 struct CBC cbc;
236 CURLcode errornum;
237 unsigned int i;
238
239 cbc.buf = buf;
240 cbc.size = 2048;
241 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | poll_flag,
242 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
243 if (d == NULL)
244 return 16;
245 start_timer ();
246 for (i=0;i<ROUNDS;i++)
247 {
248 cbc.pos = 0;
249 c = curl_easy_init ();
250 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world");
251 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
252 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
253 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
254 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
255 if (oneone)
256 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
257 else
258 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
259 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
260 /* NOTE: use of CONNECTTIMEOUT without also
261 setting NOSIGNAL results in really weird
262 crashes on my system! */
263 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
264 if (CURLE_OK != (errornum = curl_easy_perform (c)))
265 {
266 fprintf (stderr,
267 "curl_easy_perform failed: `%s'\n",
268 curl_easy_strerror (errornum));
269 curl_easy_cleanup (c);
270 MHD_stop_daemon (d);
271 return 32;
272 }
273 curl_easy_cleanup (c);
274 }
275 stop (poll_flag ? "thread with poll" : "thread with select");
276 MHD_stop_daemon (d);
277 if (cbc.pos != strlen ("/hello_world"))
278 return 64;
279 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
280 return 128;
281 return 0;
282}
283
284static int
285testMultithreadedPoolGet (int poll_flag)
286{
287 struct MHD_Daemon *d;
288 CURL *c;
289 char buf[2048];
290 struct CBC cbc;
291 CURLcode errornum;
292 unsigned int i;
293
294 cbc.buf = buf;
295 cbc.size = 2048;
296 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
297 1081, NULL, NULL, &ahc_echo, "GET",
298 MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END);
299 if (d == NULL)
300 return 16;
301 start_timer ();
302 for (i=0;i<ROUNDS;i++)
303 {
304 cbc.pos = 0;
305 c = curl_easy_init ();
306 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world");
307 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
308 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
309 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
310 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
311 if (oneone)
312 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
313 else
314 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
315 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
316 /* NOTE: use of CONNECTTIMEOUT without also
317 setting NOSIGNAL results in really weird
318 crashes on my system!*/
319 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
320 if (CURLE_OK != (errornum = curl_easy_perform (c)))
321 {
322 fprintf (stderr,
323 "curl_easy_perform failed: `%s'\n",
324 curl_easy_strerror (errornum));
325 curl_easy_cleanup (c);
326 MHD_stop_daemon (d);
327 return 32;
328 }
329 curl_easy_cleanup (c);
330 }
331 stop (poll_flag ? "thread pool with poll" : "thread pool with select");
332 MHD_stop_daemon (d);
333 if (cbc.pos != strlen ("/hello_world"))
334 return 64;
335 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
336 return 128;
337 return 0;
338}
339
340static int
341testExternalGet ()
342{
343 struct MHD_Daemon *d;
344 CURL *c;
345 char buf[2048];
346 struct CBC cbc;
347 CURLM *multi;
348 CURLMcode mret;
349 fd_set rs;
350 fd_set ws;
351 fd_set es;
352 int max;
353 int running;
354 struct CURLMsg *msg;
355 time_t start;
356 struct timeval tv;
357 unsigned int i;
358
359 multi = NULL;
360 cbc.buf = buf;
361 cbc.size = 2048;
362 d = MHD_start_daemon (MHD_USE_DEBUG,
363 1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
364 if (d == NULL)
365 return 256;
366 start_timer ();
367 multi = curl_multi_init ();
368 if (multi == NULL)
369 {
370 MHD_stop_daemon (d);
371 return 512;
372 }
373 for (i=0;i<ROUNDS;i++)
374 {
375 cbc.pos = 0;
376 c = curl_easy_init ();
377 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world");
378 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
379 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
380 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
381 if (oneone)
382 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
383 else
384 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
385 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
386 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
387 /* NOTE: use of CONNECTTIMEOUT without also
388 setting NOSIGNAL results in really weird
389 crashes on my system! */
390 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
391 mret = curl_multi_add_handle (multi, c);
392 if (mret != CURLM_OK)
393 {
394 curl_multi_cleanup (multi);
395 curl_easy_cleanup (c);
396 MHD_stop_daemon (d);
397 return 1024;
398 }
399 start = time (NULL);
400 while ((time (NULL) - start < 5) && (c != NULL))
401 {
402 max = 0;
403 FD_ZERO (&rs);
404 FD_ZERO (&ws);
405 FD_ZERO (&es);
406 curl_multi_perform (multi, &running);
407 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
408 if (mret != CURLM_OK)
409 {
410 curl_multi_remove_handle (multi, c);
411 curl_multi_cleanup (multi);
412 curl_easy_cleanup (c);
413 MHD_stop_daemon (d);
414 return 2048;
415 }
416 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
417 {
418 curl_multi_remove_handle (multi, c);
419 curl_multi_cleanup (multi);
420 curl_easy_cleanup (c);
421 MHD_stop_daemon (d);
422 return 4096;
423 }
424 tv.tv_sec = 0;
425 tv.tv_usec = 1000;
426 select (max + 1, &rs, &ws, &es, &tv);
427 curl_multi_perform (multi, &running);
428 if (running == 0)
429 {
430 msg = curl_multi_info_read (multi, &running);
431 if (msg == NULL)
432 break;
433 if (msg->msg == CURLMSG_DONE)
434 {
435 if (msg->data.result != CURLE_OK)
436 printf ("%s failed at %s:%d: `%s'\n",
437 "curl_multi_perform",
438 __FILE__,
439 __LINE__, curl_easy_strerror (msg->data.result));
440 curl_multi_remove_handle (multi, c);
441 curl_easy_cleanup (c);
442 c = NULL;
443 }
444 }
445 MHD_run (d);
446 }
447 if (NULL != c)
448 {
449 curl_multi_remove_handle (multi, c);
450 curl_easy_cleanup (c);
451 fprintf (stderr, "Timeout!?\n");
452 }
453 }
454 stop ("external select");
455 if (multi != NULL)
456 {
457 curl_multi_cleanup (multi);
458 }
459 MHD_stop_daemon (d);
460 if (cbc.pos != strlen ("/hello_world"))
461 return 8192;
462 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
463 return 16384;
464 return 0;
465}
466
467
468int
469main (int argc, char *const *argv)
470{
471 unsigned int errorCount = 0;
472
473 oneone = NULL != strstr (argv[0], "11");
474 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
475 return 2;
476 response = MHD_create_response_from_buffer (strlen ("/hello_world"),
477 "/hello_world",
478 MHD_RESPMEM_MUST_COPY);
479 errorCount += testInternalGet (0);
480 errorCount += testMultithreadedGet (0);
481 errorCount += testMultithreadedPoolGet (0);
482 errorCount += testExternalGet ();
483 errorCount += testInternalGet (MHD_USE_POLL);
484 errorCount += testMultithreadedGet (MHD_USE_POLL);
485 errorCount += testMultithreadedPoolGet (MHD_USE_POLL);
486 MHD_destroy_response (response);
487 if (errorCount != 0)
488 fprintf (stderr, "Error (code: %u)\n", errorCount);
489 curl_global_cleanup ();
490 return errorCount != 0; /* 0 == pass */
491}