Spaces:
Running
Running
File size: 5,489 Bytes
050141c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | /* tools/lib/pool.mjs β ν λμ κΈ°κ³μμ Nκ° μμ νλ‘μΈμ€λ‘ μμ
μ νλ €λ³΄λ΄λ ν.
μ΄ μ μ₯μμ κ³κΈ°νμ μ λΆ (μ¬λ‘― Γ μλ Γ μ) κ°μ **μλ‘ λ
λ¦½μΈ μμ
**μ μμ°¨λ‘ λλ¦°λ€.
β€ μ¦λͺ
κΈ°κ° κ·Έ νκ³μ λ¨Όμ λΆλͺνλ€ β μ νλμ μ΅λ 150μ΄, μ¬λ‘― 32κ°λ©΄ νλ£¨κ° κ°λ€.
μ¬κΈ°μ λ³λ ¬νμ λ¨μλ **μμ
**μ΄μ§ νμΌμ΄ μλλ€. μμ
λ§λ€ νλ‘μΈμ€λ₯Ό μλ‘ λμ°λ©΄
engine.js(1.5MB) λ₯Ό λ§€λ² λ€μ μ½μΌλ―λ‘, μ컀λ **μμ**μ΄κ³ μμ
λ§ νλ €λ³΄λΈλ€.
νλ‘ν μ½μ stdout μ JSON ν μ€μ΄λ€:
λΆλͺ¨ -> μ컀 {"id": 7, "job": {...}}
μ컀 -> λΆλͺ¨ {"id": 7, "ok": true, "result": {...}} λλ {"id":7,"ok":false,"err":"..."}
μ컀λ **stdout μ κ·Έ μΈ μ무κ²λ μ°λ©΄ μ λλ€**. μ§ν λ‘κ·Έλ stderr λ‘ λ³΄λΈλ€
(λΆλͺ¨κ° κ·Έλλ‘ νλ €λ³΄λ΄λ―λ‘ μ¬λμ κ·Έλλ‘ μ½λλ€).
μκ° νκ³λ₯Ό κ°μ§ μμ
μ μ£Όμ (measured, 2026-08-01): λ³λ ¬λ‘ λ리면 κ°μ μμ
μ΄ λ μ€λ
κ±Έλ¦°λ€. κ·Έλμ μ΄ μ μ₯μμ νμ μ **μν νκ³**(κ²°μ μ )λ₯Ό 1μμλ‘ λκ³ μκ° νκ³λ
μμ λ§μΌλ‘λ§ μ΄λ€. μ΄λ νκ³μ κ±Έλ Έλμ§ κ²°κ³Όμ μ λ κ²μ μ컀μ μ±
μμ΄λ€ β κ·ΈλμΌ
"8κ°λ‘ λλ Έλλ λ―Ένμ μ΄ λμλ€"κ° λ°κ²¬μ΄ μλλΌ λΆνλΌλ κ²μ ꡬλ³ν μ μλ€. */
import { spawn } from 'node:child_process';
import os from 'node:os';
export function poolSize(req) {
const n = Number(req) || 0;
if (n > 0) return n;
return Math.max(2, Math.min(8, os.cpus().length - 2));
}
/* runPool(jobs, workerPath, opts) -> Promise<Array<result|null>>
κ²°κ³Όλ **μ
λ ₯ μμ**λ‘ λμμ¨λ€(μλ£ μμκ° μλλ€ β λ³΄κ³ κ° μ€ν μμμ νλ€λ¦¬λ©΄
κ°μ λͺ
λ Ήμ΄ λ€λ₯Έ νλ₯Ό λΈλ€). opts.onDone(i, result) μ μλ£ μ¦μ λΆλ¦°λ€(μ§ν νμμ©).
μμ»€κ° μ£½μΌλ©΄ κ·Έ μμ
μ null μ΄κ³ , λ¨μ μμ
μ λ€λ₯Έ μμ»€κ° κ³μ κ°μ Έκ°λ€. */
export async function runPool(jobs, workerPath, opts = {}) {
const N = Math.min(poolSize(opts.concurrency), Math.max(1, jobs.length));
const results = new Array(jobs.length).fill(null);
let cursor = 0;
let done = 0;
const kids = [];
const startKid = () => {
const kid = spawn(process.execPath, [workerPath, ...(opts.argv || [])], {
cwd: opts.cwd || process.cwd(),
env: { ...process.env, ...(opts.env || {}) },
stdio: ['pipe', 'pipe', 'inherit'],
});
const st = { kid, busy: -1, buf: '' };
kid.stdout.on('data', (d) => {
st.buf += d;
let nl;
while ((nl = st.buf.indexOf('\n')) !== -1) {
const line = st.buf.slice(0, nl); st.buf = st.buf.slice(nl + 1);
if (!line.trim()) continue;
let msg;
try { msg = JSON.parse(line); }
catch { process.stderr.write(`[pool] μμ»€κ° JSON μλ μ€μ stdout μ μΌλ€: ${line}\n`); continue; }
results[msg.id] = msg.ok ? msg.result : { error: msg.err || 'unknown' };
done++;
if (opts.onDone) opts.onDone(msg.id, results[msg.id], done, jobs.length);
st.busy = -1;
feed(st);
}
});
kid.on('exit', (code) => {
if (st.busy >= 0) { // μ£½μΌλ©΄μ λ¬Όκ³ κ° μμ
μ μ€ν¨λ‘ μ λλ€
results[st.busy] = { error: `worker exited (${code})` };
done++;
st.busy = -1;
}
st.dead = true;
// μ΄μ μλ μμ»€κ° νλλ μλλ° μμ
μ΄ λ¨μμΌλ©΄ λ λͺ» λλ€ β λ¨μ κ²μ null λ‘ λλλ€.
if (kids.every(k => k.dead) && cursor < jobs.length) {
for (let i = cursor; i < jobs.length; i++) { results[i] = { error: 'no worker' }; done++; }
cursor = jobs.length;
}
settleMaybe();
});
return st;
};
let resolveAll;
const all = new Promise((res) => { resolveAll = res; });
const settleMaybe = () => {
if (done >= jobs.length) {
for (const k of kids) if (!k.dead) k.kid.stdin.end();
resolveAll(results);
}
};
const feed = (st) => {
if (st.dead) return;
if (cursor >= jobs.length) { st.kid.stdin.end(); settleMaybe(); return; }
const i = cursor++;
st.busy = i;
st.kid.stdin.write(JSON.stringify({ id: i, job: jobs[i] }) + '\n');
};
for (let k = 0; k < N; k++) kids.push(startKid());
for (const st of kids) feed(st);
const out = await all;
return out;
}
/* μ컀 μͺ½ ν¬νΌ β stdin μ JSON μ€μ λ°μ handler μ λκΈ°κ³ κ²°κ³Όλ₯Ό ν μ€λ‘ λλ €μ€λ€.
handler κ° λμ§λ©΄ κ·Έ μμ
λ§ μ€ν¨λ‘ λμκ°κ³ μ컀λ κ³μ μ°λ€(ν μ¬λ‘―μ λ²κ·Έκ° λλ¨Έμ§
31κ°μ μΈ‘μ μ λͺ» μ§μ°κ²). */
export function serveWorker(handler) {
let buf = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', async (d) => {
buf += d;
let nl;
const lines = [];
while ((nl = buf.indexOf('\n')) !== -1) { lines.push(buf.slice(0, nl)); buf = buf.slice(nl + 1); }
for (const line of lines) {
if (!line.trim()) continue;
const { id, job } = JSON.parse(line);
try {
const result = await handler(job);
process.stdout.write(JSON.stringify({ id, ok: true, result }) + '\n');
} catch (e) {
process.stdout.write(JSON.stringify({ id, ok: false, err: (e && e.stack) || String(e) }) + '\n');
}
}
});
process.stdin.on('end', () => process.exit(0));
}
|