Skip to content

Commit 72f361e

Browse files
tuananhcursoragent
andcommitted
perf: reduce single transform latency
Avoid worker round-trips for single in-flight transforms while retaining the pool for concurrent requests. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f35c2b8 commit 72f361e

4 files changed

Lines changed: 106 additions & 4 deletions

File tree

index.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,42 @@ if (forceMainThread) {
1919
}
2020
} else {
2121
const { LeanPool } = require('./lean-pool')
22+
const workerFn = require('./worker')
2223
const leanPool = new LeanPool(resolve(__dirname, 'pool-worker.js'), {
2324
maxThreads: poolSize > 0 ? poolSize : undefined,
2425
onRecycleXml: recycleXmlUtf8Buffer,
2526
useSab: useSabIpc,
2627
})
28+
let directTaskActive = false
29+
30+
function directTask(task) {
31+
if (!task.sab) return task
32+
return {
33+
...task,
34+
sab: false,
35+
recycleXml: false,
36+
args: [
37+
typeof task.xmlString === 'string'
38+
? utf8BytesFromJsString(task.xmlString)
39+
: task.args[0],
40+
...task.args.slice(1),
41+
],
42+
}
43+
}
44+
2745
pool = {
2846
run(task, opts) {
47+
// A single request does not benefit from crossing a worker boundary.
48+
// Keep the first in-flight request local, but retain the pool for
49+
// concurrent work so multi-core throughput remains available.
50+
if (!directTaskActive) {
51+
directTaskActive = true
52+
return workerFn(directTask(task))
53+
.then(parseCamaroJson)
54+
.finally(() => {
55+
directTaskActive = false
56+
})
57+
}
2958
return leanPool.run(task, opts)
3059
},
3160
destroy() {

lean-pool.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const os = require('os')
44
const { Worker } = require('worker_threads')
5+
const { parseCamaroJson } = require('./json-parse')
56
const {
67
sabEnabled,
78
createSabChannel,
@@ -10,6 +11,7 @@ const {
1011
readResultFromSab,
1112
stripXmlFromTask,
1213
CTRL,
14+
STATE,
1315
fnCode,
1416
} = require('./sab-ipc')
1517

@@ -42,6 +44,10 @@ class LeanWorker {
4244
this.idleTimeout = idleTimeout
4345
this.onRecycleXml = onRecycleXml
4446
this.useSab = useSab
47+
this.useSabWait =
48+
useSab &&
49+
typeof Atomics.waitAsync === 'function' &&
50+
typeof Atomics.notify === 'function'
4551
this.sabChannel = useSab ? createSabChannel() : null
4652
this.worker = null
4753
this.seq = 0
@@ -59,7 +65,7 @@ class LeanWorker {
5965
this.sabChannel != null ? { workerData: { sab: this.sabChannel } } : undefined
6066
const worker = new Worker(this.filename, workerOpts)
6167
worker.unref()
62-
worker.on('message', ({ id, result, error, xmlBuf, sab }) => {
68+
worker.on('message', ({ id, result, error, xmlBuf, sab, raw }) => {
6369
if (xmlBuf && this.onRecycleXml) this.onRecycleXml(xmlBuf)
6470
const p = this.pending.get(id)
6571
this.pending.delete(id)
@@ -72,6 +78,12 @@ class LeanWorker {
7278
} catch (err) {
7379
p.reject(err)
7480
}
81+
} else if (raw) {
82+
try {
83+
p.resolve(parseCamaroJson(result))
84+
} catch (err) {
85+
p.reject(err)
86+
}
7587
} else {
7688
p.resolve(result)
7789
}
@@ -105,6 +117,35 @@ class LeanWorker {
105117
this.idleTimer.unref()
106118
}
107119

120+
settleSabResult(id) {
121+
if (
122+
!this.sabChannel ||
123+
Atomics.load(this.sabChannel.control, CTRL.STATE) !== STATE.DONE
124+
) {
125+
return
126+
}
127+
const p = this.pending.get(id)
128+
this.pending.delete(id)
129+
if (!p) return
130+
try {
131+
p.resolve(readResultFromSab(this.sabChannel))
132+
} catch (err) {
133+
p.reject(err)
134+
}
135+
if (this.pending.size === 0) this.scheduleIdleShutdown()
136+
}
137+
138+
waitForSabResult(id) {
139+
const channel = this.sabChannel
140+
if (!channel) return
141+
const waiter = Atomics.waitAsync(channel.control, CTRL.STATE, STATE.BUSY)
142+
if (waiter.async) {
143+
waiter.value.then(() => this.settleSabResult(id))
144+
} else {
145+
this.settleSabResult(id)
146+
}
147+
}
148+
108149
run(task, opts = {}) {
109150
this.ensureWorker()
110151
this.clearIdleTimer()
@@ -124,11 +165,16 @@ class LeanWorker {
124165
}
125166
return new Promise((resolve, reject) => {
126167
this.pending.set(id, { resolve, reject })
168+
if (this.useSabWait) {
169+
Atomics.store(this.sabChannel.control, CTRL.STATE, STATE.BUSY)
170+
}
127171
this.worker.postMessage({
128172
id,
129173
sab: true,
174+
waitForSab: this.useSabWait,
130175
task: stripXmlFromTask(task),
131176
})
177+
if (this.useSabWait) this.waitForSabResult(id)
132178
})
133179
}
134180

pool-worker.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { CTRL, STATE, writeResultToSab } = require('./sab-ipc')
77

88
const sabChannel = workerData && workerData.sab ? workerData.sab : null
99

10-
parentPort.on('message', async ({ id, task, sab: useSab }) => {
10+
parentPort.on('message', async ({ id, task, sab: useSab, waitForSab }) => {
1111
try {
1212
if (useSab && sabChannel) {
1313
const xmlLen = Atomics.load(sabChannel.control, CTRL.XML_LEN)
@@ -18,9 +18,17 @@ parentPort.on('message', async ({ id, task, sab: useSab }) => {
1818

1919
Atomics.store(sabChannel.control, CTRL.STATE, STATE.BUSY)
2020
const raw = await workerFn(localTask)
21-
writeResultToSab(sabChannel, raw)
21+
if (waitForSab) {
22+
writeResultToSab(sabChannel, raw)
23+
Atomics.store(sabChannel.control, CTRL.STATE, STATE.DONE)
24+
Atomics.notify(sabChannel.control, CTRL.STATE, 1)
25+
return
26+
}
2227
Atomics.store(sabChannel.control, CTRL.STATE, STATE.DONE)
23-
parentPort.postMessage({ id, sab: true })
28+
// JSON strings are cheaply cloned by V8. Avoiding the explicit
29+
// UTF-8 encode into SAB and decode on the parent removes two
30+
// copies from the latency-sensitive single-request path.
31+
parentPort.postMessage({ id, raw: true, result: raw })
2432
return
2533
}
2634

@@ -43,6 +51,7 @@ parentPort.on('message', async ({ id, task, sab: useSab }) => {
4351
} catch (err) {
4452
if (useSab && sabChannel) {
4553
Atomics.store(sabChannel.control, CTRL.STATE, STATE.ERROR)
54+
Atomics.notify(sabChannel.control, CTRL.STATE, 1)
4655
}
4756
parentPort.postMessage({
4857
id,

test/worker-result-ipc.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict'
2+
3+
const t = require('tape')
4+
const { transform } = require('../')
5+
6+
t.test('worker result IPC preserves JSON and NaN values', async (t) => {
7+
const [, result] = await Promise.all([
8+
transform('<warmup/>', { value: 'warmup' }),
9+
transform('<root><item>one</item><item>two</item></root>', {
10+
items: ['root/item', '.'],
11+
missing: 'number(root/@missing)',
12+
}),
13+
])
14+
15+
t.deepEqual(result.items, ['one', 'two'], 'parses the worker JSON result')
16+
t.ok(Number.isNaN(result.missing), 'restores NaN from the worker result')
17+
t.end()
18+
})

0 commit comments

Comments
 (0)