├── COPYING ├── Makefile ├── README.md ├── build-many.sh ├── run-many.sh └── sprinter.c /COPYING: -------------------------------------------------------------------------------- 1 | COPYRIGHT AND PERMISSION NOTICE 2 | 3 | Copyright (c) 1996 - 2021, Daniel Stenberg, , and many 4 | contributors, see the THANKS file. 5 | 6 | All rights reserved. 7 | 8 | Permission to use, copy, modify, and distribute this software for any purpose 9 | with or without fee is hereby granted, provided that the above copyright 10 | notice and this permission notice appear in all copies. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN 15 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 18 | OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | Except as contained in this notice, the name of a copyright holder shall not 21 | be used in advertising or otherwise to promote the sale, use or other dealings 22 | in this Software without prior written authorization of the copyright holder. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | TARGET=sprinter 3 | TARGETOLD=sprinter-old 4 | OBJS=sprinter.o 5 | OBJSOLD=sprinter-old.o 6 | LIBS=-lcurl 7 | CFLAGS=-O3 8 | 9 | all: $(TARGET) $(TARGETOLD) 10 | 11 | $(TARGET): $(OBJS) 12 | $(CC) -o $@ $< $(LIBS) 13 | 14 | $(TARGETOLD): $(OBJSOLD) 15 | $(CC) -o $@ $< $(LIBS) 16 | 17 | sprinter.o: sprinter.c 18 | $(CC) $(CFLAGS) -c $< 19 | 20 | sprinter-old.o: sprinter.c 21 | $(CC) -DFOR_OLDER=1 $(CFLAGS) -o $@ -c $< 22 | 23 | clean: 24 | rm -f $(TARGET) $(OBJS) $(TARGETOLD) $(OBJSOLD) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # relative 2 | Tools to measure libcurl performance delta between versions 3 | 4 | ## build-many 5 | 6 | This script iterates over all the curl versions listed in the top of the file 7 | and for each version it will build and install it locally. It will clone the 8 | git repository first if not present. 9 | 10 | Each built libcurl version is installed in `build/$version`. The used build 11 | directory will be removed after install. 12 | 13 | ## run-many 14 | 15 | This script iterates over all the libcurl versions found installed in 16 | `build/$version`. For each such version, it will set `LD_LIBRARY_PATH` and 17 | then invoke `./sprinter` to have that specific version performance tested. 18 | 19 | If the found libcurl version is older than 7.66.0, the `./sprinter-old` tool 20 | will instead be used (to use a different API). 21 | 22 | ## sprinter 23 | 24 | Downloads the same URL a given number of times, using a single thread and the 25 | libcurl multi interface. It uses a specified number of concurrent transfers. 26 | 27 | The data is simply dropped on receival. If any transfer returns an error, the 28 | entire operation is canceled and an error message is shown. 29 | 30 | Only use this tool against servers you have permission to torture. 31 | 32 | ### Example using 8GB downloads 33 | 34 | ~~~ 35 | $ ./sprinter localhost/8GB 6 2 36 | curl: 7.79.0-DEV 37 | URL: localhost/8GB 38 | Transfers: 6 [2 in parallel]... 39 | Time: 8110158 us, 1351693.00 us/transfer 40 | Freq: 0.74 requests/second 41 | Downloaded: 51539608924 bytes, 48.0 GB 42 | Speed: 6354945110.1 bytes/sec 6060.5 MB/s (8589934820 bytes/transfer) 43 | ~~~ 44 | 45 | ### Example using 4KB downloads 46 | 47 | ~~~ 48 | $ ./sprinter localhost/4KB 100000 100 49 | curl: 7.79.0-DEV 50 | URL: localhost/4KB 51 | Transfers: 100000 [100 in parallel]... 52 | Time: 4065449 us, 40.65 us/transfer 53 | Freq: 24597.53 requests/second 54 | Downloaded: 430255224 bytes, 0.4 GB 55 | Speed: 105832153.8 bytes/sec 100.9 MB/s (4302 bytes/transfer) 56 | ~~~ 57 | -------------------------------------------------------------------------------- /build-many.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # libcurl versions to build 4 | ALL="7.78.0 7.77.0 7.76.1 7.70.0 7.65.0 7.60.0 7.58.0 7.50.0" 5 | 6 | if ! test -d curl; then 7 | echo "no git clone found, cloning..." 8 | git clone https://github.com/curl/curl.git 9 | fi 10 | 11 | pwd=`pwd` 12 | 13 | # iterate over all versions and build+install what's not already present 14 | for ver in $ALL; do 15 | if ! test -d "build/$ver"; then 16 | echo "======================" 17 | echo "==== Build $ver ====" 18 | echo "======================" 19 | tag=`echo "curl-$ver" | tr . _` 20 | # change cwd into curl source tree 21 | cd curl 22 | # get our version 23 | git checkout -q $tag 24 | # generate the configure script 25 | ./buildconf 26 | # create a build directory 27 | mkdir $ver && cd $ver 28 | # configure it 29 | ../configure --prefix=$pwd/build/$ver --with-ssl 30 | # build and install 31 | make -sj7 && make install 32 | # remove the build directory again 33 | cd .. && rm -rf $ver 34 | # go back to the original cwd 35 | cd $pwd 36 | fi 37 | done 38 | -------------------------------------------------------------------------------- /run-many.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # use installed versions 4 | 5 | pwd=`pwd` 6 | 7 | cd build 8 | 9 | ALL=`ls`; 10 | 11 | cd $pwd 12 | for ver in $ALL; do 13 | major=`echo $ver | cut -d. -f1`; 14 | minor=`echo $ver | cut -d. -f2`; 15 | SPRINTER=./sprinter 16 | if test "$major" = 7 && test "$minor" -lt 66; then 17 | SPRINTER=./sprinter-old 18 | fi 19 | if ! test -x $SPRINTER; then 20 | echo "$SPRINTER is missing, forget to build?" 21 | exit 22 | fi 23 | LD_LIBRARY_PATH=$pwd/build/$ver/lib $SPRINTER localhost/4KB 100000 10 2>/dev/null 24 | done 25 | -------------------------------------------------------------------------------- /sprinter.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * _ _ ____ _ 3 | * Project ___| | | | _ \| | 4 | * / __| | | | |_) | | 5 | * | (__| |_| | _ <| |___ 6 | * \___|\___/|_| \_\_____| 7 | * 8 | * Copyright (C) 2021, Daniel Stenberg, , et al. 9 | * 10 | * This software is licensed as described in the file COPYING, which 11 | * you should have received as part of this distribution. The terms 12 | * are also available at https://curl.se/docs/copyright.html. 13 | * 14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 | * copies of the Software, and permit persons to whom the Software is 16 | * furnished to do so, under the terms of the COPYING file. 17 | * 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 | * KIND, either express or implied. 20 | * 21 | ***************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #ifdef _WIN32 28 | #include 29 | #else 30 | #include 31 | #endif 32 | #include 33 | 34 | #ifdef _WIN32 35 | #define FORMAT_SIZE_T "Iu" 36 | #else 37 | #define FORMAT_SIZE_T "zu" 38 | #endif 39 | 40 | #ifndef CURL_VERSION_BITS 41 | #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) 42 | #endif 43 | 44 | #ifndef CURL_AT_LEAST_VERSION 45 | #define CURL_AT_LEAST_VERSION(x,y,z) \ 46 | (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) 47 | #endif 48 | 49 | #if !CURL_AT_LEAST_VERSION(7, 28, 0) 50 | #error "this code needs libcurl 7.28.0 or later" 51 | #endif 52 | 53 | #ifdef _WIN32 54 | static LARGE_INTEGER tool_freq; 55 | #endif 56 | 57 | #define MAXPARALLEL 500 /* max parallelism */ 58 | #define NPARALLEL 100 /* Default number of concurrent transfers */ 59 | #define NTOTAL 100000 /* Default number of transfers in total */ 60 | #define RESPCODE 200 /* Default expected response code from server */ 61 | 62 | size_t downloaded; 63 | 64 | static size_t write_cb(char *data, size_t n, size_t l, void *userp) 65 | { 66 | /* ignore the data here */ 67 | (void)data; 68 | (void)userp; 69 | downloaded += n*l; 70 | return n*l; 71 | } 72 | 73 | static void timestamp(struct timeval *t) 74 | { 75 | #ifdef WIN32 76 | LARGE_INTEGER count; 77 | QueryPerformanceCounter(&count); 78 | t->tv_sec = (long)(count.QuadPart / tool_freq.QuadPart); 79 | t->tv_usec = (long)((count.QuadPart % tool_freq.QuadPart) * 1000000 / 80 | tool_freq.QuadPart); 81 | #else 82 | (void)gettimeofday(t, NULL); 83 | #endif 84 | } 85 | 86 | static size_t timediff(struct timeval newer, struct timeval older) 87 | { 88 | size_t diff = (size_t)newer.tv_sec-older.tv_sec; 89 | return diff * 1000000 + newer.tv_usec-older.tv_usec; 90 | } 91 | 92 | #define SPRINTER_VERSION "0.1" 93 | 94 | int main(int argc, char **argv) 95 | { 96 | CURL *handles[MAXPARALLEL]; 97 | CURLM *multi_handle; 98 | int still_running = 1; /* keep number of running handles */ 99 | int i; 100 | CURLMsg *msg; /* for picking up messages with the transfer status */ 101 | int msgs_left; /* how many messages are left */ 102 | int total; 103 | int add; 104 | struct timeval start; 105 | struct timeval end; 106 | size_t diff; 107 | curl_version_info_data *v; 108 | const char *url; 109 | int ntotal = NTOTAL; 110 | int nparallel = NPARALLEL; 111 | int expected_respcode = RESPCODE; 112 | 113 | #ifdef _WIN32 114 | QueryPerformanceFrequency(&tool_freq); 115 | #endif 116 | 117 | if(argc < 2) { 118 | printf("curl sprinter version %s\n" 119 | "Usage: sprinter [total] [parallel] [respcode]\n" 120 | " will be downloaded\n" 121 | " [total] number of times (default %d) using\n" 122 | " [parallel] simultaneous transfers (default %d)\n" 123 | " [respcode] expected response code from server (default %d)\n", 124 | SPRINTER_VERSION, NTOTAL, NPARALLEL, RESPCODE); 125 | return 1; 126 | } 127 | 128 | url = argv[1]; 129 | if(argc > 2) 130 | ntotal = atoi(argv[2]); 131 | if(argc > 3) 132 | nparallel = atoi(argv[3]); 133 | if(argc > 4) 134 | expected_respcode = atoi(argv[4]); 135 | if(nparallel > ntotal) 136 | nparallel = ntotal; 137 | if(nparallel > MAXPARALLEL) 138 | nparallel = MAXPARALLEL; 139 | 140 | total = add = ntotal; 141 | 142 | /* Allocate one CURL handle per transfer */ 143 | for(i = 0; iversion, url, ntotal, nparallel); 169 | timestamp(&start); 170 | do { 171 | CURLMcode mc = curl_multi_perform(multi_handle, &still_running); 172 | 173 | if(still_running) { 174 | /* wait for activity, timeout or "nothing" */ 175 | #if (CURL_AT_LEAST_VERSION(7, 66, 0)) && !defined(FOR_OLDER) 176 | mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); 177 | #else 178 | /* should be mostly okay */ 179 | mc = curl_multi_wait(multi_handle, NULL, 0, 1000, NULL); 180 | #endif 181 | } 182 | if(mc) 183 | break; 184 | 185 | /* See how the transfers went */ 186 | while((msg = curl_multi_info_read(multi_handle, &msgs_left))) { 187 | if(msg->msg == CURLMSG_DONE) { 188 | CURL *e = msg->easy_handle; 189 | long respcode; 190 | /* anything but CURLE_OK here disqualifies this entire round */ 191 | if(msg->data.result) { 192 | fprintf(stderr, "Transfer returned %d!\n", msg->data.result); 193 | return 2; 194 | } 195 | if(curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &respcode) || 196 | expected_respcode != respcode) { 197 | fprintf(stderr, "Transfer returned unexpected response code %d!\n", 198 | respcode); 199 | return 2; 200 | } 201 | total--; 202 | curl_multi_remove_handle(multi_handle, e); 203 | if(add) { 204 | /* add it back in to get it restarted */ 205 | curl_multi_add_handle(multi_handle, e); 206 | add--; 207 | } 208 | } 209 | } 210 | } while(total); 211 | timestamp(&end); 212 | 213 | diff = timediff(end, start); 214 | printf("Time: %" FORMAT_SIZE_T " us, %.2f us/transfer\n", 215 | diff, (double)diff/(unsigned)ntotal); 216 | printf("Freq: %.2f requests/second\n", 217 | (unsigned)ntotal / ((double)diff/1000000.0)); 218 | printf("Downloaded: %" FORMAT_SIZE_T " bytes, %.1f GB\n", 219 | downloaded, downloaded/(double)(1024*1024*1024)); 220 | printf("Speed: %.1f bytes/sec %.1f MB/s (%" FORMAT_SIZE_T 221 | " bytes/transfer)\n", 222 | downloaded / ((double)diff/1000000.0), 223 | (downloaded / ((double)diff/1000000.0))/(1024*1024), 224 | downloaded / (unsigned)ntotal); 225 | 226 | curl_multi_cleanup(multi_handle); 227 | 228 | /* Free the CURL handles */ 229 | for(i = 0; i