AgentnessArenav2 / tools /lib /pool.mjs
irregular6612's picture
deploy: Space λ₯Ό main 5688e82 트리둜 λ§žμΆ˜λ‹€
050141c
Raw History Blame Contribute Delete
5.49 kB
/* 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));
}