aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-07-07 08:25:55 +0000
committerChristian Grothoff <christian@grothoff.org>2011-07-07 08:25:55 +0000
commit3a801de937017ba906522940b52886085551e96c (patch)
tree2706c685a2c50ff1cc8825f84d8a9feb3c08a477
parent1e865f76cae12df11c931d0a6b7ef95412822303 (diff)
downloadlibmicrohttpd-3a801de937017ba906522940b52886085551e96c.tar.gz
libmicrohttpd-3a801de937017ba906522940b52886085551e96c.zip
adding perf measurements
-rw-r--r--ChangeLog6
-rw-r--r--configure.ac13
-rw-r--r--src/testcurl/Makefile.am23
-rw-r--r--src/testcurl/gauger.h86
-rw-r--r--src/testcurl/perf_get.c491
-rw-r--r--src/testcurl/perf_get_concurrent.c338
6 files changed, 955 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index b7c1f0fe..65a0fcd5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
1Thu Jul 7 10:24:20 CEST 2011
2 Adding performance measurements. -CG
3
4Thu Jun 23 14:21:13 CEST 2011
5 Releasing libmicrohttpd 0.9.12. -CG
6
1Wed Jun 22 14:32:23 CEST 2011 7Wed Jun 22 14:32:23 CEST 2011
2 Force closing connection if either the client asked it or 8 Force closing connection if either the client asked it or
3 if the response contains 'Connection: close' (so far, 9 if the response contains 'Connection: close' (so far,
diff --git a/configure.ac b/configure.ac
index aefca4ab..da077505 100644
--- a/configure.ac
+++ b/configure.ac
@@ -81,46 +81,55 @@ case "$host_os" in
81 AC_DEFINE_UNQUOTED(OSX,1,[This is an OS X system]) 81 AC_DEFINE_UNQUOTED(OSX,1,[This is an OS X system])
82 CFLAGS="-no-cpp-precomp -fno-common $CFLAGS" 82 CFLAGS="-no-cpp-precomp -fno-common $CFLAGS"
83 AM_CONDITIONAL(HAVE_GNU_LD, false) 83 AM_CONDITIONAL(HAVE_GNU_LD, false)
84 AM_CONDITIONAL(HAVE_W32, false)
84 ;; 85 ;;
85linux*) 86linux*)
86 AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system]) 87 AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system])
87 AM_CONDITIONAL(HAVE_GNU_LD, true) 88 AM_CONDITIONAL(HAVE_GNU_LD, true)
89 AM_CONDITIONAL(HAVE_W32, false)
88 ;; 90 ;;
89freebsd*) 91freebsd*)
90 AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system]) 92 AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system])
91 AC_DEFINE_UNQUOTED(FREEBSD,1,[This is a FreeBSD system]) 93 AC_DEFINE_UNQUOTED(FREEBSD,1,[This is a FreeBSD system])
92 AM_CONDITIONAL(HAVE_GNU_LD, true) 94 AM_CONDITIONAL(HAVE_GNU_LD, true)
95 AM_CONDITIONAL(HAVE_W32, false)
93 CFLAGS="-D_THREAD_SAFE $CFLAGS" 96 CFLAGS="-D_THREAD_SAFE $CFLAGS"
94 ;; 97 ;;
95openbsd*) 98openbsd*)
96 AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system]) 99 AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system])
97 AC_DEFINE_UNQUOTED(OPENBSD,1,[This is an OpenBSD system]) 100 AC_DEFINE_UNQUOTED(OPENBSD,1,[This is an OpenBSD system])
98 AM_CONDITIONAL(HAVE_GNU_LD, true) 101 AM_CONDITIONAL(HAVE_GNU_LD, true)
102 AM_CONDITIONAL(HAVE_W32, false)
99 ;; 103 ;;
100netbsd*) 104netbsd*)
101 AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system]) 105 AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system])
102 AC_DEFINE_UNQUOTED(NETBSD,1,[This is a NetBSD system]) 106 AC_DEFINE_UNQUOTED(NETBSD,1,[This is a NetBSD system])
103 AM_CONDITIONAL(HAVE_GNU_LD, true) 107 AM_CONDITIONAL(HAVE_GNU_LD, true)
108 AM_CONDITIONAL(HAVE_W32, false)
104 ;; 109 ;;
105*solaris*) 110*solaris*)
106 AC_DEFINE_UNQUOTED(SOLARIS,1,[This is a Solaris system]) 111 AC_DEFINE_UNQUOTED(SOLARIS,1,[This is a Solaris system])
107 AC_DEFINE_UNQUOTED(_REENTRANT,1,[Need with solaris or errno doesnt work]) 112 AC_DEFINE_UNQUOTED(_REENTRANT,1,[Need with solaris or errno doesnt work])
108 AM_CONDITIONAL(HAVE_GNU_LD, false) 113 AM_CONDITIONAL(HAVE_GNU_LD, false)
114 AM_CONDITIONAL(HAVE_W32, false)
109 LDFLAGS="$LDFLAGS -lpthread" 115 LDFLAGS="$LDFLAGS -lpthread"
110 ;; 116 ;;
111*arm-linux*) 117*arm-linux*)
112 AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system]) 118 AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system])
113 CFLAGS="-D_REENTRANT -fPIC -pipe $CFLAGS" 119 CFLAGS="-D_REENTRANT -fPIC -pipe $CFLAGS"
114 AM_CONDITIONAL(HAVE_GNU_LD, true) 120 AM_CONDITIONAL(HAVE_GNU_LD, true)
121 AM_CONDITIONAL(HAVE_W32, false)
115 ;; 122 ;;
116*cygwin*) 123*cygwin*)
117 AC_DEFINE_UNQUOTED(CYGWIN,1,[This is a Cygwin system]) 124 AC_DEFINE_UNQUOTED(CYGWIN,1,[This is a Cygwin system])
118 AM_CONDITIONAL(HAVE_GNU_LD, false) 125 AM_CONDITIONAL(HAVE_GNU_LD, false)
126 AM_CONDITIONAL(HAVE_W32, false)
119 LDFLAGS="$LDFLAGS -no-undefined" 127 LDFLAGS="$LDFLAGS -no-undefined"
120 ;; 128 ;;
121*mingw*) 129*mingw*)
122 AC_DEFINE_UNQUOTED(MINGW,1,[This is a MinGW system]) 130 AC_DEFINE_UNQUOTED(MINGW,1,[This is a MinGW system])
123 AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system]) 131 AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system])
132 AM_CONDITIONAL(HAVE_W32, true)
124 LDFLAGS="$LDFLAGS -no-undefined -Wl,--export-all-symbols -lws2_32" 133 LDFLAGS="$LDFLAGS -no-undefined -Wl,--export-all-symbols -lws2_32"
125 AM_CONDITIONAL(HAVE_GNU_LD, true) 134 AM_CONDITIONAL(HAVE_GNU_LD, true)
126 # check if PlibC is available 135 # check if PlibC is available
@@ -133,11 +142,13 @@ netbsd*)
133*openedition*) 142*openedition*)
134 AC_DEFINE_UNQUOTED(OS390,1,[This is a OS/390 system]) 143 AC_DEFINE_UNQUOTED(OS390,1,[This is a OS/390 system])
135 AM_CONDITIONAL(HAVE_GNU_LD, false) 144 AM_CONDITIONAL(HAVE_GNU_LD, false)
145 AM_CONDITIONAL(HAVE_W32, false)
136 ;; 146 ;;
137*) 147*)
138 AC_MSG_RESULT(Unrecognised OS $host_os) 148 AC_MSG_RESULT(Unrecognised OS $host_os)
139 AC_DEFINE_UNQUOTED(OTHEROS,1,[Some strange OS]) 149 AC_DEFINE_UNQUOTED(OTHEROS,1,[Some strange OS])
140 AM_CONDITIONAL(HAVE_GNU_LD, false) 150 AM_CONDITIONAL(HAVE_GNU_LD, false)
151 AM_CONDITIONAL(HAVE_W32, false)
141;; 152;;
142esac 153esac
143 154
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index 45992576..fda0e437 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -19,6 +19,10 @@ AM_CPPFLAGS = \
19-I$(top_srcdir)/src/include \ 19-I$(top_srcdir)/src/include \
20$(LIBCURL_CPPFLAGS) 20$(LIBCURL_CPPFLAGS)
21 21
22if !HAVE_W32
23PERF_GET_CONCURRENT=perf_get_concurrent
24endif
25
22check_PROGRAMS = \ 26check_PROGRAMS = \
23 daemontest_get \ 27 daemontest_get \
24 daemontest_get_sendfile \ 28 daemontest_get_sendfile \
@@ -43,7 +47,10 @@ check_PROGRAMS = \
43 daemontest_put_chunked \ 47 daemontest_put_chunked \
44 daemontest_iplimit11 \ 48 daemontest_iplimit11 \
45 daemontest_termination \ 49 daemontest_termination \
46 daemontest_timeout 50 daemontest_timeout \
51 perf_get $(PERF_GET_CONCURRENT)
52
53
47noinst_PROGRAMS = \ 54noinst_PROGRAMS = \
48 daemon_options_test 55 daemon_options_test
49 56
@@ -70,6 +77,20 @@ daemontest_get_LDADD = \
70 $(top_builddir)/src/daemon/libmicrohttpd.la \ 77 $(top_builddir)/src/daemon/libmicrohttpd.la \
71 @LIBCURL@ 78 @LIBCURL@
72 79
80perf_get_SOURCES = \
81 perf_get.c \
82 gauger.h
83perf_get_LDADD = \
84 $(top_builddir)/src/daemon/libmicrohttpd.la \
85 @LIBCURL@
86
87perf_get_concurrent_SOURCES = \
88 perf_get_concurrent.c \
89 gauger.h
90perf_get_concurrent_LDADD = \
91 $(top_builddir)/src/daemon/libmicrohttpd.la \
92 @LIBCURL@
93
73daemontest_digestauth_SOURCES = \ 94daemontest_digestauth_SOURCES = \
74 daemontest_digestauth.c 95 daemontest_digestauth.c
75daemontest_digestauth_LDADD = \ 96daemontest_digestauth_LDADD = \
diff --git a/src/testcurl/gauger.h b/src/testcurl/gauger.h
new file mode 100644
index 00000000..3a0dd220
--- /dev/null
+++ b/src/testcurl/gauger.h
@@ -0,0 +1,86 @@
1/** ---------------------------------------------------------------------------
2 * This software is in the public domain, furnished "as is", without technical
3 * support, and with no warranty, express or implied, as to its usefulness for
4 * any purpose.
5 *
6 * gauger.h
7 * Interface for C programs to log remotely to a gauger server
8 *
9 * Author: Bartlomiej Polot
10 * -------------------------------------------------------------------------*/
11#ifndef __GAUGER_H__
12#define __GAUGER_H__
13
14#ifndef WINDOWS
15
16#include <unistd.h>
17#include <stdio.h>
18#include <sys/wait.h>
19
20#define GAUGER(category, counter, value, unit)\
21{\
22 const char * __gauger_v[10]; \
23 char __gauger_s[32];\
24 pid_t __gauger_p;\
25 if(!(__gauger_p=fork())){\
26 if(!fork()){ \
27 sprintf(__gauger_s,"%Lf", (long double) (value));\
28 __gauger_v[0] = "gauger";\
29 __gauger_v[1] = "-n";\
30 __gauger_v[2] = counter; \
31 __gauger_v[3] = "-d";\
32 __gauger_v[4] = __gauger_s;\
33 __gauger_v[5] = "-u";\
34 __gauger_v[6] = unit; \
35 __gauger_v[7] = "-c";\
36 __gauger_v[8] = category; \
37 __gauger_v[9] = (char *)NULL;\
38 execvp("gauger", (char*const*) __gauger_v); \
39 _exit(1);\
40 }else{\
41 _exit(0);\
42 }\
43 }else{\
44 waitpid(__gauger_p,NULL,0);\
45 }\
46}
47
48#define GAUGER_ID(category, counter, value, unit, id)\
49{\
50 char* __gauger_v[12];\
51 char __gauger_s[32];\
52 pid_t __gauger_p;\
53 if(!(__gauger_p=fork())){\
54 if(!fork()){\
55 sprintf(__gauger_s,"%Lf", (long double) (value));\
56 __gauger_v[0] = "gauger";\
57 __gauger_v[1] = "-n";\
58 __gauger_v[2] = counter;\
59 __gauger_v[3] = "-d";\
60 __gauger_v[4] = __gauger_s;\
61 __gauger_v[5] = "-u";\
62 __gauger_v[6] = unit;\
63 __gauger_v[7] = "-i";\
64 __gauger_v[8] = id;\
65 __gauger_v[9] = "-c";\
66 __gauger_v[10] = category;\
67 __gauger_v[11] = (char *)NULL;\
68 execvp("gauger",__gauger_v);\
69 perror("gauger");\
70 _exit(1);\
71 }else{\
72 _exit(0);\
73 }\
74 }else{\
75 waitpid(__gauger_p,NULL,0);\
76 }\
77}
78
79#else
80
81#define GAUGER_ID(category, counter, value, unit, id) {}
82#define GAUGER(category, counter, value, unit) {}
83
84#endif // WINDOWS
85
86#endif
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}
diff --git a/src/testcurl/perf_get_concurrent.c b/src/testcurl/perf_get_concurrent.c
new file mode 100644
index 00000000..deb949b2
--- /dev/null
+++ b/src/testcurl/perf_get_concurrent.c
@@ -0,0 +1,338 @@
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_concurrent.c
23 * @brief benchmark concurrent GET operations
24 * Note that we run libcurl on the machine at the
25 * same time, so the execution time may be influenced
26 * by the concurrent activity; 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 * @author Christian Grothoff
33 */
34
35#include "MHD_config.h"
36#include "platform.h"
37#include <curl/curl.h>
38#include <microhttpd.h>
39#include <stdlib.h>
40#include <string.h>
41#include <time.h>
42#include "gauger.h"
43
44/**
45 * How many rounds of operations do we do for each
46 * test (total number of requests will be ROUNDS * PAR).
47 */
48#define ROUNDS 500
49
50/**
51 * How many requests do we do in parallel?
52 */
53#define PAR 4
54
55/**
56 * Do we use HTTP 1.1?
57 */
58static int oneone;
59
60/**
61 * Response to return (re-used).
62 */
63static struct MHD_Response *response;
64
65/**
66 * Time this round was started.
67 */
68static unsigned long long start_time;
69
70
71/**
72 * Get the current timestamp
73 *
74 * @return current time in ms
75 */
76static unsigned long long
77now ()
78{
79 struct timeval tv;
80
81 GETTIMEOFDAY (&tv, NULL);
82 return (((unsigned long long) tv.tv_sec * 1000LL) +
83 ((unsigned long long) tv.tv_usec / 1000LL));
84}
85
86
87/**
88 * Start the timer.
89 */
90static void
91start_timer()
92{
93 start_time = now ();
94}
95
96
97/**
98 * Stop the timer and report performance
99 *
100 * @param desc description of the threading mode we used
101 */
102static void
103stop (const char *desc)
104{
105 double rps = ((double) (PAR * ROUNDS * 1000)) / ((double) (now() - start_time));
106
107 fprintf (stderr,
108 "Parallel GETs using %s: %f %s\n",
109 desc,
110 rps,
111 "requests/s");
112 GAUGER (desc,
113 "Parallel GETs",
114 rps,
115 "requests/s");
116}
117
118
119static size_t
120copyBuffer (void *ptr,
121 size_t size, size_t nmemb,
122 void *ctx)
123{
124 return size * nmemb;
125}
126
127static int
128ahc_echo (void *cls,
129 struct MHD_Connection *connection,
130 const char *url,
131 const char *method,
132 const char *version,
133 const char *upload_data, size_t *upload_data_size,
134 void **unused)
135{
136 static int ptr;
137 const char *me = cls;
138 int ret;
139
140 if (0 != strcmp (me, method))
141 return MHD_NO; /* unexpected method */
142 if (&ptr != *unused)
143 {
144 *unused = &ptr;
145 return MHD_YES;
146 }
147 *unused = NULL;
148 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
149 if (ret == MHD_NO)
150 abort ();
151 return ret;
152}
153
154
155static pid_t
156do_gets ()
157{
158 pid_t ret;
159 CURL *c;
160 CURLcode errornum;
161 unsigned int i;
162 unsigned int j;
163 pid_t par[PAR];
164
165 ret = fork ();
166 if (ret == -1) abort ();
167 if (ret != 0)
168 return ret;
169 for (j=0;j<PAR;j++)
170 {
171 par[j] = fork ();
172 if (par[j] == 0)
173 {
174 for (i=0;i<ROUNDS;i++)
175 {
176 c = curl_easy_init ();
177 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world");
178 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
179 curl_easy_setopt (c, CURLOPT_WRITEDATA, NULL);
180 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
181 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
182 if (oneone)
183 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
184 else
185 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
186 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
187 /* NOTE: use of CONNECTTIMEOUT without also
188 setting NOSIGNAL results in really weird
189 crashes on my system! */
190 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
191 if (CURLE_OK != (errornum = curl_easy_perform (c)))
192 {
193 fprintf (stderr,
194 "curl_easy_perform failed: `%s'\n",
195 curl_easy_strerror (errornum));
196 curl_easy_cleanup (c);
197 _exit (1);
198 }
199 curl_easy_cleanup (c);
200 }
201 _exit (0);
202 }
203 }
204 for (j=0;j<PAR;j++)
205 waitpid (par[j], NULL, 0);
206 _exit (0);
207}
208
209
210static void
211join_gets (pid_t pid)
212{
213 int status;
214
215 status = 1;
216 waitpid (pid, &status, 0);
217 if (0 != status)
218 abort ();
219}
220
221
222static int
223testInternalGet (int poll_flag)
224{
225 struct MHD_Daemon *d;
226
227 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
228 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
229 if (d == NULL)
230 return 1;
231 start_timer ();
232 join_gets (do_gets ());
233 stop (poll_flag ? "internal poll" : "internal select");
234 MHD_stop_daemon (d);
235 return 0;
236}
237
238
239static int
240testMultithreadedGet (int poll_flag)
241{
242 struct MHD_Daemon *d;
243
244 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | poll_flag,
245 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
246 if (d == NULL)
247 return 16;
248 start_timer ();
249 join_gets (do_gets ());
250 stop (poll_flag ? "thread with poll" : "thread with select");
251 MHD_stop_daemon (d);
252 return 0;
253}
254
255static int
256testMultithreadedPoolGet (int poll_flag)
257{
258 struct MHD_Daemon *d;
259
260 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
261 1081, NULL, NULL, &ahc_echo, "GET",
262 MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END);
263 if (d == NULL)
264 return 16;
265 start_timer ();
266 join_gets (do_gets ());
267 stop (poll_flag ? "thread pool with poll" : "thread pool with select");
268 MHD_stop_daemon (d);
269 return 0;
270}
271
272static int
273testExternalGet ()
274{
275 struct MHD_Daemon *d;
276 pid_t pid;
277 fd_set rs;
278 fd_set ws;
279 fd_set es;
280 int max;
281 struct timeval tv;
282 unsigned MHD_LONG_LONG tt;
283 int tret;
284
285 d = MHD_start_daemon (MHD_USE_DEBUG,
286 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
287 if (d == NULL)
288 return 256;
289 start_timer ();
290 pid = do_gets ();
291 while (0 == waitpid (pid, NULL, WNOHANG))
292 {
293 max = 0;
294 FD_ZERO (&rs);
295 FD_ZERO (&ws);
296 FD_ZERO (&es);
297 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
298 {
299 MHD_stop_daemon (d);
300 return 4096;
301 }
302 tret = MHD_get_timeout (d, &tt);
303 if (MHD_YES != tret) tt = 1;
304 tv.tv_sec = tt / 1000;
305 tv.tv_usec = 1000 * (tt % 1000);
306 select (max + 1, &rs, &ws, &es, &tv);
307 MHD_run (d);
308 }
309 stop ("external select");
310 MHD_stop_daemon (d);
311 return 0;
312}
313
314
315int
316main (int argc, char *const *argv)
317{
318 unsigned int errorCount = 0;
319
320 oneone = NULL != strstr (argv[0], "11");
321 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
322 return 2;
323 response = MHD_create_response_from_buffer (strlen ("/hello_world"),
324 "/hello_world",
325 MHD_RESPMEM_MUST_COPY);
326 errorCount += testInternalGet (0);
327 errorCount += testMultithreadedGet (0);
328 errorCount += testMultithreadedPoolGet (0);
329 errorCount += testExternalGet ();
330 errorCount += testInternalGet (MHD_USE_POLL);
331 errorCount += testMultithreadedGet (MHD_USE_POLL);
332 errorCount += testMultithreadedPoolGet (MHD_USE_POLL);
333 MHD_destroy_response (response);
334 if (errorCount != 0)
335 fprintf (stderr, "Error (code: %u)\n", errorCount);
336 curl_global_cleanup ();
337 return errorCount != 0; /* 0 == pass */
338}