/* This file is part of libmicrohttpd Copyright (C) 2007, 2009, 2011 Christian Grothoff libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. libmicrohttpd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libmicrohttpd; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file perf_get.c * @brief benchmark simple GET operations (sequential access). * Note that we run libcurl in the same process at the * same time, so the execution time given is the combined * time for both MHD and libcurl; it is quite possible * that more time is spend with libcurl than with MHD, * so the performance scores calculated with this code * should NOT be used to compare with other HTTP servers * (since MHD is actually better); only the relative * scores between MHD versions are meaningful. * Furthermore, this code ONLY tests MHD processing * a single request at a time. This is again * not universally meaningful (i.e. when comparing * multithreaded vs. single-threaded or select/poll). * @author Christian Grothoff */ #include "MHD_config.h" #include "platform.h" #include #include #include #include #include #include "gauger.h" #ifndef WINDOWS #include #include #endif #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 #undef CPU_COUNT #endif #if !defined(CPU_COUNT) #define CPU_COUNT 2 #endif /** * How many rounds of operations do we do for each * test? */ #define ROUNDS 500 /** * Do we use HTTP 1.1? */ static int oneone; /** * Response to return (re-used). */ static struct MHD_Response *response; /** * Time this round was started. */ static unsigned long long start_time; /** * Get the current timestamp * * @return current time in ms */ static unsigned long long now () { struct timeval tv; gettimeofday (&tv, NULL); return (((unsigned long long) tv.tv_sec * 1000LL) + ((unsigned long long) tv.tv_usec / 1000LL)); } /** * Start the timer. */ static void start_timer() { start_time = now (); } /** * Stop the timer and report performance * * @param desc description of the threading mode we used */ static void stop (const char *desc) { double rps = ((double) (ROUNDS * 1000)) / ((double) (now() - start_time)); fprintf (stderr, "Sequential GETs using %s: %f %s\n", desc, rps, "requests/s"); GAUGER (desc, "Sequential GETs", rps, "requests/s"); } struct CBC { char *buf; size_t pos; size_t size; }; static size_t copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) { struct CBC *cbc = ctx; if (cbc->pos + size * nmemb > cbc->size) return 0; /* overflow */ memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); cbc->pos += size * nmemb; return size * nmemb; } static int ahc_echo (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **unused) { static int ptr; const char *me = cls; int ret; (void)url;(void)version; /* Unused. Silent compiler warning. */ (void)upload_data;(void)upload_data_size; /* Unused. Silent compiler warning. */ if (0 != strcmp (me, method)) return MHD_NO; /* unexpected method */ if (&ptr != *unused) { *unused = &ptr; return MHD_YES; } *unused = NULL; ret = MHD_queue_response (connection, MHD_HTTP_OK, response); if (ret == MHD_NO) abort (); return ret; } static int testInternalGet (int port, int poll_flag) { struct MHD_Daemon *d; CURL *c; char buf[2048]; struct CBC cbc; CURLcode errornum; unsigned int i; char url[64]; if (MHD_NO != MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT)) port = 0; cbc.buf = buf; cbc.size = 2048; d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); if (d == NULL) return 1; if (0 == port) { const union MHD_DaemonInfo *dinfo; dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); if (NULL == dinfo || 0 == dinfo->port) { MHD_stop_daemon (d); return 32; } port = (int)dinfo->port; } sprintf(url, "http://127.0.0.1:%d/hello_world", port); start_timer (); for (i=0;iport) { MHD_stop_daemon (d); return 32; } port = (int)dinfo->port; } sprintf(url, "http://127.0.0.1:%d/hello_world", port); start_timer (); for (i=0;iport) { MHD_stop_daemon (d); return 32; } port = (int)dinfo->port; } sprintf(url, "http://127.0.0.1:%d/hello_world", port); start_timer (); for (i=0;iport) { MHD_stop_daemon (d); return 32; } port = (int)dinfo->port; } sprintf(url, "http://127.0.0.1:%d/hello_world", port); start_timer (); multi = curl_multi_init (); if (multi == NULL) { MHD_stop_daemon (d); return 512; } for (i=0;imsg == CURLMSG_DONE) { if (msg->data.result != CURLE_OK) printf ("%s failed at %s:%d: `%s'\n", "curl_multi_perform", __FILE__, __LINE__, curl_easy_strerror (msg->data.result)); curl_multi_remove_handle (multi, c); curl_easy_cleanup (c); c = NULL; } } /* two possibilities here; as select sets are tiny, this makes virtually no difference in actual runtime right now, even though the number of select calls is virtually cut in half (and 'select' is the most expensive of our system calls according to 'strace') */ if (0) MHD_run (d); else MHD_run_from_select (d, &rs, &ws, &es); } if (NULL != c) { curl_multi_remove_handle (multi, c); curl_easy_cleanup (c); fprintf (stderr, "Timeout!?\n"); } } stop ("external select"); if (multi != NULL) { curl_multi_cleanup (multi); } MHD_stop_daemon (d); if (cbc.pos != strlen ("/hello_world")) return 8192; if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) return 16384; return 0; } int main (int argc, char *const *argv) { unsigned int errorCount = 0; int port = 1130; (void)argc; /* Unused. Silent compiler warning. */ oneone = (NULL != strrchr (argv[0], (int) '/')) ? (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; if (oneone) port += 15; if (0 != curl_global_init (CURL_GLOBAL_WIN32)) return 2; response = MHD_create_response_from_buffer (strlen ("/hello_world"), "/hello_world", MHD_RESPMEM_MUST_COPY); errorCount += testExternalGet (port++); errorCount += testInternalGet (port++, MHD_USE_AUTO); errorCount += testMultithreadedGet (port++, MHD_USE_AUTO); errorCount += testMultithreadedPoolGet (port++, MHD_USE_AUTO); errorCount += testInternalGet (port++, 0); errorCount += testMultithreadedGet (port++, 0); errorCount += testMultithreadedPoolGet (port++, 0); if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL)) { errorCount += testInternalGet(port++, MHD_USE_POLL); errorCount += testMultithreadedGet(port++, MHD_USE_POLL); errorCount += testMultithreadedPoolGet(port++, MHD_USE_POLL); } if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL)) { errorCount += testInternalGet(port++, MHD_USE_EPOLL); errorCount += testMultithreadedPoolGet(port++, MHD_USE_EPOLL); } MHD_destroy_response (response); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); curl_global_cleanup (); return errorCount != 0; /* 0 == pass */ }