요청 하나가 아니라 전부가 느려졌다 · 쪼개기와 옮기기 · 실습
큰 계산을 쪼개고, 워커로 옮긴다
목표
루프를 오래 붙잡는 계산을 두 가지 방법으로 다룹니다 — 조각으로 나눠 사이사이
차례를 돌려주기, 그리고 워커 스레드로 통째로 옮기기. 그리고 각각의 값을
숫자로 잽니다.
왜 중요한가
"무거우면 워커로 보내라" 는 조언은 반쪽입니다. 워커는 메모리를 공유하지 않아서
지금 요청의 객체를 그대로 볼 수 없고, 스레드를 하나 띄우는 데도 시간이 듭니다.
그 시간이 일 자체보다 크면 옮기는 것이 손해입니다.
쪼개기는 반대입니다. 같은 스레드에 남아 있으니 상태를 그대로 보고, 대신 조각
사이에 양보하는 값을 총 시간으로 치릅니다. 그리고 **조각 하나의 시간이 곧 지연의
바닥**이 됩니다 — 조각을 도는 동안에는 아무도 끼어들 수 없기 때문입니다.
두 값을 재 두면 "이 일을 어디에 둘까" 가 취향이 아니라 계산이 됩니다. 마지막
단계에서 그 계산을 규칙으로 굳힙니다.
단계
1. /root/work/worker/split.mjs 에 chunks(total, size) 를 만듭니다.
2. 같은 파일에 hashRange(from, to, seed) 와 runSliced(total, sliceSize) 를 만듭니다.
3. 한 번에 도는 쪽과 쪼갠 쪽을 재어 /root/work/worker/report.json 의runs.whole·runs.sliced 에 적습니다.
4. 두 벌에서 slicing.costRatio·tailRatio·sliceMs 를 계산해 적습니다.
5. /root/work/worker/hash-worker.mjs 를 만들고 runInWorker(total) 로 옮깁니다.
6. 워커가 도는 동안을 재어 runs.worker 에 적고, worker.startupMs 도 적습니다.
7. place(job) 으로 "어디에 둘까" 를 규칙으로 만듭니다.
참고
chunks(total, size)는{from, to}객체의 배열입니다.size가 0 이하면hashRange(from, to, seed = 0)은h = (h * 31 + i) >>> 0을from부터runSliced(total, sliceSize)는{result, slices}를 돌려줍니다.runInWorker(total)는hashRange(0, total)과 같은 값을 프로미스로 돌려주고,place(job)의 규칙:cpuMs가 1 이하면"loop", 그보다 크고sharedState- 워커를 띄우는 프로그램은
node -e로 돌리지 마세요. 워커가 부모의 실행
예외를 던지세요 — 그대로 두면 영원히 도는 반복이 됩니다.
to 앞까지 돌린 값입니다. 순서에 기대는 계산이라 조각 사이에 앞 값을 seed 로
넘겨야 답이 같습니다.
spawnCostMs() 는 빈 일감 하나로 기동 시간만 잽니다.
가 참이면 "slice", 아니면 cpuMs 가 50 이상일 때 "worker" 이고 그 미만은"slice" 입니다. cpuMs 가 없거나 숫자가 아니면 예외를 던지세요.
인자를 물려받아 ERR_INPUT_TYPE_NOT_ALLOWED 로 죽습니다. 재는 프로그램도
파일로 두고 node measure.mjs 처럼 돌립니다.
단계 7개
- 경계에서 흘리지 않게 나눈다
- 쪼개고, 사이에 차례를 돌려준다
- 같은 답, 다른 분포
- 쪼개기의 값을 계산한다
- 다른 스레드로 옮긴다
- 워커의 값을 계산한다
- 규칙으로 굳힌다