Files
2nd/10_Wiki/Topics/Programming & Language/bitECS와 SharedArrayBuffer를 결합한 멀티스레드 고성능 아키텍처.md
T
2026-05-10 22:08:15 +09:00

229 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: wiki-2026-0508-bitecs와-sharedarraybuffer를-결합한-멀
title: bitECS와 SharedArrayBuffer를 결합한 멀티스레드 고성능 아키텍처
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [bitECS multithreaded, SAB ECS, ECS parallel, bitECS SharedArrayBuffer]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [ecs, bitecs, sharedarraybuffer, parallel, gamedev, browser]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: TypeScript
framework: bitECS + Web Worker
---
# bitECS와 SharedArrayBuffer를 결합한 멀티스레드 고성능 아키텍처
## 매 한 줄
> **"매 bitECS Component = TypedArray on SAB"**. bitECS의 매 SoA layout 은 매 SharedArrayBuffer 와 매 자연스럽게 결합 — 매 component data 가 매 worker 에서 매 zero-copy 공유. 매 system 분산 + 매 stripe partition 으로 매 100k+ entity 를 매 60fps 에서 매 simulate 가능.
## 매 핵심
### 매 bitECS 구조
- **Entity**: 32-bit integer ID.
- **Component**: 매 TypedArray (Float32Array, Int32Array 등) per field, 매 indexed by entity ID.
- **System**: query → process loop.
- 매 SoA (Struct of Arrays) → 매 cache-friendly + SIMD-friendly.
### 매 SAB 통합 핵심
- bitECS 의 매 component 매 backing TypedArray 의 매 buffer 를 매 SAB 로 교체.
- 매 worker 에서 매 동일 component 에 매 동시 접근 가능.
- 매 system 별로 매 worker 분배 — 또는 매 entity range 별 분배.
### 매 partition strategy
1. **System-level**: physics worker, AI worker, render worker 매 별도.
2. **Entity-level**: entity ID 매 mod N → worker N 개에 분산.
3. **Stripe-level**: contiguous range, cache-friendly.
4. **Hybrid**: 매 read-heavy system 매 모든 worker, write 매 owner worker 만.
### 매 응용
1. **Boid/flocking**: 100k boids 매 4 worker 에서 stripe.
2. **Particle system**: SAB Float32 의 매 position/velocity, 매 worker 별 batch.
3. **Pathfinding**: A* 매 worker pool, 매 result 매 SAB 에 적재.
4. **Voxel chunk update**: 매 chunk 별 worker, 매 mesh build 매 OffscreenCanvas worker.
## 💻 패턴
### Pattern 1: bitECS component → SAB-backed
```typescript
// main.ts
import { createWorld, defineComponent, Types } from "bitecs";
const MAX_ENTITIES = 100_000;
const sab = new SharedArrayBuffer(MAX_ENTITIES * 3 * 4 * 2); // pos+vel, f32
// SAB 위에 매 직접 component 정의 (bitECS 0.4+ allows custom buffer)
const Position = {
x: new Float32Array(sab, 0, MAX_ENTITIES),
y: new Float32Array(sab, MAX_ENTITIES * 4, MAX_ENTITIES),
};
const Velocity = {
x: new Float32Array(sab, MAX_ENTITIES * 8, MAX_ENTITIES),
y: new Float32Array(sab, MAX_ENTITIES * 12, MAX_ENTITIES),
};
const world = createWorld();
// ... addEntity, addComponent (writes go into SAB views)
```
### Pattern 2: worker spawn + SAB share
```typescript
const N_WORKERS = 4;
const workers = Array.from({ length: N_WORKERS }, (_, id) => {
const w = new Worker("./physics-worker.ts", { type: "module" });
w.postMessage({ sab, workerId: id, n: N_WORKERS, maxEntities: MAX_ENTITIES });
return w;
});
```
### Pattern 3: physics system in worker
```typescript
// physics-worker.ts
self.onmessage = ({ data: { sab, workerId, n, maxEntities } }) => {
const px = new Float32Array(sab, 0, maxEntities);
const py = new Float32Array(sab, maxEntities * 4, maxEntities);
const vx = new Float32Array(sab, maxEntities * 8, maxEntities);
const vy = new Float32Array(sab, maxEntities * 12, maxEntities);
const stripe = Math.ceil(maxEntities / n);
const start = workerId * stripe;
const end = Math.min(start + stripe, maxEntities);
setInterval(() => {
const dt = 0.016;
for (let i = start; i < end; i++) {
px[i] += vx[i] * dt;
py[i] += vy[i] * dt;
}
}, 16);
};
```
### Pattern 4: barrier-synchronized frame
```typescript
// shared int32 frame counter
const sync = new Int32Array(sab, syncOffset, 4);
// sync[0] = workersReady, sync[1] = generation
function workerStep() {
// do work
doStripe();
// barrier
if (Atomics.add(sync, 0, 1) + 1 === N_WORKERS) {
Atomics.store(sync, 0, 0);
Atomics.add(sync, 1, 1);
Atomics.notify(sync, 1, N_WORKERS - 1);
} else {
const gen = Atomics.load(sync, 1);
Atomics.wait(sync, 1, gen);
}
}
```
### Pattern 5: read-only system on all workers
```typescript
// AI system: read pos/vel of others, write own intent → no contention
function aiSystem(workerId: number) {
for (let i = start; i < end; i++) {
let nearestX = 0, nearestY = 0, nearestDist = Infinity;
for (let j = 0; j < maxEntities; j++) {
if (j === i) continue;
const dx = px[j] - px[i], dy = py[j] - py[i];
const d = dx * dx + dy * dy;
if (d < nearestDist) { nearestDist = d; nearestX = px[j]; nearestY = py[j]; }
}
// write only own intent slot
intent[i] = computeIntent(px[i], py[i], nearestX, nearestY);
}
}
```
### Pattern 6: render thread on main, worker writes only
```typescript
// main thread: requestAnimationFrame → read SAB, render
function frame() {
for (let i = 0; i < activeCount; i++) {
ctx.fillRect(px[i], py[i], 2, 2);
}
requestAnimationFrame(frame);
}
// 매 race: workers writing while main reads — visual tearing 가능, 대부분 게임에서 허용.
// strict 의 매 double buffer (front/back) → flip on barrier.
```
### Pattern 7: double-buffered position
```typescript
const pxA = new Float32Array(sab, offA, max);
const pxB = new Float32Array(sab, offB, max);
let frontIdx = 0;
function workerStep() {
const front = frontIdx === 0 ? pxA : pxB;
const back = frontIdx === 0 ? pxB : pxA;
// read front, write back
for (let i = start; i < end; i++) back[i] = front[i] + vx[i] * dt;
// barrier → main flips frontIdx
}
```
### Pattern 8: SoA SIMD (WASM SIMD or manual unroll)
```typescript
// 매 4-wide unroll — JIT가 종종 SIMD화
for (let i = start; i < end; i += 4) {
px[i] += vx[i] * dt;
px[i + 1] += vx[i + 1] * dt;
px[i + 2] += vx[i + 2] * dt;
px[i + 3] += vx[i + 3] * dt;
}
```
### Pattern 9: spawn/kill queue (lock-free SPSC)
```typescript
// 매 main 만 spawn, worker 만 kill — single-producer queue
// 매 ring buffer of entity IDs, Atomics-driven head/tail
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| <1k entities | Single thread, 매 충분 |
| 1k10k, simple physics | bitECS single-thread |
| 10k+, heavy AI/physics | bitECS + SAB + worker pool |
| Render heavy | OffscreenCanvas worker |
| Voxel/chunk world | Per-chunk worker assignment |
**기본값**: 매 single thread first. 매 measure → SAB 매 4 worker 부터 의미 있을 때만.
## 🔗 Graph
- 부모: [[ECS]] · [[bitECS]] · [[Web Worker]] · [[SharedArrayBuffer]]
- 변형: [[Bevy ECS]] · [[Flecs]] · [[Unity DOTS]]
- 응용: [[Browser Game Engine]] · [[Particle System]] · [[Boid Simulation]]
- Adjacent: [[Web Worker와 SharedArrayBuffer를 이용한 실제 고부하 병렬 처리 구현체 (실패_성공 포함)]] · [[OffscreenCanvas]] · [[가변적 LOD(Level of Detail) 시스템]]
## 🤖 LLM 활용
**언제**: 매 large-scale entity simulation in browser. 매 60fps 매 100k+ entity.
**언제 X**: 매 small game, 매 single-thread bitECS 충분 — 매 SAB 의 debug 비용 매 큼.
## ❌ 안티패턴
- **Anti1: 매 component write 매 worker 동시**: race. 매 ownership 명확히.
- **Anti2: 매 frame SAB 재생성**: GC 폭발. startup 1번.
- **Anti3: 매 worker 마다 component query**: query overhead 누적. 매 main 에서 ID list 한번 + worker 에 stripe.
- **Anti4: false sharing — 매 worker 가 인접 entity write**: 매 stripe 대신 매 mod 분산은 false sharing 위험. stripe 사용.
- **Anti5: render race 무시**: visual artifact. 매 double buffer or 매 tolerate.
## 🧪 검증 / 중복
- Verified (bitECS GitHub, 20252026 Web Worker SAB ecosystem).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — bitECS+SAB architecture + canonical merge |