@hatiolab/figure-model 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +3 -0
- package/dist/asset-types.d.ts +34 -3
- package/dist/asset-types.d.ts.map +1 -1
- package/dist/capabilities.js +28 -4
- package/dist/capabilities.js.map +1 -1
- package/dist/graph-types.d.ts +60 -1
- package/dist/graph-types.d.ts.map +1 -1
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +212 -21
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/kernel-version.d.ts +1 -1
- package/dist/kernel-version.js +1 -1
- package/dist/names.d.ts +19 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +47 -0
- package/dist/names.js.map +1 -0
- package/dist/package.d.ts +86 -0
- package/dist/package.d.ts.map +1 -0
- package/dist/package.js +202 -0
- package/dist/package.js.map +1 -0
- package/dist/render-lod.d.ts +12 -0
- package/dist/render-lod.d.ts.map +1 -0
- package/dist/render-lod.js +32 -0
- package/dist/render-lod.js.map +1 -0
- package/dist/shape-ops.d.ts +1 -1
- package/dist/shape-ops.d.ts.map +1 -1
- package/dist/shape-ops.js.map +1 -1
- package/dist/words.d.ts +6 -6
- package/dist/words.js +8 -8
- package/dist/words.js.map +1 -1
- package/docs/assembly-constraints.md +3 -3
- package/docs/asset-persistence.md +8 -8
- package/docs/authoring-transactions.md +2 -2
- package/docs/capability-bindings.md +1 -1
- package/docs/common-kernel-experiment.md +2 -2
- package/docs/completeness-review-2026-10-04.md +150 -1
- package/docs/core-semantics.md +2 -2
- package/docs/cutover.md +4 -4
- package/docs/document-contract.md +3 -3
- package/docs/dryroom-profile-validation.md +1 -1
- package/docs/editor-integration.md +4 -4
- package/docs/evaluation-changes-design.md +304 -0
- package/docs/evaluation-performance-summary-2026-10-05.md +236 -0
- package/docs/full-conveyor-validation.md +8 -8
- package/docs/full-dryroom-validation.md +5 -5
- package/docs/full-forklift-validation.md +5 -5
- package/docs/full-robot-validation.md +4 -4
- package/docs/layout-system.md +2 -2
- package/docs/legacy-risk-audit-2026-09-23.md +14 -14
- package/docs/minimal-model.md +3 -3
- package/docs/openusd-review.md +14 -14
- package/docs/operator-contracts.md +4 -4
- package/docs/performance-followup-2026-10-05.md +314 -0
- package/docs/prototypes/README.md +2 -2
- package/docs/prototypes/asset.schema.json +1 -1
- package/docs/prototypes/document.schema.json +1 -1
- package/docs/prototypes/editor-browser.mjs +2 -2
- package/docs/prototypes/linear-comparison.py +1 -1
- package/docs/prototypes/render-dryroom.py +1 -1
- package/docs/prototypes/render-full-dryroom.mjs +12 -12
- package/docs/prototypes/runtime-browser.mjs +3 -3
- package/docs/research-review.md +1 -1
- package/docs/review-2026-09-22.md +19 -19
- package/docs/runtime-extraction.md +10 -10
- package/docs/sample-validation-expanded.md +5 -5
- package/docs/sample-validation.md +5 -5
- package/docs/shape-dimension-contract.md +1 -1
- package/docs/spatial-contracts.md +2 -2
- package/docs/status.md +1 -1
- package/docs/storage-integration.md +5 -5
- package/package.json +1 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# 일반 평가의 변경분 전달 설계 (검토용)
|
|
2
|
+
|
|
3
|
+
작성일: 2026-10-05 · 상태: 방향 승인(2026-10-05), 아래 보완 조건으로 구현 · 관련: ADR-0108(pose step), completeness review 2026-10-04 F1,
|
|
4
|
+
things-scene `src/figure/figure-rig.ts`
|
|
5
|
+
|
|
6
|
+
## 장면
|
|
7
|
+
|
|
8
|
+
모델러 사용자가 보드에서 컨베이어 60대를 함께 끌어 늘인다. 매 프레임 크기와 롤러 회전이 함께 바뀌므로 모든 인스턴스가
|
|
9
|
+
pose step이 아닌 일반 평가를 탄다. 브라우저 프레임 7~8 ms 가운데 커널 평가가 약 3 ms다. 문+램프 60대에서 flag가 문턱을
|
|
10
|
+
넘나들 때도 매 프레임 일반 평가다.
|
|
11
|
+
|
|
12
|
+
## 지금 일반 평가가 하는 일
|
|
13
|
+
|
|
14
|
+
rig는 프레임마다 `document.evaluator().advance(inputs)`를 부른다. pose step을 탈 수 없으면 `step`이 평가하고, 결과를
|
|
15
|
+
스냅샷으로 만든다. 스냅샷에는 값 레코드(롤러 컨베이어 421개 키), geometry 목록(40개 body), places가 들어간다. 바뀐 값과
|
|
16
|
+
body는 동결 사본으로, 형상은 제자리 동결로 공개된다(F1 계약).
|
|
17
|
+
|
|
18
|
+
크기 변경 프레임의 단계별 시간(Node, 롤러 컨베이어 60대, 프레임당 ms, `step` 안에 타이머를 넣어 잰 값이라 타이머
|
|
19
|
+
비용이 조금 들어 있다. 전체는 3.74~3.80 ms):
|
|
20
|
+
|
|
21
|
+
| 단계 | ms | 비율 |
|
|
22
|
+
| ------------------------------------------------------ | ----- | ---- |
|
|
23
|
+
| 연산 실행과 값 검사 | 1.65 | 44% |
|
|
24
|
+
| body 공개(사본·동결, 롤러 행 사본 24개 포함) | 0.95 | 25% |
|
|
25
|
+
| 바뀐 값 공개(값 레코드에 넣을 동결 사본) | 0.43 | 11% |
|
|
26
|
+
| 형상 제자리 동결 | 0.28 | 7% |
|
|
27
|
+
| step 밖(헛 pose step 시도, capabilities, advance) | 0.33 | 9% |
|
|
28
|
+
| 입력 검사·방문 목록 | 0.09 | 2% |
|
|
29
|
+
| geometry 목록 재구성, 값 레코드·목록 복사 | 0.05 | 1% |
|
|
30
|
+
|
|
31
|
+
함수별 self time으로 보면 공개(사본·동결) 32%, 검사 17%, step 관리 16%, 연산 25%였다(같은 장면, 프로파일러 50 µs 간격).
|
|
32
|
+
|
|
33
|
+
flag 장면(문+램프 60대)은 프레임당 0.20 ms, 인스턴스당 3.3 µs다. 공개 26%, 관리 20%, 검사 19%, 헛 pose step 시도 11%다.
|
|
34
|
+
|
|
35
|
+
## rig가 실제로 읽는 것
|
|
36
|
+
|
|
37
|
+
| 결과의 부분 | rig가 읽는가 |
|
|
38
|
+
| --------------------------- | ---------------------------------------------------------------------------- |
|
|
39
|
+
| geometry의 body | 읽는다. id, key, shape, pose. 모양이 같으면 같은 객체라는 점을 계획 재사용에 쓴다 |
|
|
40
|
+
| 값 레코드 | 외형(look)이 이름 붙인 visibility flag 몇 개만 읽는다. 421개 중 나머지는 읽지 않는다 |
|
|
41
|
+
| places(bindings) | 읽는다. 부착점 노드의 자세 |
|
|
42
|
+
| body·값의 동결 사본 | 읽기만 한다. 사본이 필요한 이유는 다른 호출자와 소유권 계약 때문이다 |
|
|
43
|
+
|
|
44
|
+
rig 밖의 소비자(`rig.evaluation()`, 선택, 상위 뷰, 저작 도구)는 스냅샷 전체를 읽는다. 이 경로는 그대로 둔다.
|
|
45
|
+
|
|
46
|
+
## 제안: 변경분 경로
|
|
47
|
+
|
|
48
|
+
변경분은 명시적으로 고른 evaluator만 받는다. `evaluator({ changes: true })`로 만든 evaluator의 `advance`는 일반 평가
|
|
49
|
+
대신 `kind: 'changes'`를 돌려준다. 옵션 없이 만든 evaluator는 지금처럼 `evaluation | poses`만 돌려준다. `evaluation | poses`를
|
|
50
|
+
처리하던 소비자가 새 종류를 받는 일은 없다. 다른 패키지(things-scene rig)가 쓰므로 타입과 수명 계약을 공개 타입으로
|
|
51
|
+
둔다. 결과는 pose step 결과(ADR-0108)를 넓힌 모양이다.
|
|
52
|
+
|
|
53
|
+
- `kind: 'changes'`, `generation`
|
|
54
|
+
- `bodies: Int32Array`, `poses: Float64Array(12×n)`: 이번 평가에서 다시 계산된 body의 슬롯과 자세 숫자. pose step과 같다.
|
|
55
|
+
- `shapes`: `bodies`의 각 슬롯의 형상. 형상은 이미 제자리에서 동결되어 공유되는 객체다. 다시 계산된 body는 형상이
|
|
56
|
+
같은 객체여도 함께 보낸다. 바뀌었는지는 소비자가 객체 동일성으로 판단한다.
|
|
57
|
+
- `structure`: generation이 바뀌었을 때만(첫 평가, 반복 개수 변경, 실패 뒤 재시작) 온다. 새 body 목록 전체의 `ids`와
|
|
58
|
+
`keys`다. 이때 `bodies`는 모든 슬롯을 담고, `shapes`와 `poses`도 모든 슬롯의 것이다. 소비자는 이것만으로 새 슬롯의
|
|
59
|
+
형상·자세·id·key를 모두 초기화할 수 있다. 커널은 이전 슬롯과 견주어 같은 형상을 빼고 보내지 않는다.
|
|
60
|
+
- `read(ref)`, `changed(ref)`: pose step과 같은 수명(다음 호출 뒤 `STEP_EXPIRED`). rig는 visibility flag를 이것으로 읽는다.
|
|
61
|
+
`read`는 숫자와 flag를 그대로 돌려주고, 객체는 매번 새로 만든 동결 사본으로 돌려준다. 수명 제한만으로는 내부 가변
|
|
62
|
+
객체의 노출을 막을 수 없기 때문이다.
|
|
63
|
+
- `places`: 읽는 값이 바뀐 부착점만 온다. `structure`가 올 때는 모든 부착점이 온다. place 안의 자세와 값도 `read`와 같은
|
|
64
|
+
동결 사본에서 만든다.
|
|
65
|
+
|
|
66
|
+
커널이 하는 일은 다음과 같다.
|
|
67
|
+
|
|
68
|
+
- 연산 실행과 검사는 지금과 같다. 같은 연산을 같은 순서로 실행하므로 숫자는 스냅샷과 비트 단위로 같다.
|
|
69
|
+
- 공개(사본·동결)를 미룬다. 마지막 스냅샷 이후 바뀐 슬롯과 body를 표시만 해 두고, 다음 `evaluate`나 스냅샷을 요구하는
|
|
70
|
+
`advance`가 올 때 그만큼만 공개한다. 바뀌지 않은 형상과 body가 스냅샷 사이에서 같은 객체로 이어지는 계약, 이전 결과가
|
|
71
|
+
바뀌지 않는 계약, 쓰기가 거절되는 계약은 그대로다.
|
|
72
|
+
- 형상의 제자리 동결은 지금처럼 만들 때 한다. 변경분 경로가 형상 객체를 넘기기 때문이다.
|
|
73
|
+
|
|
74
|
+
소유권은 다음과 같다. 변경분 경로가 넘기는 것은 typed array 사본(호출자 소유), 동결된 형상, 문자열, 수명이 정해진
|
|
75
|
+
`read`뿐이다. 커널 내부의 가변 객체는 넘기지 않으므로 F1 결함은 다시 열리지 않는다.
|
|
76
|
+
|
|
77
|
+
## 누적 변경과 스냅샷
|
|
78
|
+
|
|
79
|
+
- 변경분 호출을 여러 번 한 뒤의 `evaluate`는 같은 입력의 전체 평가와 같고, 마지막 스냅샷 이후 다시 계산된 값과 body만
|
|
80
|
+
새 객체다. 그 사이 여러 번 다시 계산된 body도 한 번만 공개된다.
|
|
81
|
+
- pose step과 변경분이 섞여도 같다. pose step은 evaluator의 슬롯을 바꾸지 않으므로, 그 뒤의 변경분과 스냅샷은 pose step이
|
|
82
|
+
움직인 입력을 바뀐 것으로 보고 다시 계산한다(지금 pose step 규칙과 같다).
|
|
83
|
+
- 값이 바뀌었다가 원래대로 돌아와도 다시 계산된 body는 새 객체다. 지금 스냅샷 경로와 같은 규칙이다.
|
|
84
|
+
- 실패 뒤 첫 호출은 처음 평가이고 `structure`를 담는다. 미뤄 둔 공개는 실패와 함께 버려진다.
|
|
85
|
+
|
|
86
|
+
## 포함해야 하는 경우
|
|
87
|
+
|
|
88
|
+
- 형상: 형상 객체가 바뀐 body만 `shapes`에 온다. rig는 그 슬롯의 geometry를 다시 얻고, 배치 계획을 형상 동일성으로 다시 판단한다.
|
|
89
|
+
- 반복 개수: 구조가 바뀌면 generation이 바뀌고 `ids`와 `keys`가 온다. rig는 지금 generation 변경에서 하듯 slots를 다시 만든다.
|
|
90
|
+
- 재질: 재질은 asset의 look에서 오며 평가 결과에 없다. 지금처럼 rig가 look으로 정한다. body의 look이 바뀌는 경우는
|
|
91
|
+
`keys`와 id로 정해지므로 구조 변경에 포함된다.
|
|
92
|
+
- visibility: flag의 값은 커널이 계산한 값을 `read`로 읽는다. rig는 문턱을 다시 계산하지 않는다.
|
|
93
|
+
- 부착점: 읽는 값이 바뀐 place만 capabilities가 계산해 넘긴다. 전체 bindings 재계산을 하지 않는다. 부착점의 목록은
|
|
94
|
+
asset의 선언(`capabilities.bindings`)으로 정해지며 평가 중에 생기거나 없어지지 않는다. place가 가리키는 대상(행의 사본
|
|
95
|
+
등)이 없어지면 그 평가는 실패한다. 실패한 동안 소비자는 마지막에 그린 place를 유지하고, 다음 성공한 호출은 `structure`와
|
|
96
|
+
함께 모든 place를 보낸다. 따라서 삭제와 재생성은 실패 뒤 재시작의 전체 place 목록으로 전달된다.
|
|
97
|
+
- 실패: 입력이 거절되거나 연산이 실패하면 지금과 같이 예외를 던지고 evaluator가 보관하던 것을 버린다. 미뤄 둔 공개도
|
|
98
|
+
함께 버린다. rig는 이미 그린 것을 유지하고, 다음 호출은 처음 평가로 시작한다.
|
|
99
|
+
|
|
100
|
+
렌더러가 Figure 의미를 재구현하지 않도록, 아래는 모두 커널과 capabilities가 정한다. body의 정체(id·key), 형상, 자세,
|
|
101
|
+
flag의 참·거짓, place의 자세, 구조 변경 여부다. rig는 받은 숫자와 객체를 그리기에 반영할 뿐이다.
|
|
102
|
+
|
|
103
|
+
## 예상 절감 범위
|
|
104
|
+
|
|
105
|
+
없어지는 것(Node, 크기 변경, 프레임당):
|
|
106
|
+
|
|
107
|
+
- body 공개 0.95 ms. 대신 다시 계산된 body의 자세 숫자를 typed array에 쓴다. 60대×40개 body×12개 숫자로, 0.1 ms를 넘지 않을 것으로
|
|
108
|
+
추정한다(따로 재지 않았다).
|
|
109
|
+
- 바뀐 값 공개 0.43 ms. rig가 읽는 flag만 `read`한다.
|
|
110
|
+
- 값 레코드·목록 복사와 전체 bindings 재계산 일부(합계 0.05 ms 안팎).
|
|
111
|
+
|
|
112
|
+
남는 것: 연산과 검사 1.65 ms, 형상 동결 0.28 ms, 입력 검사 0.09 ms, step 관리 일부.
|
|
113
|
+
|
|
114
|
+
추정: 커널 3.7 ms 중 1.2~1.4 ms(32~38%). 브라우저에서 커널이 3.0~3.3 ms이므로 같은 비율이면 프레임당 약 1.0~1.2 ms다
|
|
115
|
+
(프레임 7~8 ms의 13~16%). rig 쪽 `dress`(0.19~0.25 ms)도 다시 계산된 body만 다루게 되어 일부 줄 수 있다. 이 값은 Node
|
|
116
|
+
비율을 브라우저에 옮긴 추정이다. 확인은 브라우저 한 페이지 교차 A/B로 하며, 이번에 관찰한 A/A 변동 폭(블록 차이
|
|
117
|
+
중앙값 약 0.1~0.2 ms)보다 충분히 크다.
|
|
118
|
+
|
|
119
|
+
flag 장면은 인스턴스당 3.3 µs라 절감 여지가 프레임당 약 0.05 ms다. 이 장면은 이 경로로 줄일 대상이 아니다.
|
|
120
|
+
|
|
121
|
+
## 별도의 작은 후보: 헛 pose step 시도
|
|
122
|
+
|
|
123
|
+
`advance`는 pose step을 먼저 시도한다. 이때 바뀐 입력이 빠른 경로를 탈 수 있는지 보기 전에 `bindFast`(슬롯 배열 사본과
|
|
124
|
+
typed array 할당)를 한다. 직전 호출이 일반 평가였다면 `held.fast`가 비어 있어 매번 새로 만든다. 입력 검사도 pose step과
|
|
125
|
+
step에서 두 번 한다. 빠른 경로 판정을 먼저 하면 이 비용이 없어진다. 공개 계약에 영향이 없다. 크기 변경에서 프레임당
|
|
126
|
+
약 0.07 ms, flag에서 약 0.02 ms(그 장면 커널의 11%)로, 효과가 작아 측정은 블록 수를 늘린 A/B로 보조한다.
|
|
127
|
+
|
|
128
|
+
## 소비자 쪽 조건
|
|
129
|
+
|
|
130
|
+
- rig는 `evaluator({ changes: true })`를 명시적으로 고른다.
|
|
131
|
+
- `lastEvaluation`을 밖에서 직접 읽지 않더라도, `rig.evaluation()`과 그것을 쓰는 조회(`placeNode` 등)는 최신 값을 돌려준다.
|
|
132
|
+
`rig.evaluation()`은 evaluator의 `evaluate`(미룬 공개를 하는 스냅샷)로 만든다.
|
|
133
|
+
|
|
134
|
+
## 검증 계획
|
|
135
|
+
|
|
136
|
+
- 변경분을 적용한 결과가 같은 입력의 스냅샷(`evaluate`)과 같다: 모든 fixture, 무작위 입력 순서, 구조 변경 포함.
|
|
137
|
+
- 변경분 경로 뒤의 스냅샷이 F1 소유권 시험을 모두 통과한다. 미룬 공개가 이전 결과를 바꾸지 않는다.
|
|
138
|
+
- 실패 뒤의 다음 호출이 처음 평가와 같다.
|
|
139
|
+
- rig: 병합 없는 rig와 그리기가 같고, 선택(`bodyAt`), 부착점, sizing 시험이 그대로 통과한다.
|
|
140
|
+
- 성능: 크기 교대, 끌기(행 개수 변화), 회전, flag 장면의 frame 중앙값과 p95를 A/A와 함께 잰다.
|
|
141
|
+
|
|
142
|
+
## 결정 (2026-10-05)
|
|
143
|
+
|
|
144
|
+
1. 변경분은 `evaluator({ changes: true })`로 명시적으로 고른 소비자만 받는다. 기본 동작은 그대로다.
|
|
145
|
+
2. 미룬 공개는 `evaluate` 때 구성한다.
|
|
146
|
+
3. 예상 절감 1.0~1.2 ms는 목표값이다. 채택은 브라우저 전체 프레임 중앙값·p95와 메모리를 잰 뒤에 정한다. 발행하지 않는다.
|
|
147
|
+
|
|
148
|
+
## 구현과 측정 (2026-10-05)
|
|
149
|
+
|
|
150
|
+
구현(발행 안 함):
|
|
151
|
+
|
|
152
|
+
- figure-model `a968731`: pose step 시도가 빠른 경로 판정 전에 상태를 만들지 않는다(별도 커밋).
|
|
153
|
+
- `ac92b39`, `1d059f3`, 그 뒤 커밋: `evaluator({ changes: true })`로 고른 evaluator만 변경분을 받는다. `structure`에는
|
|
154
|
+
ids·keys·paths·indexes가 있다. 처음 평가의 공개도 스냅샷을 요구할 때까지 미룬다.
|
|
155
|
+
- 시험 `src/evaluation-changes.test.ts` 8개. 결함 9종(구조에서 슬롯 생략, `read`가 내부 객체 반환, 미룬 공개 소실, pose step이
|
|
156
|
+
쓴 body 누락, 기본 evaluator가 변경분을 받음, `evaluate`가 공개하지 않음, 첫 평가의 미룬 표시 누락 등)을 심어 실패를
|
|
157
|
+
확인했다. figure-model 시험 739개와 build가 통과한다.
|
|
158
|
+
- things-scene rig는 `changes: true`를 고르고, 알려 준 슬롯만 다시 입힌다. flag는 커널의 `read`로, place는 id로 갱신한다.
|
|
159
|
+
rig 시험(변경분 뒤 `lastEvaluation`과 `rig.evaluation()`이 스냅샷과 같음, 새로 만든 rig와 그리기가 같음, 변경분 옵션 선택)을
|
|
160
|
+
더했고, flag·place·옵션 결함을 심어 실패를 확인했다. figure 시험 79개가 형제 소스로 통과한다. 이 rig는 발행되지 않은
|
|
161
|
+
figure-model API가 있어야 빌드되므로 커밋하지 않고 로컬 브랜치 `candidate/figure-rig-changes`에 두었다.
|
|
162
|
+
|
|
163
|
+
측정(브라우저, 인스턴스 60개, frame 중앙값(p95) ms):
|
|
164
|
+
|
|
165
|
+
| 방식 | 크기 교대 | 끌기 | 회전·flag |
|
|
166
|
+
| -------------------------------------- | -------------------------------------- | -------------------------------------- | --------- |
|
|
167
|
+
| 한 페이지 A/A, 블록 차이 중앙값 | +0.02, +0.05 | +0.06, −0.01 | ±0.11 |
|
|
168
|
+
| 한 페이지 B/B(후보끼리), 블록 차이 | −0.03, +0.00 | +0.08, +0.11 | ±0.18 |
|
|
169
|
+
| 한 페이지 A/B, 블록 차이 | −1.14, −1.08 | −1.09, −0.91 | ±0.13 |
|
|
170
|
+
| 한 페이지 절대값, A/A 양쪽 → B/B 양쪽 | 6.88~7.38 → 6.69~6.81 | 7.01~7.93 → 7.00~7.40 | |
|
|
171
|
+
| 따로 띄운 실행, frame | 7.40 (9.88), 7.50 (14.86) → 5.77 (7.56), 6.33 (7.62) | 7.59 (9.45), 7.55 (9.72) → 6.74 (8.03), 5.73 (7.38) | |
|
|
172
|
+
| 따로 띄운 실행, rig `update` | 5.67, 5.56 → 4.10, 4.67 | 5.62, 5.67 → 4.58, 4.30 | |
|
|
173
|
+
|
|
174
|
+
- 한 페이지 A/B의 HEAD 쪽은 같은 회차의 A/A보다 느렸다(7.21·8.18 대 6.88~7.38). 두 쪽이 한 페이지에서 서로의 할당·GC에
|
|
175
|
+
영향을 주는 것으로 보인다. 그래서 짝지은 차이(약 −1 ms)는 개선을 부풀렸을 수 있다. 같은 조건의 절대값(A/A 대 B/B)으로는
|
|
176
|
+
크기 교대가 0.1~0.6 ms 줄었고, 끌기는 섞여 있다.
|
|
177
|
+
- 따로 띄운 실행에서는 frame 중앙값과 p95, rig `update`가 4회 모두 줄었다. 다만 이 방식의 A/A 변동은 이전에 최대 ±1.9 ms였다.
|
|
178
|
+
- 메모리(distinct 500대): heap 67.7 → 69.3 MB(+1.6 MB). 첫 평가 공개를 미루기 전에는 72.7 MB였다. 살아 있는 동안 39.3 →
|
|
179
|
+
41.1 MB, 60대 6.3 → 6.5 MB. 모두 dispose한 뒤 남는 양과 은행은 같다.
|
|
180
|
+
- 판정: rig `update`가 줄어든 것은 여러 방식에서 일관된다. 전체 프레임의 개선 크기는 측정 방식에 따라 0.1~1.8 ms로 갈리므로,
|
|
181
|
+
채택은 두 쪽이 서로 영향을 주지 않는 측정으로 확인한 뒤 정한다.
|
|
182
|
+
|
|
183
|
+
### 실행 단위 측정 (2026-10-05)
|
|
184
|
+
|
|
185
|
+
`things-scene/test/browser/check-figure-runs.mjs`로 쟀다. 같은 브라우저에서 한 번에 한 판의 페이지만 띄우고, 실행마다
|
|
186
|
+
rig·렌더러·context를 해제하고 페이지를 닫았다. 한 round에서 A1·A2(같은 판, A/A)·B를 한 번씩 돌리고, 6 round에서 순서
|
|
187
|
+
6가지를 한 번씩 썼다. 반복 수(6 round, 인스턴스 60, 워밍업 60, 실행당 300프레임)는 측정 전에 정했다. 값은 실행 중앙값의
|
|
188
|
+
중앙값(괄호 안은 p95의 중앙값) ms이고, 차이는 round별 짝 차이의 중앙값과 범위다.
|
|
189
|
+
|
|
190
|
+
| 구성 | A1 / A2 / B | A2−A1 중앙값 (범위) | B−A1 중앙값 (범위) | A2−A1 p95 | B−A1 p95 |
|
|
191
|
+
| ---------------------------- | ---------------------------------- | -------------------------- | --------------------------- | --------------- | ------------------------- |
|
|
192
|
+
| 크기 교대, 조회 없음 | 7.00 (8.74) / 6.96 (9.23) / 6.59 (8.89) | −0.03 (−0.23~−0.01) | −0.47 (−0.95~+0.08) | +0.22 (−0.36~+2.80) | +0.11 (−0.69~+1.80) |
|
|
193
|
+
| 크기 교대, 매 프레임 조회 | 7.05 (8.63) / 7.07 (8.46) / 7.12 (8.25) | +0.03 (−0.10~+0.12) | +0.09 (−0.10~+0.20) | −0.08 (−0.35~+0.23) | −0.34 (−0.53~−0.25) |
|
|
194
|
+
| 크기 교대, 30프레임마다 조회 | 7.82 (9.33) / 7.82 (9.90) / 7.47 (9.69) | −0.06 (−0.29~+0.26) | −0.30 (−0.42~−0.22) | +0.32 (−1.05~+0.71) | +0.42 (−3.18~+2.73) |
|
|
195
|
+
| 끌기(행 개수 변화), 조회 없음 | 7.22 (9.41) / 7.37 (9.53) / 6.67 (8.54) | +0.06 (−0.06~+0.20) | −0.63 (−0.98~+0.02) | +0.19 (−0.03~+0.64) | −0.38 (−1.38~+0.24) |
|
|
196
|
+
| 끌기, 매 프레임 조회 | 7.18 (8.36) / 7.14 (8.87) / 7.28 (8.47) | +0.00 (−0.09~+0.12) | +0.10 (−0.28~+0.22) | +0.05 (−4.03~+0.93) | +0.01 (−3.36~+0.50) |
|
|
197
|
+
| 회전(pose step만) | 6.07 (8.20) / 6.05 (8.13) / 5.86 (7.80) | +0.02 (−0.30~+0.59) | +0.06 (−1.24~+0.20) | −0.33 (−1.24~+1.05) | −0.35 (−1.56~+0.38) |
|
|
198
|
+
| 문+램프 flag | 3.25 (5.04) / 3.22 (4.80) / 3.19 (4.59) | +0.01 (−0.21~+0.11) | −0.12 (−0.37~−0.02) | −0.18 (−0.77~+0.07) | −0.41 (−1.25~+0.12) |
|
|
199
|
+
|
|
200
|
+
메모리(인스턴스 60, 페이지의 rig 생성 전 heap보다 늘어난 양, MB, 실행이 남긴 그대로 / 한 번 조회 뒤 / 30프레임 반복 조회 뒤):
|
|
201
|
+
|
|
202
|
+
| 구성 | A1 | B |
|
|
203
|
+
| ------------------ | -------------- | -------------- |
|
|
204
|
+
| 크기 교대, 조회 없음 | 8.1 / 8.1 / 8.1 | 8.4 / 8.9 / 9.1 |
|
|
205
|
+
| 끌기, 조회 없음 | 8.5 / 8.4 / 8.5 | 8.7 / 9.1 / 9.4 |
|
|
206
|
+
| 크기 교대, 매 프레임 조회 | 8.1 / 8.1 / 8.1 | 9.1 / 9.1 / 9.1 |
|
|
207
|
+
| 회전 | 8.2 / 8.1 / 8.2 | 8.4 / 8.4 / 8.5 |
|
|
208
|
+
| 문+램프 | 3.5 / 3.5 / 3.5 | 3.4 / 3.4 / 3.6 |
|
|
209
|
+
|
|
210
|
+
해석:
|
|
211
|
+
|
|
212
|
+
- 스냅샷을 읽지 않거나 가끔 읽는 소비자에서는 전체 프레임 중앙값이 줄었다. 크기 교대 −0.47 ms(6회 중 5회 감소), 30프레임마다
|
|
213
|
+
조회 −0.30 ms(6회 모두 감소), 끌기 −0.63 ms(6회 중 5회 감소)다. 짝 차이의 중앙값에서 개선을 관찰했지만, 개별 회차는
|
|
214
|
+
A/A 범위와 겹치거나 악화됐다(크기 교대 +0.08, 끌기 +0.02 ms인 회차가 있다).
|
|
215
|
+
- p95는 끌기에서만 대체로 줄었다(−0.38 ms, 6회 중 4회). 크기 교대의 p95 차이는 A/A의 p95 변동 범위 안이다.
|
|
216
|
+
- 매 프레임 `rig.evaluation()`을 읽으면 절감이 사라진다(크기 교대 +0.09, 끌기 +0.10 ms). 미룬 공개가 조회 때 돌아오기 때문이다.
|
|
217
|
+
- pose step만 도는 장면과 flag 장면은 A/A 범위 안이다.
|
|
218
|
+
- 메모리는 60대에서 0.2~1.0 MB 늘었다. 스냅샷을 한 번 읽은 뒤가 더 크다. 커널이 공개 사본을 갖고 있고, rig는 그와 별도로 자기
|
|
219
|
+
body를 갖기 때문이다. 이전에는 rig가 커널의 공개 사본을 그대로 썼다.
|
|
220
|
+
- 결론: 변경분 경로는 스냅샷을 매 프레임 읽지 않는 사용 조건에서 크기 변경 프레임의 중앙값을 0.3~0.6 ms 줄인다. p95의
|
|
221
|
+
일관된 개선은 끌기에서만 보였고, 매 프레임 조회에서는 이득이 없으며, 메모리가 조금 는다. 완료 기준(전체 프레임 중앙값과
|
|
222
|
+
p95의 일관된 개선)은 크기 교대 p95에서 충족되지 않았다. 후보는 브랜치에 두고 채택을 보류한다.
|
|
223
|
+
|
|
224
|
+
## 스냅샷 조회의 실제 사용 (2026-10-05 조사)
|
|
225
|
+
|
|
226
|
+
things-scene·things-factory·operato-application 세 저장소의 제품 소스(시험·번들 제외)에서 인스턴스 evaluator와 rig 스냅샷을
|
|
227
|
+
읽는 곳을 모두 찾았다.
|
|
228
|
+
|
|
229
|
+
| 읽는 곳 | 언제 | 읽는 항목 |
|
|
230
|
+
| ----------------------------------------- | -------------------------------------------------------------------- | ----------------------------- |
|
|
231
|
+
| `FigureRig.placeNode(id)` → `evaluation()` | 부착점 노드를 처음 만들 때, rig·부착점마다 한 번. 이후는 만든 노드를 쓴다. 호출은 운반물이 자리에 들어갈 때(`attachPointFor` → `placeAttachment`) | `bindings` 중 그 부착점 하나 |
|
|
232
|
+
| `figureTopView` → 새 rig의 `evaluate()` | 2D 평면도를 그릴 때, 크기 key가 바뀔 때만(`figure-instance.ts`의 `_top` 캐시) | geometry(형상·자세·표시), palette |
|
|
233
|
+
| rig 내부 `draw` | 매 갱신 | 변경분에서 만든 rig 자신의 body |
|
|
234
|
+
|
|
235
|
+
- 매 프레임 `rig.evaluation()`을 읽는 제품 소비자는 없다. `lastEvaluation`을 밖에서 읽는 곳은 시험뿐이다.
|
|
236
|
+
- figure-ui 모델러와 operato-application은 인스턴스 evaluator나 rig를 쓰지 않는다(전체 평가를 쓴다).
|
|
237
|
+
- 따라서 측정의 「매 프레임 조회」는 지금 제품에 없는 사용 조건이다. 실제로 문제가 되는 것은 드문 조회 한 번이 스냅샷 전체를
|
|
238
|
+
공개하고, 그 사본을 계속 보유하는 메모리다(60대에서 조회 전 +0.2~0.3 MB, 한 번 조회 뒤 +0.5~1.0 MB).
|
|
239
|
+
|
|
240
|
+
## 다음 순서 (설계, 구현 전)
|
|
241
|
+
|
|
242
|
+
1. 조회 범위를 줄인다.
|
|
243
|
+
- 커널(capabilities)이 변경분 evaluator에 부착점 하나를 묻는 조회를 둔다. 지금 상태에서 그 부착점이 읽는 값만 동결 사본으로
|
|
244
|
+
읽어 만든다(변경분의 `places`와 같은 규칙). 스냅샷을 만들지 않고, 다음 호출의 수명 제한도 필요 없다(반환 값이 호출자 소유다).
|
|
245
|
+
- `placeNode`는 이 조회를 쓴다. 부착점 의미는 커널에 남는다.
|
|
246
|
+
- 평면도는 한 번 쓰고 버리는 rig로 인스턴스 evaluator를 만들 이유가 없다. 전체 평가(가변, 동결 없음)를 rig와 같은 외형 규칙으로
|
|
247
|
+
입히는 경로를 둔다. 외형을 입히는 규칙은 rig의 것 하나를 함께 쓴다.
|
|
248
|
+
2. 커널의 공유 구조를 고친다.
|
|
249
|
+
- 스냅샷은 마지막 변경 뒤 첫 조회에서만 구성되고, 바뀌지 않은 재조회는 같은 body·값 객체를 다시 쓴다. 이것은 지금 구현이 이미
|
|
250
|
+
그렇다(미룬 공개가 없으면 공개하지 않는다). 값 레코드와 목록은 계약대로 호출마다 새로 만든다(60대 0.03 ms).
|
|
251
|
+
- 변경분이 다시 계산한 노드와 값의 공개 사본은 그 자리에서 놓는다. 다음 조회에서 어차피 새로 공개되므로 동일성 계약(바뀌지 않은
|
|
252
|
+
것만 같은 객체)은 그대로이고, 한 번 조회한 뒤 쌓이던 낡은 사본이 남지 않는다.
|
|
253
|
+
- 변경분 소비자와 스냅샷이 같은 동결 body를 나눠 쓰려면 변경될 때마다 body를 공개(사본·동결)해야 한다. 이것은 변경분 경로가 없애려던
|
|
254
|
+
비용이다. 그래서 body는 나누지 않고, 지금처럼 동결된 형상만 나눈다.
|
|
255
|
+
3. 가변 렌더 버퍼와 공개 불변 결과를 나눈다.
|
|
256
|
+
- 변경분의 typed array는 호출자 소유의 가변 버퍼다. 형상은 커널의 동결 객체를 그대로 공유한다. 스냅샷은 동결된 공개 결과다.
|
|
257
|
+
- rig의 `lastEvaluation`은 rig가 변경분으로 만든 자기 그리기 상태다. 커널 스냅샷의 객체를 담지 않는다. 밖에서 최신 값을 원하면
|
|
258
|
+
`rig.evaluation()`(커널 스냅샷)을 읽는다.
|
|
259
|
+
|
|
260
|
+
예상: 지금 제품의 사용 조건(조회 드묾)에서 프레임 시간은 바뀌지 않는다. 1번으로 부착점 조회가 스냅샷 전체 공개를 일으키지 않게 되고,
|
|
261
|
+
2번으로 조회 뒤의 메모리 증가가 줄어든다. 매 프레임 전체 스냅샷을 요구하는 소비자가 생기면 공개 비용은 피할 수 없다. 크기 교대 p95의
|
|
262
|
+
완료 기준 미충족은 이 순서로 바뀌지 않는다.
|
|
263
|
+
|
|
264
|
+
## 조회 범위 축소와 낡은 사본 해제 (2026-10-05)
|
|
265
|
+
|
|
266
|
+
구현:
|
|
267
|
+
|
|
268
|
+
- figure-model: 인스턴스 evaluator의 `value(ref)`와 capabilities의 `place(id)`. 지금 상태(마지막 평가와 그 뒤의 pose step)를
|
|
269
|
+
동결 사본으로 읽어 만들고 스냅샷을 만들지 않는다. 반환값은 호출자 소유다. pose step이 자세만 쓴 행(아무 노드도 읽지 않는 행)은
|
|
270
|
+
물을 때 그 자리에서 실행한다. 두 종류의 evaluator 모두에 있다.
|
|
271
|
+
- figure-model: 변경분 단계는 다시 계산한 노드와 값의 공개 사본을 놓는다. 호출자가 가진 스냅샷은 건드리지 않고, 다시 계산되지
|
|
272
|
+
않은 것은 다음 스냅샷에서 같은 객체다.
|
|
273
|
+
- things-scene 후보 브랜치(`candidate/figure-rig-changes` `8b7c0ce2`): `placeNode`가 `place(id)`를 쓴다. 평면도는
|
|
274
|
+
`evaluateOnce()`(전체 평가를 rig의 외형 규칙 하나로 입힘, 인스턴스 evaluator 없음)를 쓴다. 외형 규칙은 `dressOne` 하나로
|
|
275
|
+
스냅샷·변경분·전체 평가가 함께 쓴다. `lastEvaluation`은 `lastDrawn`(`FigureRigDrawing`, rig 자신의 그리기 상태)으로 이름과
|
|
276
|
+
타입을 바꿨다. 커널 스냅샷은 `evaluation()`이다.
|
|
277
|
+
|
|
278
|
+
시험: figure-model `src/evaluation-reads.test.ts` 3개(부착점 단건 조회가 두 종류 evaluator에서 pose step 뒤에도 전체 평가와 같음,
|
|
279
|
+
값 조회의 소유권, 보관한 스냅샷은 그대로이고 놓은 스냅샷의 다시 계산된 body는 회수되며 바뀌지 않은 body는 같은 객체). 결함
|
|
280
|
+
5종(해제 안 함, 바뀌지 않은 것까지 해제, pose step 무시, 내부 객체 반환, 행 실행 누락)을 심어 실패를 확인했다. things-scene
|
|
281
|
+
후보에 시험 2개(부착점 단건 조회는 스냅샷을 만들지 않고, 재사용된 노드가 pose step·평가 뒤에도 커널의 자리이며 그린 상태의
|
|
282
|
+
region까지 같음 · 평면도의 전체 평가가 스냅샷 경로와 geometry·palette·places·크기 결과가 같음). 결함 3종을 심어 실패를 확인했다.
|
|
283
|
+
|
|
284
|
+
메모리(Node, 컨베이어 60대에 부착점 하나, GC 뒤 rig 생성 전보다 늘어난 MB, 예열 한 번 뒤 두 번씩, 두 번의 차이 0.1 MB 이내).
|
|
285
|
+
각 단계의 결과는 전체 평가와 같았다.
|
|
286
|
+
|
|
287
|
+
| 단계 | HEAD rig, 놓음 | HEAD rig, 보관 | 후보·해제 없음, 놓음 | 후보·해제 없음, 보관 | 후보·해제, 놓음 | 후보·해제, 보관 |
|
|
288
|
+
| ---------------------- | -------------- | -------------- | -------------------- | -------------------- | --------------- | --------------- |
|
|
289
|
+
| 조회 전 | 6.35~6.39 | 6.38~6.43 | 6.53~6.56 | 6.54~6.58 | 6.53~6.57 | 6.53~6.58 |
|
|
290
|
+
| 부착점 단건 조회 뒤 | 6.35~6.39 | 6.34~6.39 | 6.42~6.49 | 6.43~6.51 | 6.42~6.49 | 6.43~6.51 |
|
|
291
|
+
| 전체 스냅샷 조회 뒤 | 6.35~6.38 | 6.40~6.47 | 7.39~7.44 | 7.47~7.52 | 7.39~7.45 | 7.47~7.52 |
|
|
292
|
+
| 30프레임 변경 뒤 | 6.34~6.42 | 7.88~7.95 | 8.05~8.15 | 8.17~8.24 | 7.77~7.87 | 7.90~7.97 |
|
|
293
|
+
| 재조회 뒤 | 6.41~6.45 | 7.91~7.96 | 7.56~7.62 | 8.94~9.02 | 7.56~7.61 | 8.94~8.99 |
|
|
294
|
+
| dispose 뒤 | 0.01~0.08 | 0.03~0.09 | 0.01~0.08 | 0.02~0.10 | 0.01~0.08 | 0.02~0.10 |
|
|
295
|
+
|
|
296
|
+
- 부착점 단건 조회는 메모리를 늘리지 않는다. 지금까지는 이 조회가 스냅샷 전체를 공개했다.
|
|
297
|
+
- 낡은 사본 해제로 스냅샷 조회 뒤 변경 단계의 보유량이 0.3 MB 줄었다(8.05~8.24 → 7.77~7.97).
|
|
298
|
+
- 그래도 스냅샷을 한 번 읽은 뒤에는 후보가 HEAD rig보다 놓은 경우 1.1~1.2 MB, 보관한 경우 1.0 MB 더 쓴다. HEAD에서는 rig가 커널의
|
|
299
|
+
공개 사본을 그대로 쓰므로 공개 사본이 하나뿐이다. 후보에서는 커널의 공개 사본(바뀌지 않은 것의 동일성 계약 때문에 유지)과 rig
|
|
300
|
+
자신의 body가 따로 있고, `evaluation()`이 입힌 사본도 rig의 입힘 캐시에 남는다.
|
|
301
|
+
- 지금 제품에서 스냅샷을 읽는 곳은 부착점(이제 단건 조회)과 평면도(이제 전체 평가)뿐이다. 그래서 제품 사용 조건에서는 스냅샷 공개가
|
|
302
|
+
일어나지 않고, 보유량은 「조회 전」 줄(HEAD보다 0.15~0.2 MB)에 머문다.
|
|
303
|
+
- 이 작업은 조회 비용과 메모리의 개선이다. 크기 교대 p95가 완료 기준을 채우지 못한 문제는 그대로이고, 변경분 경로의 기본 채택은
|
|
304
|
+
보류한다.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# 평가 성능 작업 정리와 채택 판단 자료 (2026-10-05)
|
|
2
|
+
|
|
3
|
+
completeness review 2026-10-04(F1~F4)에서 2026-10-05까지의 성능·메모리·호환성 근거를 한 곳에 모은다. 판단할 것은 하나다.
|
|
4
|
+
일반 평가의 변경분 경로를 제품 기본값으로 채택할지. 이 문서는 그 판단에 필요한 근거만 담는다. 세부 기록은 각 줄의 문서에 있다.
|
|
5
|
+
|
|
6
|
+
## 1. 분류
|
|
7
|
+
|
|
8
|
+
### 유지하는 것
|
|
9
|
+
|
|
10
|
+
| 무엇 | 근거 | 위치 |
|
|
11
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
12
|
+
| 증분 평가와 pose step(ADR-0108) | 앞서 적용·측정 완료 | figure-model 발행판 |
|
|
13
|
+
| 정적 부품 병합·운반 부품 병합(ADR-0109·0111) | 앞서 적용·측정 완료 | things-scene 발행판 |
|
|
14
|
+
| F1~F4 정확성 수정 | 결과 소유권, 공개 입구 검증, 깊은 그래프, 검사 옵션. 시험으로 확인 | figure-model 0.3.3 |
|
|
15
|
+
| 부착점 단건 조회 `place(id)`, 값 조회 `value(ref)` | 스냅샷 없이 최신 상태를 동결 사본으로 읽는다. 제품의 부착점 조회가 스냅샷 전체 공개를 일으키지 않는다 | figure-model `fe8323b`, things-scene `candidate/figure-read-scope` |
|
|
16
|
+
| 평면도의 전체 평가 `evaluateOnce()` | 버리는 rig로 인스턴스 evaluator를 만들지 않는다. 기존 경로와 결과가 같다 | things-scene `candidate/figure-read-scope` `2d082c7c` |
|
|
17
|
+
| pose step 시도의 판정 순서 | 계약 영향 없음. Node에서 크기 변경 프레임의 헛 시도 비용 0.08 → 0.004 ms(60대). 브라우저 전체 프레임 효과는 A/A 폭 안 | figure-model `a968731` |
|
|
18
|
+
| 평가자 오버로드 순서 | `ReturnType<...['evaluator']>`로 타입을 얻던 소비자(things-scene rig)가 그대로 컴파일된다 | figure-model `dc32aa7` |
|
|
19
|
+
| 시험 | 결과 소유권, 변경분 동등성, 조회 소유권, 낡은 사본 회수, 묶음 키, 은행 회수 | figure-model `src/*.test.ts`, things-scene `test/*.check.js` |
|
|
20
|
+
| 측정 도구 | 단계 분해(`BREAKDOWN`), 프로필(`PROFILE`), 한 페이지 교차 A/B, 실행 단위 A/A·A/B(`check-figure-runs.mjs`) | things-scene `test/browser/` |
|
|
21
|
+
|
|
22
|
+
### 후보로 보존하는 것 (기본 채택 보류)
|
|
23
|
+
|
|
24
|
+
| 무엇 | 위치 |
|
|
25
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
26
|
+
| 일반 평가의 변경분 경로 `evaluator({ changes: true })` | figure-model `ac92b39`, `1d059f3`, `c34621c`, `fe8323b`(낡은 사본 해제 부분). 옵션을 고른 소비자만 받는다 |
|
|
27
|
+
| 변경분을 쓰는 rig | things-scene `candidate/figure-rig-changes` `8b7c0ce2`(조회 범위 축소 포함, `lastDrawn`) |
|
|
28
|
+
|
|
29
|
+
### 코드를 되돌리고 설계·측정 기록을 남긴 것
|
|
30
|
+
|
|
31
|
+
| 무엇 | 되돌린 이유 | 기록 |
|
|
32
|
+
| -------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
33
|
+
| 읽히지 않는 행의 제자리 동결 | 커널 5~10% 감소, 전체 프레임 효과 미확인 | completeness review 「제자리 동결 후보의 측정 결과」 |
|
|
34
|
+
| 형상별 키 캐시 | 배치 계획 약 0.2 ms 감소(잠정), 전체 프레임 효과 미확인 | completeness review 「rig 크기 변경 비용 분해」 |
|
|
35
|
+
| 병합 부품 geometry 장기 보유 | 끌기에서 감소가 보였으나 크기 교대 p95 악화 회차, 은행 +20% | completeness review 「병합 geometry 재생성 분해」 |
|
|
36
|
+
| 병합 부품 geometry 갱신 구간 보유 | 전체 프레임 차이가 A/A 폭 안 | things-scene `docs/plans/figure-merged-part-renewal.md` |
|
|
37
|
+
|
|
38
|
+
## 2. 변경분 경로의 근거
|
|
39
|
+
|
|
40
|
+
### 성능
|
|
41
|
+
|
|
42
|
+
실행 단위 측정(`check-figure-runs.mjs`): 한 번에 한 판의 페이지, 실행마다 해제, 6 round × A1·A2·B, 순서 균형, 반복 수 사전 고정,
|
|
43
|
+
인스턴스 60, 워밍업 60, 실행당 300프레임. 비교 기준은 F1 적용판이다.
|
|
44
|
+
|
|
45
|
+
| 사용 조건 | B−A1 중앙값 (범위) | A2−A1 중앙값 (범위) | B−A1 p95 | A2−A1 p95 |
|
|
46
|
+
| ----------------------------- | -------------------- | -------------------- | -------------------- | -------------------- |
|
|
47
|
+
| 크기 교대, 스냅샷 조회 없음 | −0.47 (−0.95~+0.08) | −0.03 (−0.23~−0.01) | +0.11 (−0.69~+1.80) | +0.22 (−0.36~+2.80) |
|
|
48
|
+
| 크기 교대, 30프레임마다 조회 | −0.30 (−0.42~−0.22) | −0.06 (−0.29~+0.26) | +0.42 (−3.18~+2.73) | +0.32 (−1.05~+0.71) |
|
|
49
|
+
| 크기 교대, 매 프레임 조회 | +0.09 (−0.10~+0.20) | +0.03 (−0.10~+0.12) | −0.34 (−0.53~−0.25) | −0.08 (−0.35~+0.23) |
|
|
50
|
+
| 끌기(행 개수 변화), 조회 없음 | −0.63 (−0.98~+0.02) | +0.06 (−0.06~+0.20) | −0.38 (−1.38~+0.24) | +0.19 (−0.03~+0.64) |
|
|
51
|
+
| 끌기, 매 프레임 조회 | +0.10 (−0.28~+0.22) | +0.00 (−0.09~+0.12) | +0.01 (−3.36~+0.50) | +0.05 (−4.03~+0.93) |
|
|
52
|
+
| 회전(pose step만) | +0.06 (−1.24~+0.20) | +0.02 (−0.30~+0.59) | −0.35 | −0.33 |
|
|
53
|
+
| 문+램프 flag | −0.12 (−0.37~−0.02) | +0.01 (−0.21~+0.11) | −0.41 | −0.18 |
|
|
54
|
+
|
|
55
|
+
- 스냅샷을 드물게 읽거나 읽지 않을 때 짝 차이의 중앙값에서 0.3~0.6 ms 감소를 관찰했다. 개별 회차는 A/A 범위와 겹치거나
|
|
56
|
+
악화된 경우가 있다. 기대치(1.0~1.2 ms)에는 못 미쳤다.
|
|
57
|
+
- p95는 끌기에서만 대체로 줄었다. 크기 교대의 p95 차이는 A/A의 p95 변동 범위 안이다. 완료 기준(중앙값과 p95의 일관된
|
|
58
|
+
개선)은 크기 교대에서 충족되지 않았다.
|
|
59
|
+
- 매 프레임 스냅샷을 읽으면 이득이 없다. 지금 제품에는 그런 소비자가 없다(아래 사용 조건).
|
|
60
|
+
- 한 페이지에 두 판을 함께 띄운 A/B는 두 쪽이 서로 영향을 주어 차이를 부풀렸다(약 −1 ms). 판단에는 쓰지 않는다.
|
|
61
|
+
|
|
62
|
+
### 메모리
|
|
63
|
+
|
|
64
|
+
Node, 컨베이어 60대와 부착점 하나, GC 뒤 rig 생성 전보다 늘어난 MB.
|
|
65
|
+
|
|
66
|
+
| 단계 | HEAD rig | 변경분 rig(해제 포함) |
|
|
67
|
+
| --------------------- | -------- | --------------------- |
|
|
68
|
+
| 스냅샷 조회 없음 | 6.35~6.43 | 6.53~6.58 |
|
|
69
|
+
| 부착점 단건 조회 뒤 | 6.34~6.39 | 6.42~6.51 |
|
|
70
|
+
| 전체 스냅샷 한 번 뒤 | 6.35~6.47 | 7.39~7.52 |
|
|
71
|
+
| 재조회 뒤(놓음 / 보관) | 6.41~6.45 / 7.91~7.96 | 7.56~7.61 / 8.94~8.99 |
|
|
72
|
+
| dispose 뒤 | 0.01~0.09 | 0.01~0.10 |
|
|
73
|
+
|
|
74
|
+
- 제품의 드문 조회 조건(스냅샷 공개 없음)에서도 60대 기준 0.15~0.2 MB 늘어난다.
|
|
75
|
+
- 전체 스냅샷을 한 번 읽으면 약 1 MB가 더 남는다. 커널의 공개 사본(바뀌지 않은 것의 동일성 계약 때문에 유지)과 rig 자신의
|
|
76
|
+
body가 따로 있기 때문이다. 낡은 공개 사본 해제로 변경 단계에서 약 0.3 MB 줄었지만, HEAD보다 줄어든 것은 아니다.
|
|
77
|
+
- 브라우저 distinct 500대: heap 67.7 → 69.3 MB(첫 평가 공개를 미루기 전에는 72.7 MB).
|
|
78
|
+
|
|
79
|
+
### 사용 조건
|
|
80
|
+
|
|
81
|
+
세 저장소의 제품 소스에서 인스턴스 스냅샷을 읽는 곳은 부착점 노드를 처음 만들 때(이제 단건 조회)와 평면도(이제 전체 평가)뿐이다.
|
|
82
|
+
매 프레임 스냅샷을 읽는 제품 소비자는 없다. figure-ui 모델러와 operato-application은 인스턴스 evaluator를 쓰지 않는다.
|
|
83
|
+
|
|
84
|
+
### 호환성
|
|
85
|
+
|
|
86
|
+
- 기본 evaluator의 동작은 그대로다. 변경분은 옵션을 고른 소비자만 받는다. 기본 evaluator가 변경분을 받지 않음을 시험으로 확인했다.
|
|
87
|
+
- 추가된 공개 이름: `FigureGraphChanges`, `FigureGraphChangesEvaluator`, `FigureCapabilityChanges`, `FigureCapabilityChangesEvaluator`,
|
|
88
|
+
evaluator의 `value(ref)`, capabilities evaluator의 `place(id)`.
|
|
89
|
+
- 변경분 rig는 `lastEvaluation`(커널 스냅샷을 입힌 것)을 `lastDrawn`(rig 자신의 그리기 상태)으로 바꾼다. 밖에서 이 필드를
|
|
90
|
+
읽는 곳은 시험뿐이다.
|
|
91
|
+
- 두 things-scene 후보 브랜치는 발행되지 않은 figure-model API가 있어야 빌드된다. 기본 브랜치에 넣으려면 figure-model 발행이 먼저다.
|
|
92
|
+
|
|
93
|
+
### 유지보수
|
|
94
|
+
|
|
95
|
+
계산 상태, 공개 스냅샷, 렌더 상태의 책임이 나뉘었다. 대신 경로와 수명 규칙이 늘었다. 미뤄 둔 공개, pose step이 쓴 body와 값의
|
|
96
|
+
추적, 조회의 수명과 사본 규칙, 구조 변경 시 전체 슬롯 전달이 그것이다. 각 규칙에 결함을 심어 실패하는 시험이 있다.
|
|
97
|
+
|
|
98
|
+
## 3. 판단에 남은 것
|
|
99
|
+
|
|
100
|
+
- 변경분 경로의 기본 채택 여부. 지금 근거로는 조건부 개선(중앙값)과 순증 메모리가 함께 있고, p95 기준은 충족되지 않았다.
|
|
101
|
+
- 최종 성능 판단의 기준은 F1 적용판 대비 개선이 아니다. 제품에서 필요한 규모와 프레임·메모리 목표를 충족하는지다. 이번 측정은
|
|
102
|
+
F1 적용판 대비라서, 이번 측정으로 JS 대비 우위를 확인하지 않았다. 앞선 JS 비교 결과(ADR-0108·0109의 보드 경로 비교)는 그대로
|
|
103
|
+
유효하다. 다음 비교는 같은 장면에서 세 경로(현재 JS 구현, 정확성 수정과 독립 개선을 적용한 Figure 기본 경로, 변경분 경로를
|
|
104
|
+
적용한 Figure 후보)를 잰다. 외형·움직임·크기 변경·부착물 기능을 맞추고, 목표 대수와 프레임·메모리 예산을 측정 전에 고정한다.
|
|
105
|
+
JS 컨베이어의 롤러가 돌지 않는 점처럼 기능이 다르면 맞추거나 따로 표시한다. things-scene `test/browser/bench-figure-vs-js.mjs`가
|
|
106
|
+
출발점이다.
|
|
107
|
+
- 발행하지 않았다.
|
|
108
|
+
|
|
109
|
+
## 4. 세 경로 비교의 목표 (측정 전 고정, 2026-10-05)
|
|
110
|
+
|
|
111
|
+
| 항목 | 고정한 값 |
|
|
112
|
+
| ---------- | -------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| 대수 | 컨베이어 500대, 로봇 300대(ADR-0108·0109 측정과 같은 규모) |
|
|
114
|
+
| 프레임 | 전체 프레임 중앙값 16.7 ms 이하, p95 33 ms 이하. ADR-0108의 커널+rig 8.3 ms는 그 안의 내역 목표로 둔다 |
|
|
115
|
+
| 메모리 | 같은 장면의 JS heap(GC 뒤) 대비 1.5배 이하 |
|
|
116
|
+
| 움직임 | 컨베이어 롤러 회전은 JS에 없다. Figure끼리만 비교하고 JS 칸은 n/a로 둔다. 로봇은 관절 움직임으로 세 경로를 비교한다 |
|
|
117
|
+
| 경로 | 현재 JS 구현 · Figure 기본 경로(정확성 수정과 독립 개선) · Figure 후보(변경분 경로) |
|
|
118
|
+
| 재는 것 | 전체 프레임 중앙값·p95, 생성 시간, 메모리. 외형·움직임·크기 변경·부착물 기능을 맞춘다 |
|
|
119
|
+
|
|
120
|
+
결과를 본 뒤 이 값을 바꾸지 않는다. 변경분 경로의 기본 채택은 이 목표에 대한 결과로 정한다.
|
|
121
|
+
|
|
122
|
+
## 5. 세 경로 비교 결과 (2026-10-05)
|
|
123
|
+
|
|
124
|
+
`things-scene/test/browser/check-figure-vs-js-runs.mjs`, 실기 GPU Apple M5. 보드 경로(`Scene.create`, model-layer, threed)에서 한 번에 한
|
|
125
|
+
판의 페이지만 띄웠다. 4 round(js·D1·D2·C의 라틴 방진 순서), 워밍업 30, 실행당 150프레임으로 측정 전에 고정했다. D는 Figure 기본 경로
|
|
126
|
+
(`candidate/figure-read-scope` 빌드), C는 변경분 후보(`candidate/figure-rig-changes` 빌드)이고, 둘 다 figure-model HEAD를 묶었다. 값은
|
|
127
|
+
실행 중앙값의 중앙값(괄호 안은 p95의 중앙값) ms, heap은 빈 페이지보다 늘어난 MB다. 목표를 넘은 값에 ✗를 붙였다.
|
|
128
|
+
|
|
129
|
+
| 장면 | JS | Figure 기본 D1 / D2 | Figure 후보 C | C−D1 round별 중앙값 | D2−D1 round별 중앙값 |
|
|
130
|
+
| ---------------------------- | -------------------------- | ------------------------------------ | ---------------------- | ---------------------------- | -------------------------- |
|
|
131
|
+
| 컨베이어 500 정지 | 11.73 (12.77) | 11.77 (12.80) / 11.72 (12.57) | 11.42 (12.91) | +0.05 −0.98 −0.34 +0.43 | +0.16 −1.16 +0.15 +0.53 |
|
|
132
|
+
| 컨베이어 500 크기 교대 | 217.22 ✗ (236.88 ✗) | 61.45 ✗ (69.08 ✗) / 61.31 ✗ (67.38 ✗) | 49.80 ✗ (57.11 ✗) | −11.06 −11.45 −14.03 −11.28 | −0.45 +0.32 +2.04 +0.01 |
|
|
133
|
+
| 컨베이어 500 끌기(행 개수 변화) | 215.69 ✗ (235.63 ✗) | 63.73 ✗ (70.80 ✗) / 63.72 ✗ (71.09 ✗) | 51.87 ✗ (58.11 ✗) | −11.64 −11.60 −11.66 −12.19 | +0.48 −1.07 +1.06 −0.36 |
|
|
134
|
+
| 컨베이어 500 롤러 회전 | n/a(JS 롤러는 돌지 않음) | 18.68 ✗ (21.46) / 18.80 ✗ (21.01) | 18.58 ✗ (21.70) | −0.27 −0.11 +0.19 −3.90 | +0.05 −0.11 +0.19 −2.06 |
|
|
135
|
+
| 로봇 300 관절 | 22.67 ✗ (24.11) | 12.78 (16.62) / 12.52 (14.82) | 12.64 (14.47) | −0.01 −0.00 −0.26 −0.04 | −0.09 −0.58 −0.48 −0.18 |
|
|
136
|
+
| 로봇 300 집게에 상자를 든 채 관절 | 27.76 ✗ (31.87) | 20.87 ✗ (23.82) / 21.05 ✗ (23.56) | 20.49 ✗ (23.70) | −0.67 −0.09 −1.00 +0.19 | +0.24 +0.45 −1.00 +0.12 |
|
|
137
|
+
|
|
138
|
+
p95 차이(C−D1, round별): 크기 교대 −11.88 −12.06 −14.38 −9.68, 끌기 −12.99 −10.63 −12.89 −14.73. 다른 장면은 A/A 범위 안이다.
|
|
139
|
+
|
|
140
|
+
| 장면 | 생성 ms JS / D / C | heap MB JS / D / C | 메모리 목표(JS의 1.5배 이하) |
|
|
141
|
+
| -------------- | ------------------------- | ------------------------ | ---------------------------- |
|
|
142
|
+
| 컨베이어 500 | 534~560 / 637~797 / 702~752 | 82.4 / 38.6 / 40.1 | D·C 충족(JS의 약 0.5배) |
|
|
143
|
+
| 로봇 300 관절 | 227 / 318~321 / 345 | 48.7 / 25.5 / 23.8 | D·C 충족 |
|
|
144
|
+
| 로봇 300 들기 | 256 / 315~316 / 311 | 58.0 / 35.0 / 33.3 | D·C 충족 |
|
|
145
|
+
|
|
146
|
+
로봇 300대 모두 집게에 상자를 들었다(4 round 모두 300/300).
|
|
147
|
+
|
|
148
|
+
### 읽은 것
|
|
149
|
+
|
|
150
|
+
- 변경분 후보(C)는 보드의 크기 교대와 끌기에서 중앙값과 p95를 4 round 모두 약 11~14 ms 줄였다(D의 약 19%). 같은 조건 A/A(D2−D1)는
|
|
151
|
+
±2 ms 안이다. 정지, 롤러 회전, 로봇 관절, 들기에서는 A/A 범위 안이다. 메모리는 컨베이어에서 1.5 MB 늘고 로봇에서 1.7 MB 줄었다.
|
|
152
|
+
- rig 단독 측정(60대, 크기 교대)의 차이는 약 0.5 ms였다. 보드 경로 500대에서 대당 차이가 그보다 크게 나타난 이유는 확인하지 않았다.
|
|
153
|
+
보드는 크기 변경 한 번에 `rig.update()`를 한 번 부른다. 채택 전에 이 차이를 분해해 확인해야 한다.
|
|
154
|
+
- 제품 목표: 크기 교대·끌기는 세 경로 모두 프레임 목표를 크게 넘는다(JS 216~217 ms, D 61~64 ms, C 50~52 ms). 롤러 회전(D·C 18.6~18.8 ms)과
|
|
155
|
+
로봇 들기(D·C 20.5~21.1 ms, JS 27.8 ms)도 중앙값 16.7 ms를 넘는다. p95 33 ms는 정지·회전·로봇 장면에서 지켜진다. 정지 컨베이어와
|
|
156
|
+
로봇 관절은 Figure 두 경로가 목표 안이고 JS보다 같거나 빠르다. 메모리는 모든 장면에서 Figure가 JS의 절반 수준이다.
|
|
157
|
+
- 생성 시간은 Figure가 JS보다 길다(컨베이어 약 1.2~1.4배, 로봇 약 1.2~1.5배). 생성 시간 목표는 정하지 않았다.
|
|
158
|
+
|
|
159
|
+
### 판단 자료
|
|
160
|
+
|
|
161
|
+
- 변경분 경로의 기본 채택 기준(전체 프레임 중앙값과 p95의 일관된 개선, 메모리 예산 안)은 보드 경로 목표 규모의 크기 변경 장면에서
|
|
162
|
+
충족되고, 다른 장면에서 악화가 없다.
|
|
163
|
+
- 남는 것: rig 단독과 보드 경로의 차이 크기가 설명되지 않았다. 그리고 어느 경로도 500대 크기 변경, 롤러 회전, 로봇 들기에서 프레임
|
|
164
|
+
목표를 충족하지 못한다. 이것은 기본 채택 여부와 별개의 과제다.
|
|
165
|
+
|
|
166
|
+
## 6. 보드와 rig 단독의 차이 분해 (2026-10-05)
|
|
167
|
+
|
|
168
|
+
`things-scene/test/browser/check-figure-gap.mjs`. 같은 입력(폭 2400과 2496을 프레임마다 교대, 그 밖에는 아무것도 바꾸지 않음)을 rig
|
|
169
|
+
단독(같은 빌드의 rig 클래스로 만든 rig, 보드가 그 폭에 주는 인스턴스 크기)과 보드에 주었다. 60대와 500대, D1·D2·C를 4 round 돌렸다.
|
|
170
|
+
실행마다 프레임을 잰 뒤, 60프레임씩 단계 계측·할당 샘플링·CPU 프로필을 따로 돌렸다. 값은 중앙값, ms/프레임(전 인스턴스 합).
|
|
171
|
+
|
|
172
|
+
| 경로·대수 | D1 frame (p95) | C frame (p95) | C−D1 round별 중앙값 | D2−D1 round별 중앙값 |
|
|
173
|
+
| ----------- | -------------- | -------------- | ---------------------------- | -------------------------- |
|
|
174
|
+
| rig 60 | 7.24 (9.11) | 6.33 (7.84) | −1.25 −1.69 −0.41 −0.66 | +0.26 +0.54 +0.14 +0.13 |
|
|
175
|
+
| rig 500 | 50.02 (55.66) | 39.54 (45.23) | −11.06 −10.11 −10.37 −10.37 | +2.12 +1.37 −0.31 −0.20 |
|
|
176
|
+
| 보드 60 | 14.44 (16.01) | 13.95 (15.00) | +0.02 −0.17 −0.63 −2.43 | +0.25 +0.34 +0.13 −1.08 |
|
|
177
|
+
| 보드 500 | 60.78 (67.42) | 49.97 (56.30) | −11.04 −10.77 −10.58 −11.05 | −0.04 −0.99 +0.38 +0.00 |
|
|
178
|
+
|
|
179
|
+
500대의 단계별 시간(D1 → C):
|
|
180
|
+
|
|
181
|
+
| 단계 | rig 500 | 보드 500 |
|
|
182
|
+
| ----------------------------- | --------------- | --------------- |
|
|
183
|
+
| rig update 전체 | 45.02 → 34.33 | 45.36 → 34.55 |
|
|
184
|
+
| 커널(advance) | 27.31 → 19.23 | 27.26 → 19.34 |
|
|
185
|
+
| 외형 입히기(dress → fromChanges) | 1.87 → 1.83 | 1.82 → 1.75 |
|
|
186
|
+
| 그리기 준비(draw) | 15.62 → 13.00 | 15.91 → 13.27 |
|
|
187
|
+
| 그 안의 배치 계획 / 병합 | 6.63 / 6.25 → 5.58 / 5.29 | 6.50 / 6.45 → 5.54 / 5.55 |
|
|
188
|
+
| 할당(MB/프레임) | 80.0 → 75.1 | 82.0 → 76.7 |
|
|
189
|
+
| GC(ms/프레임, 비율) | 5.98 (11.3%) → 3.85 (9.2%) | 6.27 (9.5%) → 4.44 (8.2%) |
|
|
190
|
+
|
|
191
|
+
60대에서는 커널 3.20 → 2.36 ms(rig), 3.32 → 2.67 ms(보드)였다.
|
|
192
|
+
|
|
193
|
+
같은 작업량: 네 경우 모두 D와 C의 draw call, 삼각형, geometry 수, 그린 body 수, 행 사본 수가 같다(500대: 2,000~2,001회, 944,000~944,012개,
|
|
194
|
+
body 20,000, 사본 12,000). 프레임마다 rig 갱신 1회, 평가 1회(D는 스냅샷 `evaluation`, C는 `changes`)이고, 스냅샷 조회(`evaluate`·
|
|
195
|
+
`evaluation()`·`evaluateOnce`)는 0회다.
|
|
196
|
+
|
|
197
|
+
설명:
|
|
198
|
+
|
|
199
|
+
- 보드와 rig 단독의 차이가 아니다. 500대의 개선은 rig 단독 −10.1~−11.1 ms, 보드 −10.6~−11.1 ms로 같다. 보드는 두 판 모두에 약 10 ms의
|
|
200
|
+
고정된 몫(장면 갱신·제출)을 더할 뿐이다.
|
|
201
|
+
- 규모와 입력의 차이다. 앞선 rig 측정(약 0.5 ms)은 60대에서 크기 교대에 롤러 회전을 함께 바꾸었고, 소스를 변환한 번들이었다. 같은 입력의
|
|
202
|
+
60대에서는 rig −0.4~−1.7 ms, 보드 +0.0~−2.4 ms다.
|
|
203
|
+
- 500대의 개선 약 10.5 ms는 커널 약 8 ms(대당 약 16 µs)와 그리기 준비 약 2.6 ms로 나뉜다. 그리기 준비의 코드는 D와 C가 같다. C는
|
|
204
|
+
프레임당 할당이 약 5 MB 적고 GC가 약 2 ms 적다. GC 정지가 그리기 준비 구간 안에서 일어나므로, 그 몫이 그리기 준비의 감소로 보인다고
|
|
205
|
+
해석한다(같은 코드의 시간이 준 것을 다른 원인으로 설명할 근거는 찾지 못했다). 60대의 0.5 ms를 비례로 늘린 4.2 ms보다 큰 것은 대당 커널
|
|
206
|
+
절감이 같은 입력에서 더 크고(약 16 µs), 500대에서 GC 부담의 차이가 더해지기 때문이다.
|
|
207
|
+
- 대당 프레임당 할당이 약 160 KB(500대에서 80 MB/프레임)로 크다. 기본 채택과 별개로 다음 성능 과제의 단서다.
|
|
208
|
+
|
|
209
|
+
## 7. 결정 (2026-10-05)
|
|
210
|
+
|
|
211
|
+
- 변경분 경로를 things-scene의 기본 경로로 채택한다(사용자 승인). 근거는 같은 입력·같은 작업량 검증과 500대 반복 측정이다. 보드 500대 크기
|
|
212
|
+
교대에서 프레임 중앙값 60.78 → 49.97 ms(약 18%), p95 67.42 → 56.30 ms(약 16%) 줄었다.
|
|
213
|
+
- 측정된 것은 커널 비용의 감소(500대 27.3 → 19.3 ms)다. GC가 그리기 준비 구간의 추가 감소에 기여했다는 설명은 할당과 GC 시간이 함께 준
|
|
214
|
+
관측에서 끌어낸 추론이며, 직접 확인하지 않았다.
|
|
215
|
+
- 채택과 제품 성능 목표 달성은 별개다. 500대 크기 변경은 여전히 프레임 목표 16.7 ms를 넘는다.
|
|
216
|
+
- 후속 과제(이번 발행 범위 밖): 500대 크기 변경에서 프레임당 약 80 MB(대당 약 160 KB)의 할당.
|
|
217
|
+
- 발행 순서: figure-model 발행 → things-scene 후보(`candidate/figure-rig-changes`, 조회 범위 축소 포함) 병합과 figure-model 범위 갱신,
|
|
218
|
+
build와 전체 시험 → things-scene 발행·제품 설치 → 크기 변경, 롤러 회전, visibility, 부착물, 평면도 확인.
|
|
219
|
+
|
|
220
|
+
## 8. 발행 (2026-10-05)
|
|
221
|
+
|
|
222
|
+
- figure-model 0.3.4(커밋 04b6b12까지, 깨끗한 사본에서 발행), things-scene 10.1.73(병합 7ba1b7ee, 범위 9a4a2404). operato-application에
|
|
223
|
+
두 판을 설치했다(빌드 15개 통과).
|
|
224
|
+
- 설치본 화면 확인(operato-figure :3300, 로드된 vendor 번들에서 things-scene 10.1.73의 변경분 rig 코드 확인). 모델러의 미리보기에서,
|
|
225
|
+
저장된 모델을 바꾸지 않고(저장 상태 그대로) 설치본의 rig를 직접 조작하고 결과를 커널 스냅샷과 견주었다.
|
|
226
|
+
- 컨베이어 크기·롤러 개수: 길이 2,400·3,000·3,600·4,800 mm에서 롤러 24·30·36·48개. 크기 변경은 `changes`로 그려졌고, rig가 그린
|
|
227
|
+
상태(`lastDrawn`)가 매번 커널 스냅샷과 같았다.
|
|
228
|
+
- 롤러 회전: 미리보기의 회전 조작값을 움직이면 `poses`(pose step)로 그려졌다.
|
|
229
|
+
- 램프 flag(DRY_ROOM): 문을 열면(1, 0.5) 빛나는 부품이 그려지고 닫으면(0) 빠졌다. `changes`로 그려졌고 스냅샷의 visible과 같았다.
|
|
230
|
+
- 로봇 집게 부착(6축 로봇암): 부착점 노드를 만들 때 스냅샷이 만들어지지 않았다(0회). 관절을 움직인 pose step 네 번과 크기를 바꾼
|
|
231
|
+
`changes` 세 번 뒤에도 노드가 커널의 자리와 차이 0으로 같았고, 같은 노드가 다시 쓰였다.
|
|
232
|
+
- 평면도: `FigureInstance.topView`가 전체 평가(`evaluateOnce`)로 만든 결과가 이전 경로(인스턴스 evaluator 스냅샷)와 같았다. 로봇
|
|
233
|
+
세 크기(형상 12개, 선언 범위를 넘는 크기는 1,080으로 맞춰짐), 컨베이어 세 크기(형상 40·52·116개, 20,000 mm는 10,000으로 맞춰짐).
|
|
234
|
+
- 확인용으로 페이지 안에서만 등록한 이름(`verify-robot`, `verify-conveyor`)은 확인 뒤 해제했고, 보드나 첨부는 만들지 않았다. 띄운 서버는
|
|
235
|
+
확인 뒤 내렸다.
|
|
236
|
+
- 이번 작업은 완료다. 후속 과제로 남긴다: 500대 크기 변경의 프레임 목표 16.7 ms 미달, 프레임당 약 80 MB 할당.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Figure 전체 컨베이어 검증 — 2026-09-21
|
|
2
2
|
|
|
3
3
|
> 현재 구현·검증·제품 연결 상태는 [구현·검증 현황](status.md)을 따른다. 본문의 단계별 연구/검증 기록은 제품 전체 완료 선언이 아니다.
|
|
4
4
|
|
|
5
5
|
## 판정과 범위
|
|
6
6
|
|
|
7
|
-
저장소 CONVEYOR seed의 **17개 파트 유형 전체**를 실험
|
|
7
|
+
저장소 CONVEYOR seed의 **17개 파트 유형 전체**를 실험 Figure 그래프로 작성했다. 기본 형상과 재질을 재현하면서 기존 반복 배치의 겹침을 의도적으로 제거했다. 제품 모델러 통합·애니메이션 재생·AI 저작 검증은 아직 아니다.
|
|
8
8
|
|
|
9
9
|
- 선언 및 메시 어댑터: [full-conveyor.mjs](prototypes/full-conveyor.mjs)
|
|
10
10
|
- 회귀 검사: [full-conveyor.test.mjs](prototypes/full-conveyor.test.mjs)
|
|
11
11
|
- 브라우저: `node docs/prototypes/render-full-dryroom.mjs --conveyor`
|
|
12
|
-
- 전체 테스트: `node --test docs/prototypes
|
|
12
|
+
- 전체 테스트: `node --test docs/prototypes/*.test.mjs`
|
|
13
13
|
|
|
14
14
|
## 끝 롤러 공간을 먼저 확보하는 이유
|
|
15
15
|
|
|
@@ -51,16 +51,16 @@ Chrome WebGL에서 직접 이미지를 확인했다. 환경 반사광을 포함
|
|
|
51
51
|
| 상태 | draw calls | 삼각형 |
|
|
52
52
|
|---|---:|---:|
|
|
53
53
|
| 원본 재구현 | 32 | 1,612 |
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
54
|
+
| Figure 기본 | 30 | 1,516 |
|
|
55
|
+
| Figure 회전 37° | 30 | 1,516 |
|
|
56
|
+
| Figure 길이3000·폭900·높이700·회전37° | 34 | 1,708 |
|
|
57
57
|
|
|
58
|
-
원본과
|
|
58
|
+
원본과 Figure 기본 상태의 해당 시점 RGBA 채널 차이는 0이었다. **메시가 같다는 뜻은 아니다.** 원본의 불필요한 두 롤러가 완전히 가려져 영상에는 나타나지 않기 때문이다. 영상만으로 내부 중첩을 판정할 수 없다는 사례다. 브라우저 pageerror는 없었다. 임시 이미지: `/private/tmp/figure-full-conveyor.png`.
|
|
59
59
|
|
|
60
60
|
## 남은 한계
|
|
61
61
|
|
|
62
62
|
1. 원본 비교는 제품 생성 코드를 읽고 만든 어댑터 기준이다. 제품 렌더러 직접 실행이 아니며 rounded profile 함수도 공유한다.
|
|
63
|
-
2. 재질·분할 수는 seed 메타데이터를 붙인다.
|
|
63
|
+
2. 재질·분할 수는 seed 메타데이터를 붙인다. Figure 전체 직렬화/재질/품질 정책 계약의 완성을 뜻하지 않는다.
|
|
64
64
|
3. 접촉 관계는 치수 의존 그래프로 표현했다. 사용자가 접합면을 선택하는 UI 및 feature 기반 저작 작업으로의 연결은 별도 검증이 필요하다.
|
|
65
65
|
4. 검사한 접합과 끝 롤러 간격 외의 모든 부품 간 충돌을 증명한 것은 아니다. 받침 하단은 원본처럼 y=6mm이며 바닥 접촉으로 몰래 변경하지 않았다.
|
|
66
66
|
5. 반복 ID는 순번 기반이다. 크기 변경 시 ID는 유지되지만 가운데 정렬 때문에 위치는 움직인다. 삭제 후 재생성·외부 참조의 제품 정책은 여전히 별도 계약이 필요하다.
|