From f0907a01f8cb6fbaf707dccc1eb70e5ea3b714eb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 14 Feb 2017 15:37:08 -0800 Subject: [PATCH] Fix #19993 - peekLF and peekSingleByteChar apparently aren't inlined, hurting the perf in this very hot loop. The perf regression is partly from function calls, and partly from the function depending on a closure. It also seems like there might be more gains to be had if I refactor this file to not depend so heavily on closures and nested anonymous functions. --- .../search/node/worker/searchWorker.ts | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/services/search/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/worker/searchWorker.ts index c51657425f3..aed8cff2f91 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorker.ts +++ b/src/vs/workbench/services/search/node/worker/searchWorker.ts @@ -170,12 +170,12 @@ export class SearchWorkerEngine { return resolve(null); } - let buffer = new Buffer(options.bufferLength); + const buffer = new Buffer(options.bufferLength); let pos: number; let i: number; let line = ''; let lineNumber = 0; - let lastBufferHadTraillingCR = false; + let lastBufferHadTrailingCR = false; const decodeBuffer = (buffer: NodeBuffer, start: number, end: number): string => { if (options.encoding === UTF8 || options.encoding === UTF8_with_bom) { @@ -203,6 +203,9 @@ export class SearchWorkerEngine { return clb(error); // return early if canceled or limit reached or no more bytes to read } + let crlfCharSize = 1; + let crBytes = [CR]; + let lfBytes = [LF]; pos = 0; i = 0; @@ -228,52 +231,49 @@ export class SearchWorkerEngine { options.encoding = UTF16le; break; } - } - - // when we are running with UTF16le/be, LF and CR are encoded as - // two bytes, like 0A 00 (LF) / 0D 00 (CR) for LE or flipped around - // for BE. We need to account for this when splitting the buffer into - // newlines, and when detecting a CRLF combo. - let byteOffsetMultiplier = 1; - if (options.encoding === UTF16le || options.encoding === UTF16be) { - byteOffsetMultiplier = 2; - } - const peekSingleByteChar = (char: number, offset = 0) => { - const from = i + offset; + // when we are running with UTF16le/be, LF and CR are encoded as + // two bytes, like 0A 00 (LF) / 0D 00 (CR) for LE or flipped around + // for BE. We need to account for this when splitting the buffer into + // newlines, and when detecting a CRLF combo. if (options.encoding === UTF16le) { - return buffer[from] === char && buffer[from + 1] === 0x00; + crlfCharSize = 2; + crBytes = [CR, 0x00]; + lfBytes = [LF, 0x00]; } else if (options.encoding === UTF16be) { - return buffer[from] === 0x00 && buffer[from + 1] === char; - } else { - return buffer[from] === char; + crlfCharSize = 2; + crBytes = [0x00, CR]; + lfBytes = [0x00, LF]; } - }; - const peekLF = (offset?: number) => peekSingleByteChar(LF, offset); - const peekCR = (offset?: number) => peekSingleByteChar(CR, offset); + } - if (lastBufferHadTraillingCR) { - if (peekLF()) { - lineFinished(1 * byteOffsetMultiplier); + if (lastBufferHadTrailingCR) { + if (buffer[i] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + 1] === lfBytes[1])) { + lineFinished(1 * crlfCharSize); i++; } else { lineFinished(0); } - lastBufferHadTraillingCR = false; + lastBufferHadTrailingCR = false; } + /** + * This loop executes for every byte of every file in the workspace - it is highly performance-sensitive! + * Hence the duplication in reading the buffer to avoid a function call. Previously a function call was not + * being inlined by V8. + */ for (; i < bytesRead; ++i) { - if (peekLF()) { - lineFinished(1 * byteOffsetMultiplier); - } else if (peekCR()) { // CR (Carriage Return) - if (i + byteOffsetMultiplier === bytesRead) { - lastBufferHadTraillingCR = true; - } else if (peekLF(1 * byteOffsetMultiplier)) { - lineFinished(2 * byteOffsetMultiplier); - i += 2 * byteOffsetMultiplier - 1; + if (buffer[i] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + 1] === lfBytes[1])) { + lineFinished(1 * crlfCharSize); + } else if (buffer[i] === crBytes[0] && (crBytes.length === 1 || buffer[i + 1] === crBytes[1])) { // CR (Carriage Return) + if (i + crlfCharSize === bytesRead) { + lastBufferHadTrailingCR = true; + } else if (buffer[i + crlfCharSize] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + crlfCharSize + 1] === lfBytes[1])) { + lineFinished(2 * crlfCharSize); + i += 2 * crlfCharSize - 1; } else { - lineFinished(1 * byteOffsetMultiplier); + lineFinished(1 * crlfCharSize); } } } -- GitLab