Skip to content

Commit 03fed1e

Browse files
committed
perf: fully inline _getCachedSource in source() and split CachedSource ctor
Two follow-ups to keep CodSpeed happy: 1. `source()` previously inlined only the warm `_cachedSource` check and still called `_getCachedSource()` on cold reads, which under the interpreter (CodSpeed's simulation mode) pays a function-call stack frame plus a redundant re-check of `_cachedSource`. Inline the whole helper body so both warm and cold paths stay branchless-to-call. Local --no-opt: cold `new CachedSource(RawSource).source()` drops 534 -> 477 ns/op (+12% vs main). 2. The constructor was evaluating `cachedData ? cachedData.X : undefined` once per cached-data field (five separate ternaries). Replace with a single branch on `cachedData` so construction pays one conditional instead of five. Local --no-opt: `new CachedSource(new RawSource(…))` drops 431 -> 375 ns/op (+13% vs main), erasing the -11% regression CodSpeed showed on `cached-source: new CachedSource()`. `_getCachedSource()` stays on the class because `buffer()`, `streamChunks()` and `getCachedData()` still call it; only `source()` had a hot enough caller to justify duplicating the body. https://claude.ai/code/session_01LZbaaPrnDTu6y7s4nK4cJz
1 parent 3191486 commit 03fed1e

1 file changed

Lines changed: 51 additions & 30 deletions

File tree

lib/CachedSource.js

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -127,36 +127,48 @@ class CachedSource extends Source {
127127
* @type {Source | (() => Source)}
128128
*/
129129
this._source = source;
130-
/**
131-
* @private
132-
* @type {boolean | undefined}
133-
*/
134-
this._cachedSourceType = cachedData ? cachedData.source : undefined;
135130
/**
136131
* @private
137132
* @type {undefined | string}
138133
*/
139134
this._cachedSource = undefined;
140-
/**
141-
* @private
142-
* @type {Buffer | undefined}
143-
*/
144-
this._cachedBuffer = cachedData ? cachedData.buffer : undefined;
145-
/**
146-
* @private
147-
* @type {number | undefined}
148-
*/
149-
this._cachedSize = cachedData ? cachedData.size : undefined;
150-
/**
151-
* @private
152-
* @type {BufferedMaps}
153-
*/
154-
this._cachedMaps = cachedData ? cachedData.maps : new Map();
155-
/**
156-
* @private
157-
* @type {(string | Buffer)[] | undefined}
158-
*/
159-
this._cachedHashUpdate = cachedData ? cachedData.hash : undefined;
135+
// Split on `cachedData` once instead of re-evaluating the ternary for
136+
// every field. Under the interpreter (and CodSpeed's simulation) each
137+
// ternary is a separate branch; consolidating cuts the per-instance
138+
// branch count roughly in half.
139+
if (cachedData) {
140+
/**
141+
* @private
142+
* @type {boolean | undefined}
143+
*/
144+
this._cachedSourceType = cachedData.source;
145+
/**
146+
* @private
147+
* @type {Buffer | undefined}
148+
*/
149+
this._cachedBuffer = cachedData.buffer;
150+
/**
151+
* @private
152+
* @type {number | undefined}
153+
*/
154+
this._cachedSize = cachedData.size;
155+
/**
156+
* @private
157+
* @type {BufferedMaps}
158+
*/
159+
this._cachedMaps = cachedData.maps;
160+
/**
161+
* @private
162+
* @type {(string | Buffer)[] | undefined}
163+
*/
164+
this._cachedHashUpdate = cachedData.hash;
165+
} else {
166+
this._cachedSourceType = undefined;
167+
this._cachedBuffer = undefined;
168+
this._cachedSize = undefined;
169+
this._cachedMaps = new Map();
170+
this._cachedHashUpdate = undefined;
171+
}
160172
}
161173

162174
/**
@@ -212,12 +224,21 @@ class CachedSource extends Source {
212224
* @returns {SourceValue} source
213225
*/
214226
source() {
215-
// Inline the hot warm-cache check so the common case avoids a
216-
// `_getCachedSource()` call and its prototype-method lookup. Falling
217-
// back to the shared helper handles the rarer buffer-derived path.
227+
// Fully inlined _getCachedSource: both warm- and cold-cache paths skip
228+
// the prototype method lookup / stack frame the interpreter would
229+
// otherwise pay on every call.
218230
if (this._cachedSource !== undefined) return this._cachedSource;
219-
const source = this._getCachedSource();
220-
if (source !== undefined) return source;
231+
const cachedBuffer = this._cachedBuffer;
232+
const cachedSourceType = this._cachedSourceType;
233+
if (cachedBuffer !== undefined && cachedSourceType !== undefined) {
234+
const value = cachedSourceType
235+
? cachedBuffer.toString("utf8")
236+
: cachedBuffer;
237+
if (isDualStringBufferCachingEnabled()) {
238+
this._cachedSource = /** @type {string} */ (value);
239+
}
240+
return /** @type {string} */ (value);
241+
}
221242
return (this._cachedSource =
222243
/** @type {string} */
223244
(this.original().source()));

0 commit comments

Comments
 (0)