Skip to content

Commit 9547e1a

Browse files
committed
Use writev() to send responses
Massive performance improvements!
1 parent 824d03c commit 9547e1a

File tree

4 files changed

+47
-25
lines changed

4 files changed

+47
-25
lines changed

README

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ It is meant to be only an exercise in writing a server using BSD sockets and
66
other auxilliary APIs, like epoll. It is not meant to serve things on the
77
Internet, as it isn't resilient enough.
88

9-
This can yield up to 40000 requests/s on a dual-core Core i7 processor. Not
10-
much, but for something written in just over a day, it's not as bad as it
11-
sounds. I'm open to suggestions that can improve performance; better yet if
12-
you can fork it and actually make it happen.
9+
This can yield around 175000 requests/s on a dual-core Core i7 processor.
10+
Not too shabby for something written in about 20 days. I'm open to
11+
suggestions that can improve performance; better yet if you can fork it and
12+
actually make it happen.

lwan-serve-files.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
static lwan_http_status_t
3535
_serve_file_stream(lwan_t* l, lwan_request_t *request, void *data)
3636
{
37+
char headers[512];
3738
lwan_http_status_t return_status;
3839
int file_fd;
3940
struct stat st;
41+
size_t header_len;
4042

4143
if (UNLIKELY((file_fd = open(data, O_RDONLY)) < 0)) {
4244
return_status = (errno == EACCES) ? HTTP_FORBIDDEN : HTTP_NOT_FOUND;
@@ -63,15 +65,27 @@ _serve_file_stream(lwan_t* l, lwan_request_t *request, void *data)
6365
return _serve_file_stream(l, request, index_file);
6466
}
6567

66-
lwan_request_set_corked(request, true);
6768
request->response->content_length = st.st_size;
68-
if (UNLIKELY(!lwan_response_header(l, request, HTTP_OK))) {
69+
header_len = lwan_prepare_response_header(l, request, HTTP_OK, headers);
70+
if (!header_len) {
6971
return_status = HTTP_INTERNAL_ERROR;
70-
goto end_corked;
72+
goto end;
7173
}
7274

7375
if (request->method == HTTP_HEAD) {
74-
return_status = HTTP_OK;
76+
if (UNLIKELY(write(request->fd, headers, header_len) < 0)) {
77+
perror("write");
78+
return_status = HTTP_INTERNAL_ERROR;
79+
} else
80+
return_status = HTTP_OK;
81+
goto end;
82+
}
83+
84+
lwan_request_set_corked(request, true);
85+
86+
if (UNLIKELY(write(request->fd, headers, header_len) < 0)) {
87+
perror("write");
88+
return_status = HTTP_INTERNAL_ERROR;
7589
goto end_corked;
7690
}
7791

lwan.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <sys/socket.h>
3636
#include <sys/time.h>
3737
#include <sys/types.h>
38+
#include <sys/uio.h>
3839
#include <unistd.h>
3940

4041
#include "lwan.h"
@@ -596,10 +597,11 @@ lwan_request_set_response(lwan_request_t *request, lwan_response_t *response)
596597
#define APPEND_CONSTANT(const_str_) \
597598
APPEND_STRING_LEN((const_str_), sizeof(const_str_) - 1)
598599

599-
bool
600-
lwan_response_header(lwan_t *l __attribute__((unused)), lwan_request_t *request, lwan_http_status_t status)
600+
ALWAYS_INLINE size_t
601+
lwan_prepare_response_header(lwan_t *l __attribute__((unused)), lwan_request_t *request, lwan_http_status_t status,
602+
char headers[])
601603
{
602-
char headers[512], *p_headers;
604+
char *p_headers;
603605
char buffer[32];
604606
int32_t len;
605607

@@ -620,12 +622,7 @@ lwan_response_header(lwan_t *l __attribute__((unused)), lwan_request_t *request,
620622
(request->flags.is_keep_alive ? sizeof("Keep-Alive") : sizeof("Close")) - 1);
621623
APPEND_CONSTANT("\r\n\r\n\0");
622624

623-
if (UNLIKELY(write(request->fd, headers, strlen(headers)) < 0)) {
624-
perror("write header");
625-
return false;
626-
}
627-
628-
return true;
625+
return p_headers - headers - 1;
629626
}
630627

631628
#undef APPEND_STRING_LEN
@@ -637,6 +634,8 @@ lwan_response_header(lwan_t *l __attribute__((unused)), lwan_request_t *request,
637634
bool
638635
lwan_response(lwan_t *l, lwan_request_t *request, lwan_http_status_t status)
639636
{
637+
char headers[512];
638+
640639
if (UNLIKELY(!request->response)) {
641640
lwan_default_response(l, request, status);
642641
return false;
@@ -654,16 +653,25 @@ lwan_response(lwan_t *l, lwan_request_t *request, lwan_http_status_t status)
654653
return false;
655654
}
656655

657-
if (UNLIKELY(!lwan_response_header(l, request, status)))
658-
return false;
656+
size_t header_len = lwan_prepare_response_header(l, request, status, headers);
657+
if (!header_len)
658+
return lwan_default_response(l, request, HTTP_INTERNAL_ERROR);
659659

660-
if (request->method == HTTP_HEAD)
660+
if (request->method == HTTP_HEAD) {
661+
if (write(request->fd, headers, header_len) < 0) {
662+
perror("write");
663+
return false;
664+
}
661665
return true;
666+
}
667+
668+
struct iovec response_vec[] = {
669+
{ .iov_base = headers, .iov_len = header_len },
670+
{ .iov_base = request->response->content, .iov_len = request->response->content_length }
671+
};
662672

663-
if (UNLIKELY(write(request->fd,
664-
request->response->content,
665-
request->response->content_length) < 0)) {
666-
perror("write response");
673+
if (UNLIKELY(writev(request->fd, response_vec, N_ELEMENTS(response_vec)) < 0)) {
674+
perror("writev");
667675
return false;
668676
}
669677

lwan.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ void lwan_set_url_map(lwan_t *l, lwan_url_map_t *url_map);
172172
void lwan_main_loop(lwan_t *l);
173173
void lwan_request_set_response(lwan_request_t *request, lwan_response_t *response);
174174
bool lwan_response(lwan_t *l, lwan_request_t *request, lwan_http_status_t status);
175-
bool lwan_response_header(lwan_t *l, lwan_request_t *request, lwan_http_status_t status);
175+
size_t lwan_prepare_response_header(lwan_t *l, lwan_request_t *request, lwan_http_status_t status, char header_buffer[]);
176176
bool lwan_default_response(lwan_t *l, lwan_request_t *request, lwan_http_status_t status);
177177
const char *lwan_http_status_as_string(lwan_http_status_t status) __attribute__((pure));
178178
const char *lwan_determine_mime_type_for_file_name(char *file_name) __attribute__((pure));

0 commit comments

Comments
 (0)