-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprocessOs.html
More file actions
118 lines (110 loc) · 6.32 KB
/
Copy pathprocessOs.html
File metadata and controls
118 lines (110 loc) · 6.32 KB
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Process OS - pyproc</title>
<link rel="icon" href="../assets/logo.svg">
<meta name="theme-color" content="#0a0f1c">
<link rel="stylesheet" href="demo.css">
<script type="module" src="siteChrome.js"></script>
</head>
<body>
<div class="wrap">
<header class="site">
<a class="logo" href="../"><img class="logoMark" src="../assets/logo.svg" width="22" height="22" alt=""> <span><span class="py">py</span>proc</span></a>
<nav>
<a href="agentSandbox.html">Agent</a>
<a href="machine.html">Machine</a>
<a href="serverDev.html">Server</a>
<a href="speedLab.html">Speed</a>
<a href="terminal.html">Terminal</a>
<a href="processOs.html" class="on">Process OS</a>
<a href="basic.html">Basics</a>
</nav>
<sns-links></sns-links>
</header>
<h1>Fork and real multi-core</h1>
<p class="lead">One interpreter boots, takes a memory snapshot, and forks 4 worker processes from the image.
Each worker is an independent interpreter with its own GIL, so the same job runs on real CPU cores in parallel.
<span class="dim">On header-less hosting the first visit reloads once to unlock SharedArrayBuffer.</span></p>
<div class="panel">
<div class="status" id="st">Spawning workers...</div>
<div class="kv" id="stats" style="display:none">
<div class="k">workers</div><div id="kWorkers"></div>
<div class="k">parallel map</div><div><div class="barWrap"><div class="bar ok" id="barPar"></div></div><span id="kPar"></span></div>
<div class="k">serial</div><div><div class="barWrap"><div class="bar" id="barSer"></div></div><span id="kSer"></span></div>
<div class="k">speedup</div><div id="kSpeed"></div>
<div class="k">results match</div><div id="kSame"></div>
<div class="k">value</div><div id="kVal" class="dim"></div>
</div>
<div class="term" id="out" style="margin-top:.7rem">The job: <span class="ok">def _fn(n): return sum(i*i for i in range(n))</span> over 4 tasks of n=2,000,000.</div>
</div>
<footer class="site">Chromium/Edge. Source: <a href="https://github.com/eddmpython/pyproc">github.com/eddmpython/pyproc</a> · <a href="https://www.npmjs.com/package/pyproc">npm install pyproc</a></footer>
</div>
<script type="module">
import { boot } from "../index.js";
const out = document.getElementById("out"), st = document.getElementById("st");
const el = (id) => document.getElementById(id);
const log = (m) => { out.textContent += "\n" + m; };
// 예제 실행 게이트(?gate): 완주 여부를 하네스에 보고. 사람이 열면 no-op.
const params = new URLSearchParams(location.search);
const gateMode = params.has("gate");
const INDEX = params.get("indexURL") ? new URL(params.get("indexURL"), location.href).href : undefined;
const gateReport = (ok) => { if (!gateMode) return; fetch("/gateReport", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ok: !!ok, checks: [{ name: document.title, pass: !!ok, info: (st.textContent + " | " + out.textContent).slice(-180) }] }) }).catch(() => {}); };
// GitHub Pages처럼 헤더를 못 다는 호스팅에서 SAB를 여는 부트스트랩(실측: swCoiProbe).
// 배포 루트에 pyprocSw.js 사본이 있는 전제(pages 워크플로가 조립). COOP/COEP 서버에선 no-op.
if (!crossOriginIsolated && "serviceWorker" in navigator && !sessionStorage.getItem("pyprocCoiTried")) {
try {
sessionStorage.setItem("pyprocCoiTried", "1");
st.textContent = "Unlocking SharedArrayBuffer (one-time reload)...";
await navigator.serviceWorker.register("../pyprocSw.js?coi=1");
await navigator.serviceWorker.ready;
location.reload();
await new Promise(() => {}); // 새 문서로 넘어갈 때까지 정지
} catch (e) { /* 사본 없는 환경(로컬 직접 서빙 등) -> 그대로 진행 */ }
}
try {
// 머신 핸들에서 프로세스 풀을 연다: proc({ lanes }) = 스냅샷 fork로 N개 독립 인터프리터.
const machine = await boot({ indexURL: INDEX });
let t0 = performance.now();
const os = await machine.proc({ lanes: 4 });
const workers = os.ps().length;
st.textContent = `${workers} workers forked from one snapshot in ${Math.round(performance.now() - t0)}ms`;
const fn = "def _fn(n):\n return sum(i*i for i in range(n))";
const args = [2000000, 2000000, 2000000, 2000000];
log("running parallel map across 4 workers...");
let t = performance.now();
const par = await os.map(fn, args);
const parMs = Math.round(performance.now() - t);
log("running the same 4 tasks on 1 worker (serial baseline)...");
t = performance.now();
// 직렬 기준선: 같은 태스크를 워커 1개에서 exec로 순차 실행(공개 표면만 사용).
const serialPid = os.ps().find((p) => p.state === "ready").pid;
const ser = [];
for (const a of args) ser.push(await os.exec(serialPid, fn, a));
const serMs = Math.round(performance.now() - t);
// 2^53을 넘는 파이썬 int는 BigInt로 온다(정밀도 보존이 올바른 동작).
// JSON.stringify는 BigInt에서 던지므로 비교/표시에 쓰지 않는다(2026-07-12 라이브 실결함).
const same = par.length === ser.length && par.every((v, i) => v === ser[i]);
el("stats").style.display = "grid";
el("kWorkers").textContent = `${workers} (forked from one snapshot)`;
el("kPar").textContent = ` ${parMs}ms`;
el("kSer").textContent = ` ${serMs}ms`;
el("barPar").style.width = `${Math.round((parMs / serMs) * 100)}%`;
el("barSer").style.width = "100%";
el("kSpeed").innerHTML = `<span class="badge ok">${(serMs / parMs).toFixed(2)}x</span>`;
el("kSame").textContent = String(same);
el("kVal").textContent = `${par[0]} (BigInt: exact 64-bit integer, no precision loss)`;
st.textContent = "Done. Same result, real cores.";
log(`process table: ${JSON.stringify(os.ps())}`);
os.terminate();
gateReport(same && workers === 4);
} catch (e) {
st.textContent = "Failed";
log("Error: " + e);
gateReport(false);
}
</script>
</body>
</html>