From 251aebd2c6f1367c9c27ba5b3e25896cb264639a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 16 Jan 2013 08:37:48 +0800 Subject: [PATCH] optimize http file sending This adapts the approach from the single-packet-per-poll-loop improvement to sending more packets while the socket can take them. It still falls back to the multi-state scheme if the socket ever chokes, which it certainly will on larger files, so it's safe while being highly efficient at smaller file sizes. Nor should it significantly add to latency for other sockets, it simply stuffs the pipe asynchronously as much as the pipe can take. We also increase the packet payoad size from 512 to 1400 a time. This reduces the time taken in the 300 connection / 5000 transfers ab test from >8s to ~3.4s, transferring the same amount of data. $ ab -t 100 -n 5000 -c 300 'http://127.0.0.1:7681/' This is ApacheBench, Version 2.3 <$Revision: 1373084 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 500 requests Completed 1000 requests Completed 1500 requests Completed 2000 requests Completed 2500 requests Completed 3000 requests Completed 3500 requests Completed 4000 requests Completed 4500 requests Completed 5000 requests Finished 5000 requests Server Software: libwebsockets Server Hostname: 127.0.0.1 Server Port: 7681 Document Path: / Document Length: 8447 bytes Concurrency Level: 300 Time taken for tests: 3.400 seconds Complete requests: 5000 Failed requests: 0 Write errors: 0 Total transferred: 42680000 bytes HTML transferred: 42235000 bytes Requests per second: 1470.76 [#/sec] (mean) Time per request: 203.976 [ms] (mean) Time per request: 0.680 [ms] (mean, across all concurrent requests) Transfer rate: 12260.17 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 7 24 15.6 20 125 Processing: 32 172 50.2 161 407 Waiting: 27 154 49.4 142 386 Total: 81 196 48.3 182 428 Percentage of the requests served within a certain time (ms) 50% 182 66% 185 75% 188 80% 194 90% 304 95% 316 98% 322 99% 328 100% 428 (longest request) Signed-off-by: Andy Green --- lib/parsers.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/lib/parsers.c b/lib/parsers.c index 952aee2..4da1cff 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -1994,8 +1994,9 @@ int libwebsockets_serve_http_file(struct libwebsocket_context *context, { int fd; struct stat stat_buf; - char buf[512]; + char buf[1400]; char *p = buf; + int n, m; strncpy(wsi->filepath, file, sizeof wsi->filepath); wsi->filepath[sizeof(wsi->filepath) - 1] = '\0'; @@ -2025,12 +2026,49 @@ int libwebsockets_serve_http_file(struct libwebsocket_context *context, "\x0d\x0a", content_type, (unsigned int)stat_buf.st_size); - libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); + n = libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); + if (n) { + close(fd); + return n; + } wsi->filepos = 0; - libwebsocket_callback_on_writable(context, wsi); wsi->state = WSI_STATE_HTTP_ISSUING_FILE; + while (!lws_send_pipe_choked(wsi)) { + + n = read(fd, buf, sizeof buf); + if (n > 0) { + wsi->filepos += n; + m = libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP); + if (m) { + close(fd); + return m; + } + } + + if (n < 0) { + close(fd); + return -1; + } + + if (n < sizeof(buf) || wsi->filepos == wsi->filelen) { + /* oh, we were able to finish here! */ + wsi->state = WSI_STATE_HTTP; + close(fd); + + if (wsi->protocol->callback(context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space, + wsi->filepath, wsi->filepos)) + libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 0; + } + } + + /* we choked, no worries schedule service for the rest of it */ + + libwebsocket_callback_on_writable(context, wsi); + close(fd); return 0; @@ -2041,7 +2079,7 @@ int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context, { int fd; int ret = 0; - char buf[512]; + char buf[1400]; int n; #ifdef WIN32 @@ -2054,20 +2092,26 @@ int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context, lseek(fd, wsi->filepos, SEEK_SET); - n = read(fd, buf, 512); - if (n > 0) { - libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP); - wsi->filepos += n; - } + while (!lws_send_pipe_choked(wsi)) { + n = read(fd, buf, sizeof buf); + if (n > 0) { + libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP); + wsi->filepos += n; + } - if (n < 0) - ret = -1; + if (n < 0) { + close(fd); + return -1; + } - if (n < 512 || wsi->filepos == wsi->filelen) - wsi->state = WSI_STATE_HTTP; - else - if (!ret) - libwebsocket_callback_on_writable(context, wsi); + if (n < sizeof(buf) || wsi->filepos == wsi->filelen) { + wsi->state = WSI_STATE_HTTP; + close(fd); + return 0; + } + } + + libwebsocket_callback_on_writable(context, wsi); close(fd); -- 2.7.4