Introduction of thread pools is really good thing, but it adds overhead to reading files which are already in page cache in linux. With preadv2 (introduced in Linux 4.6) and RWF_NOWAIT flag (introduced in Linux 4.14) we can eliminate this overhead. Needs glibc >= 2.26
# HG changeset patch # User Vadim Fedorenko <vfedore...@yandex-team.ru> # Date 1515498853 -10800 # Tue Jan 09 14:54:13 2018 +0300 # Node ID f955f9cddd38ce35e19c50b871558ca8739a1d4b # Parent 6d2e92acb013224e6ef2c71c9e61ab07f0b03271 Add preadv2() with RWF_NOWAIT flag Eliminate overhead with threads synchronization when cache file or chain is in page cache already diff -r 6d2e92acb013 -r f955f9cddd38 auto/unix --- a/auto/unix Thu Dec 28 12:01:05 2017 +0200 +++ b/auto/unix Tue Jan 09 14:54:13 2018 +0300 @@ -726,6 +726,21 @@ if (n == -1) return 1" . auto/feature +# preadv2() was introduced in Linux 4.6, glibc 2.26 +# RWF_NOWAIT flag was introduced in Linux 4.14 + +ngx_feature="preadv2()" +ngx_feature_name="NGX_HAVE_PREADV2_NONBLOCK" +ngx_feature_run=no +ngx_feature_incs='#include <sys/uio.h>' +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="char buf[1]; struct iovec vec[1]; ssize_t n; + vec[0].iov_base = buf; + vec[0].iov_len = 1; + n = preadv2(0, vec, 1, 0, RWF_NOWAIT); + if (n == -1) return 1" +. auto/feature ngx_feature="sys_nerr" ngx_feature_name="NGX_SYS_NERR" diff -r 6d2e92acb013 -r f955f9cddd38 src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c Thu Dec 28 12:01:05 2017 +0200 +++ b/src/core/ngx_output_chain.c Tue Jan 09 14:54:13 2018 +0300 @@ -577,7 +577,15 @@ } else #endif #if (NGX_THREADS) - if (ctx->thread_handler) { +#if (NGX_HAVE_PREADV2_NONBLOCK) + + n = ngx_preadv2_file(src->file, dst->pos, (size_t) size, + src->file_pos); +#else + n = NGX_AGAIN; +#endif + if (n == NGX_AGAIN && ctx->thread_handler) { + src->file->thread_task = ctx->thread_task; src->file->thread_handler = ctx->thread_handler; src->file->thread_ctx = ctx->filter_ctx; @@ -589,7 +597,7 @@ return NGX_AGAIN; } - } else + } else if (!ctx->thread_handler && n == NGX_AGAIN) #endif { n = ngx_read_file(src->file, dst->pos, (size_t) size, diff -r 6d2e92acb013 -r f955f9cddd38 src/http/ngx_http_file_cache.c --- a/src/http/ngx_http_file_cache.c Thu Dec 28 12:01:05 2017 +0200 +++ b/src/http/ngx_http_file_cache.c Tue Jan 09 14:54:13 2018 +0300 @@ -699,6 +699,19 @@ #if (NGX_THREADS) if (clcf->aio == NGX_HTTP_AIO_THREADS) { + +#if (NGX_HAVE_PREADV2_NONBLOCK) + + n = ngx_preadv2_file(&c->file, c->buf->pos, c->body_start, 0); + + if (n != NGX_AGAIN) { + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->file.log, 0, + "preadv2 non blocking: \"%s\" - %uz", c->file.name.data, c->body_start); + return n; + } + +#endif + c->file.thread_task = c->thread_task; c->file.thread_handler = ngx_http_cache_thread_handler; c->file.thread_ctx = r; diff -r 6d2e92acb013 -r f955f9cddd38 src/os/unix/ngx_files.c --- a/src/os/unix/ngx_files.c Thu Dec 28 12:01:05 2017 +0200 +++ b/src/os/unix/ngx_files.c Tue Jan 09 14:54:13 2018 +0300 @@ -26,6 +26,68 @@ #endif +#if (NGX_THREADS) +#if (NGX_HAVE_PREADV2_NONBLOCK) + +ngx_uint_t ngx_preadv2_nonblock = 1; + +#endif + +ssize_t +ngx_preadv2_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ +#if (NGX_HAVE_PREADV2_NONBLOCK) + ssize_t n; + struct iovec iovs[1]; + + if (!ngx_preadv2_nonblock) { + return NGX_AGAIN; + } + + iovs[0].iov_base = buf; + iovs[0].iov_len = size; + + n = preadv2(file->fd, iovs, 1, offset, RWF_NOWAIT); + + if (n == -1) { // let's analyze the return code + switch (ngx_errno) { + case EAGAIN: + ngx_log_debug(NGX_LOG_DEBUG_CORE, file->log, 0, + "preadv2() will block on \"%s\"", file->name.data); + return NGX_AGAIN; + case EINVAL: + // Most possible case - not supported RWF_NOWAIT + ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno, + "preadv2() \"%s\" failed RWF_NOWAIT", file->name.data); + ngx_preadv2_nonblock = 0; + return NGX_AGAIN; + default: + return NGX_AGAIN; + + } + } + + // Check if we read partial file + if (((size_t)n < size) && (n < file->info.st_size)) { + // blocked on partial read + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "preadv2() blocked partial on \"%s\" " + "with read size %uz", file->name.data, n); + return NGX_AGAIN; + } + + file->offset += n; + + return n; + +#else + + return NGX_AGAIN; + +#endif +} + +#endif ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) diff -r 6d2e92acb013 -r f955f9cddd38 src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h Thu Dec 28 12:01:05 2017 +0200 +++ b/src/os/unix/ngx_files.h Tue Jan 09 14:54:13 2018 +0300 @@ -389,7 +389,12 @@ off_t offset, ngx_pool_t *pool); ssize_t ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, ngx_pool_t *pool); + +#if (NGX_HAVE_PREADV2_NONBLOCK) +ssize_t ngx_preadv2_file(ngx_file_t *file, u_char *buf, size_t size, + off_t offset); #endif +#endif #endif /* _NGX_FILES_H_INCLUDED_ */ _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel