Skip to content

Commit b277776

Browse files
LinkgoronMylesBorins
authored andcommitted
fs: improve fsPromises readFile performance
Improve the fsPromises readFile performance by allocating only one buffer, when size is known, increase the size of the readbuffer chunks, and dont read more data if size bytes have been read Refs: #37583 PR-URL: #37608 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 180e3f6 commit b277776

2 files changed

Lines changed: 45 additions & 20 deletions

File tree

‎lib/internal/fs/promises.js‎

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
// See https://github.com/libuv/libuv/pull/1501.
55
constkIoMaxLength=2**31-1;
66

7-
// Note: This is different from kReadFileBufferLength used for non-promisified
8-
// fs.readFile.
9-
constkReadFileMaxChunkSize=2**14;
7+
constkReadFileBufferLength=512*1024;
8+
constkReadFileUnknownBufferLength=64*1024;
109
constkWriteFileMaxChunkSize=2**14;
1110

1211
const{
@@ -316,25 +315,46 @@ async function readFileHandle(filehandle, options) {
316315
if(size>kIoMaxLength)
317316
thrownewERR_FS_FILE_TOO_LARGE(size);
318317

319-
constchunks=[];
320-
letisFirstChunk=true;
321-
constfirstChunkSize=size===0 ? kReadFileMaxChunkSize : size;
322-
constchunkSize=MathMin(firstChunkSize,kReadFileMaxChunkSize);
323318
letendOfFile=false;
319+
lettotalRead=0;
320+
constnoSize=size===0;
321+
constbuffers=[];
322+
constfullBuffer=noSize ? undefined : Buffer.allocUnsafeSlow(size);
324323
do{
325324
if(signal?.aborted){
326325
throwlazyDOMException('The operation was aborted','AbortError');
327326
}
328-
constbuf=Buffer.alloc(isFirstChunk ? firstChunkSize : chunkSize);
329-
const{ bytesRead, buffer }=
330-
awaitread(filehandle,buf,0,buf.length,-1);
331-
endOfFile=bytesRead===0;
332-
if(bytesRead>0)
333-
ArrayPrototypePush(chunks,buffer.slice(0,bytesRead));
334-
isFirstChunk=false;
327+
letbuffer;
328+
letoffset;
329+
letlength;
330+
if(noSize){
331+
buffer=Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength);
332+
offset=0;
333+
length=kReadFileUnknownBufferLength;
334+
}else{
335+
buffer=fullBuffer;
336+
offset=totalRead;
337+
length=MathMin(size-totalRead,kReadFileBufferLength);
338+
}
339+
340+
constbytesRead=(awaitbinding.read(filehandle.fd,buffer,offset,
341+
length,-1,kUsePromises))||0;
342+
totalRead+=bytesRead;
343+
endOfFile=bytesRead===0||totalRead===size;
344+
if(noSize&&bytesRead>0){
345+
constisBufferFull=bytesRead===kReadFileUnknownBufferLength;
346+
constchunkBuffer=isBufferFull ? buffer : buffer.slice(0,bytesRead);
347+
ArrayPrototypePush(buffers,chunkBuffer);
348+
}
335349
}while(!endOfFile);
336350

337-
constresult=chunks.length===1 ? chunks[0] : Buffer.concat(chunks);
351+
letresult;
352+
if(size>0){
353+
result=totalRead===size ? fullBuffer : fullBuffer.slice(0,totalRead);
354+
}else{
355+
result=buffers.length===1 ? buffers[0] : Buffer.concat(buffers,
356+
totalRead);
357+
}
338358

339359
returnoptions.encoding ? result.toString(options.encoding) : result;
340360
}

‎test/parallel/test-fs-promises-file-handle-readFile.js‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const {
1010
open,
1111
readFile,
1212
writeFile,
13-
truncate
13+
truncate,
1414
}=fs.promises;
1515
constpath=require('path');
1616
consttmpdir=require('../common/tmpdir');
@@ -64,6 +64,7 @@ async function doReadAndCancel() {
6464
awaitassert.rejects(readFile(fileHandle,{ signal }),{
6565
name: 'AbortError'
6666
});
67+
awaitfileHandle.close();
6768
}
6869

6970
// Signal aborted on first tick
@@ -74,10 +75,11 @@ async function doReadAndCancel() {
7475
fs.writeFileSync(filePathForHandle,buffer);
7576
constcontroller=newAbortController();
7677
const{ signal }=controller;
77-
tick(1,()=>controller.abort());
78+
process.nextTick(()=>controller.abort());
7879
awaitassert.rejects(readFile(fileHandle,{ signal }),{
7980
name: 'AbortError'
80-
});
81+
},'tick-0');
82+
awaitfileHandle.close();
8183
}
8284

8385
// Signal aborted right before buffer read
@@ -90,10 +92,12 @@ async function doReadAndCancel() {
9092

9193
constcontroller=newAbortController();
9294
const{ signal }=controller;
93-
tick(2,()=>controller.abort());
95+
tick(1,()=>controller.abort());
9496
awaitassert.rejects(fileHandle.readFile({ signal,encoding: 'utf8'}),{
9597
name: 'AbortError'
96-
});
98+
},'tick-1');
99+
100+
awaitfileHandle.close();
97101
}
98102

99103
// Validate file size is within range for reading
@@ -111,6 +115,7 @@ async function doReadAndCancel() {
111115
name: 'RangeError',
112116
code: 'ERR_FS_FILE_TOO_LARGE'
113117
});
118+
awaitfileHandle.close();
114119
}
115120
}
116121

0 commit comments

Comments
 (0)