From eb2d4161f56efa7d82a6364734c674757f1f3ea4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 3 Apr 2019 02:58:31 +0200 Subject: [PATCH] fs: improve readFile performance This increases the maximum buffer size per read to 512kb when using `fs.readFile`. This is important to improve the read performance for bigger files. PR-URL: https://github.com/nodejs/node/pull/27063 Refs: https://github.com/nodejs/node/issues/25741 Reviewed-By: Matteo Collina Reviewed-By: James M Snell Reviewed-By: Jamie Davis --- lib/fs.js | 12 ++++------ lib/internal/fs/read_file_context.js | 34 ++++++++++++++++------------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 9761dd2bb1defa..7f7a465a4c1673 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -266,19 +266,17 @@ function readFileAfterStat(err, stats) { const size = context.size = isFileType(stats, S_IFREG) ? stats[8] : 0; - if (size === 0) { - context.buffers = []; - context.read(); - return; - } - if (size > kMaxLength) { err = new ERR_FS_FILE_TOO_LARGE(size); return context.close(err); } try { - context.buffer = Buffer.allocUnsafeSlow(size); + if (size === 0) { + context.buffers = []; + } else { + context.buffer = Buffer.allocUnsafeSlow(size); + } } catch (err) { return context.close(err); } diff --git a/lib/internal/fs/read_file_context.js b/lib/internal/fs/read_file_context.js index a4e7a3563bf821..e1de8fc7cda2c3 100644 --- a/lib/internal/fs/read_file_context.js +++ b/lib/internal/fs/read_file_context.js @@ -6,7 +6,14 @@ const { Buffer } = require('buffer'); const { FSReqCallback, close, read } = internalBinding('fs'); -const kReadFileBufferLength = 8 * 1024; +// Use 64kb in case the file type is not a regular file and thus do not know the +// actual file size. Increasing the value further results in more frequent over +// allocation for small files and consumes CPU time and memory that should be +// used else wise. +// Use up to 512kb per read otherwise to partition reading big files to prevent +// blocking other threads in case the available threads are all in use. +const kReadFileUnknownBufferLength = 64 * 1024; +const kReadFileBufferLength = 512 * 1024; function readFileAfterRead(err, bytesRead) { const context = this.context; @@ -14,19 +21,17 @@ function readFileAfterRead(err, bytesRead) { if (err) return context.close(err); - if (bytesRead === 0) - return context.close(); - context.pos += bytesRead; - if (context.size !== 0) { - if (context.pos === context.size) - context.close(); - else - context.read(); + if (context.pos === context.size || bytesRead === 0) { + context.close(); } else { - // Unknown size, just read until we don't get bytes. - context.buffers.push(context.buffer.slice(0, bytesRead)); + if (context.size === 0) { + // Unknown size, just read until we don't get bytes. + const buffer = bytesRead === kReadFileUnknownBufferLength ? + context.buffer : context.buffer.slice(0, bytesRead); + context.buffers.push(buffer); + } context.read(); } } @@ -60,7 +65,7 @@ class ReadFileContext { constructor(callback, encoding) { this.fd = undefined; this.isUserFd = undefined; - this.size = undefined; + this.size = 0; this.callback = callback; this.buffers = null; this.buffer = null; @@ -75,9 +80,10 @@ class ReadFileContext { let length; if (this.size === 0) { - buffer = this.buffer = Buffer.allocUnsafeSlow(kReadFileBufferLength); + buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength); offset = 0; - length = kReadFileBufferLength; + length = kReadFileUnknownBufferLength; + this.buffer = buffer; } else { buffer = this.buffer; offset = this.pos;